summaryrefslogtreecommitdiff
path: root/chromium/third_party/ink
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2018-01-31 16:33:43 +0100
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2018-02-06 16:33:22 +0000
commitda51f56cc21233c2d30f0fe0d171727c3102b2e0 (patch)
tree4e579ab70ce4b19bee7984237f3ce05a96d59d83 /chromium/third_party/ink
parentc8c2d1901aec01e934adf561a9fdf0cc776cdef8 (diff)
downloadqtwebengine-chromium-da51f56cc21233c2d30f0fe0d171727c3102b2e0.tar.gz
BASELINE: Update Chromium to 65.0.3525.40
Also imports missing submodules Change-Id: I36901b7c6a325cda3d2c10cedb2186c25af3b79b Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'chromium/third_party/ink')
-rw-r--r--chromium/third_party/ink/closure/array/array.js1667
-rw-r--r--chromium/third_party/ink/closure/asserts/asserts.js391
-rw-r--r--chromium/third_party/ink/closure/base.js2962
-rw-r--r--chromium/third_party/ink/closure/crypt/base64.js370
-rw-r--r--chromium/third_party/ink/closure/crypt/crypt.js196
-rw-r--r--chromium/third_party/ink/closure/debug/debug.js666
-rw-r--r--chromium/third_party/ink/closure/debug/entrypointregistry.js159
-rw-r--r--chromium/third_party/ink/closure/debug/error.js66
-rw-r--r--chromium/third_party/ink/closure/debug/errorcontext.js49
-rw-r--r--chromium/third_party/ink/closure/disposable/disposable.js305
-rw-r--r--chromium/third_party/ink/closure/disposable/idisposable.js45
-rw-r--r--chromium/third_party/ink/closure/dom/asserts.js299
-rw-r--r--chromium/third_party/ink/closure/dom/browserfeature.js74
-rw-r--r--chromium/third_party/ink/closure/dom/classlist.js276
-rw-r--r--chromium/third_party/ink/closure/dom/dom.js3234
-rw-r--r--chromium/third_party/ink/closure/dom/htmlelement.js29
-rw-r--r--chromium/third_party/ink/closure/dom/nodetype.js48
-rw-r--r--chromium/third_party/ink/closure/dom/safe.js458
-rw-r--r--chromium/third_party/ink/closure/dom/tagname.js562
-rw-r--r--chromium/third_party/ink/closure/dom/tags.js41
-rw-r--r--chromium/third_party/ink/closure/dom/vendor.js97
-rw-r--r--chromium/third_party/ink/closure/events/browserevent.js470
-rw-r--r--chromium/third_party/ink/closure/events/browserfeature.js140
-rw-r--r--chromium/third_party/ink/closure/events/event.js144
-rw-r--r--chromium/third_party/ink/closure/events/eventhandler.js479
-rw-r--r--chromium/third_party/ink/closure/events/eventid.js46
-rw-r--r--chromium/third_party/ink/closure/events/events.js1006
-rw-r--r--chromium/third_party/ink/closure/events/eventtarget.js395
-rw-r--r--chromium/third_party/ink/closure/events/eventtype.js360
-rw-r--r--chromium/third_party/ink/closure/events/keycodes.js439
-rw-r--r--chromium/third_party/ink/closure/events/listenable.js338
-rw-r--r--chromium/third_party/ink/closure/events/listener.js129
-rw-r--r--chromium/third_party/ink/closure/events/listenermap.js307
-rw-r--r--chromium/third_party/ink/closure/events/wheelevent.js170
-rw-r--r--chromium/third_party/ink/closure/format/format.js503
-rw-r--r--chromium/third_party/ink/closure/fs/url.js106
-rw-r--r--chromium/third_party/ink/closure/functions/functions.js485
-rw-r--r--chromium/third_party/ink/closure/html/legacyconversions.js204
-rw-r--r--chromium/third_party/ink/closure/html/safehtml.js994
-rw-r--r--chromium/third_party/ink/closure/html/safescript.js234
-rw-r--r--chromium/third_party/ink/closure/html/safestyle.js560
-rw-r--r--chromium/third_party/ink/closure/html/safestylesheet.js344
-rw-r--r--chromium/third_party/ink/closure/html/safeurl.js454
-rw-r--r--chromium/third_party/ink/closure/html/trustedresourceurl.js412
-rw-r--r--chromium/third_party/ink/closure/html/uncheckedconversions.js254
-rw-r--r--chromium/third_party/ink/closure/i18n/bidi.js878
-rw-r--r--chromium/third_party/ink/closure/i18n/bidiformatter.js556
-rw-r--r--chromium/third_party/ink/closure/i18n/graphemebreak.js451
-rw-r--r--chromium/third_party/ink/closure/i18n/uchar.js294
-rw-r--r--chromium/third_party/ink/closure/iter/iter.js1284
-rw-r--r--chromium/third_party/ink/closure/labs/useragent/browser.js339
-rw-r--r--chromium/third_party/ink/closure/labs/useragent/engine.js157
-rw-r--r--chromium/third_party/ink/closure/labs/useragent/platform.js161
-rw-r--r--chromium/third_party/ink/closure/labs/useragent/util.js157
-rw-r--r--chromium/third_party/ink/closure/math/box.js403
-rw-r--r--chromium/third_party/ink/closure/math/coordinate.js280
-rw-r--r--chromium/third_party/ink/closure/math/irect.js45
-rw-r--r--chromium/third_party/ink/closure/math/long.js966
-rw-r--r--chromium/third_party/ink/closure/math/math.js449
-rw-r--r--chromium/third_party/ink/closure/math/rect.js478
-rw-r--r--chromium/third_party/ink/closure/math/size.js228
-rw-r--r--chromium/third_party/ink/closure/object/object.js751
-rw-r--r--chromium/third_party/ink/closure/proto2/descriptor.js202
-rw-r--r--chromium/third_party/ink/closure/proto2/fielddescriptor.js313
-rw-r--r--chromium/third_party/ink/closure/proto2/message.js733
-rw-r--r--chromium/third_party/ink/closure/proto2/objectserializer.js202
-rw-r--r--chromium/third_party/ink/closure/proto2/serializer.js198
-rw-r--r--chromium/third_party/ink/closure/reflect/reflect.js139
-rw-r--r--chromium/third_party/ink/closure/soy/data.js525
-rw-r--r--chromium/third_party/ink/closure/soy/soy.js298
-rw-r--r--chromium/third_party/ink/closure/string/const.js186
-rw-r--r--chromium/third_party/ink/closure/string/string.js1642
-rw-r--r--chromium/third_party/ink/closure/string/typedstring.js48
-rw-r--r--chromium/third_party/ink/closure/structs/collection.js55
-rw-r--r--chromium/third_party/ink/closure/structs/inversionmap.js158
-rw-r--r--chromium/third_party/ink/closure/structs/map.js485
-rw-r--r--chromium/third_party/ink/closure/structs/set.js280
-rw-r--r--chromium/third_party/ink/closure/structs/structs.js354
-rw-r--r--chromium/third_party/ink/closure/style/style.js2046
-rw-r--r--chromium/third_party/ink/closure/ui/component.js1305
-rw-r--r--chromium/third_party/ink/closure/ui/idgenerator.js48
-rw-r--r--chromium/third_party/ink/closure/uri/uri.js1550
-rw-r--r--chromium/third_party/ink/closure/uri/utils.js1103
-rw-r--r--chromium/third_party/ink/closure/useragent/product.js182
-rw-r--r--chromium/third_party/ink/closure/useragent/useragent.js581
-rw-r--r--chromium/third_party/ink/ink/web/js/canvas_manager/canvas_manager.js598
-rw-r--r--chromium/third_party/ink/ink/web/js/cursor_updater.js124
-rw-r--r--chromium/third_party/ink/ink/web/js/embed/embed.js67
-rw-r--r--chromium/third_party/ink/ink/web/js/embed/embed_component.js417
-rw-r--r--chromium/third_party/ink/ink/web/js/embed/events.js225
-rw-r--r--chromium/third_party/ink/ink/web/js/main.soy.js31
-rw-r--r--chromium/third_party/ink/ink_event.pb.js2654
-rw-r--r--chromium/third_party/ink/ink_scripts.js118
-rw-r--r--chromium/third_party/ink/prebuilt/_platform_specific/arm/ink_arm.nexebin0 -> 3933536 bytes
-rw-r--r--chromium/third_party/ink/prebuilt/_platform_specific/x86-32/ink_x86_32.nexebin0 -> 4195704 bytes
-rw-r--r--chromium/third_party/ink/prebuilt/_platform_specific/x86-64/ink_x86_64.nexebin0 -> 4654840 bytes
-rw-r--r--chromium/third_party/ink/prebuilt/index.html18
-rw-r--r--chromium/third_party/ink/prebuilt/ink.nmf14
-rw-r--r--chromium/third_party/ink/prebuilt/ink_demo.js15
-rw-r--r--chromium/third_party/ink/prebuilt/ink_lib_binary.js154
-rw-r--r--chromium/third_party/ink/sketchology/proto/animations.pb.js984
-rw-r--r--chromium/third_party/ink/sketchology/proto/document.pb.js2831
-rw-r--r--chromium/third_party/ink/sketchology/proto/elements.pb.js3976
-rw-r--r--chromium/third_party/ink/sketchology/proto/rect_bounds.pb.js525
-rw-r--r--chromium/third_party/ink/sketchology/proto/sengine.pb.js8196
-rw-r--r--chromium/third_party/ink/sketchology/public/js/common/brush_model.js243
-rw-r--r--chromium/third_party/ink/sketchology/public/js/common/color.js113
-rw-r--r--chromium/third_party/ink/sketchology/public/js/common/element_listener.js32
-rw-r--r--chromium/third_party/ink/sketchology/public/js/common/model.js89
-rw-r--r--chromium/third_party/ink/sketchology/public/js/common/proto_serializer.js77
-rw-r--r--chromium/third_party/ink/sketchology/public/js/common/undo_state_change_event.js28
-rw-r--r--chromium/third_party/ink/sketchology/public/js/common/util.js292
-rw-r--r--chromium/third_party/ink/sketchology/public/nacl/embed.soy.js50
-rw-r--r--chromium/third_party/ink/sketchology/public/nacl/sketchology_engine_wrapper.js764
-rw-r--r--chromium/third_party/ink/template/soy/soyutils_usegoog.js2401
-rw-r--r--chromium/third_party/ink/wireserializer.js845
116 files changed, 67358 insertions, 0 deletions
diff --git a/chromium/third_party/ink/closure/array/array.js b/chromium/third_party/ink/closure/array/array.js
new file mode 100644
index 00000000000..926df8a6abc
--- /dev/null
+++ b/chromium/third_party/ink/closure/array/array.js
@@ -0,0 +1,1667 @@
+// Copyright 2006 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Utilities for manipulating arrays.
+ *
+ * @author pupius@google.com (Daniel Pupius)
+ * @author arv@google.com (Erik Arvidsson)
+ * @author pallosp@google.com (Peter Pallos)
+ */
+
+
+goog.provide('goog.array');
+
+goog.require('goog.asserts');
+
+
+/**
+ * @define {boolean} NATIVE_ARRAY_PROTOTYPES indicates whether the code should
+ * rely on Array.prototype functions, if available.
+ *
+ * The Array.prototype functions can be defined by external libraries like
+ * Prototype and setting this flag to false forces closure to use its own
+ * goog.array implementation.
+ *
+ * If your javascript can be loaded by a third party site and you are wary about
+ * relying on the prototype functions, specify
+ * "--define goog.NATIVE_ARRAY_PROTOTYPES=false" to the JSCompiler.
+ *
+ * Setting goog.TRUSTED_SITE to false will automatically set
+ * NATIVE_ARRAY_PROTOTYPES to false.
+ */
+goog.define('goog.NATIVE_ARRAY_PROTOTYPES', goog.TRUSTED_SITE);
+
+
+/**
+ * @define {boolean} If true, JSCompiler will use the native implementation of
+ * array functions where appropriate (e.g., {@code Array#filter}) and remove the
+ * unused pure JS implementation.
+ */
+goog.define('goog.array.ASSUME_NATIVE_FUNCTIONS', false);
+
+
+/**
+ * Returns the last element in an array without removing it.
+ * Same as goog.array.last.
+ * @param {IArrayLike<T>|string} array The array.
+ * @return {T} Last item in array.
+ * @template T
+ */
+goog.array.peek = function(array) {
+ return array[array.length - 1];
+};
+
+
+/**
+ * Returns the last element in an array without removing it.
+ * Same as goog.array.peek.
+ * @param {IArrayLike<T>|string} array The array.
+ * @return {T} Last item in array.
+ * @template T
+ */
+goog.array.last = goog.array.peek;
+
+// NOTE(arv): Since most of the array functions are generic it allows you to
+// pass an array-like object. Strings have a length and are considered array-
+// like. However, the 'in' operator does not work on strings so we cannot just
+// use the array path even if the browser supports indexing into strings. We
+// therefore end up splitting the string.
+
+
+/**
+ * Returns the index of the first element of an array with a specified value, or
+ * -1 if the element is not present in the array.
+ *
+ * See {@link http://tinyurl.com/developer-mozilla-org-array-indexof}
+ *
+ * @param {IArrayLike<T>|string} arr The array to be searched.
+ * @param {T} obj The object for which we are searching.
+ * @param {number=} opt_fromIndex The index at which to start the search. If
+ * omitted the search starts at index 0.
+ * @return {number} The index of the first matching array element.
+ * @template T
+ */
+goog.array.indexOf = goog.NATIVE_ARRAY_PROTOTYPES &&
+ (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.indexOf) ?
+ function(arr, obj, opt_fromIndex) {
+ goog.asserts.assert(arr.length != null);
+
+ return 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)) {
+ // Array.prototype.indexOf uses === so only strings should be found.
+ 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;
+ };
+
+
+/**
+ * Returns the index of the last element of an array with a specified value, or
+ * -1 if the element is not present in the array.
+ *
+ * See {@link http://tinyurl.com/developer-mozilla-org-array-lastindexof}
+ *
+ * @param {!IArrayLike<T>|string} arr The array to be searched.
+ * @param {T} obj The object for which we are searching.
+ * @param {?number=} opt_fromIndex The index at which to start the search. If
+ * omitted the search starts at the end of the array.
+ * @return {number} The index of the last matching array element.
+ * @template T
+ */
+goog.array.lastIndexOf = goog.NATIVE_ARRAY_PROTOTYPES &&
+ (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.lastIndexOf) ?
+ function(arr, obj, opt_fromIndex) {
+ goog.asserts.assert(arr.length != null);
+
+ // Firefox treats undefined and null as 0 in the fromIndex argument which
+ // leads it to always return -1
+ var fromIndex = opt_fromIndex == null ? arr.length - 1 : opt_fromIndex;
+ return 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)) {
+ // Array.prototype.lastIndexOf uses === so only strings should be found.
+ 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;
+ };
+
+
+/**
+ * Calls a function for each element in an array. Skips holes in the array.
+ * See {@link http://tinyurl.com/developer-mozilla-org-array-foreach}
+ *
+ * @param {IArrayLike<T>|string} arr Array or array like object over
+ * which to iterate.
+ * @param {?function(this: S, T, number, ?): ?} f The function to call for every
+ * element. This function takes 3 arguments (the element, the index and the
+ * array). The return value is ignored.
+ * @param {S=} opt_obj The object to be used as the value of 'this' within f.
+ * @template T,S
+ */
+goog.array.forEach = goog.NATIVE_ARRAY_PROTOTYPES &&
+ (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.forEach) ?
+ function(arr, f, opt_obj) {
+ goog.asserts.assert(arr.length != null);
+
+ Array.prototype.forEach.call(arr, f, opt_obj);
+ } :
+ function(arr, f, opt_obj) {
+ var l = arr.length; // must be fixed during loop... see docs
+ var arr2 = goog.isString(arr) ? arr.split('') : arr;
+ for (var i = 0; i < l; i++) {
+ if (i in arr2) {
+ f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr);
+ }
+ }
+ };
+
+
+/**
+ * Calls a function for each element in an array, starting from the last
+ * element rather than the first.
+ *
+ * @param {IArrayLike<T>|string} arr Array or array
+ * like object over which to iterate.
+ * @param {?function(this: S, T, number, ?): ?} f The function to call for every
+ * element. This function
+ * takes 3 arguments (the element, the index and the array). The return
+ * value is ignored.
+ * @param {S=} opt_obj The object to be used as the value of 'this'
+ * within f.
+ * @template T,S
+ */
+goog.array.forEachRight = function(arr, f, opt_obj) {
+ var l = arr.length; // must be fixed during loop... see docs
+ var arr2 = goog.isString(arr) ? arr.split('') : arr;
+ for (var i = l - 1; i >= 0; --i) {
+ if (i in arr2) {
+ f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr);
+ }
+ }
+};
+
+
+/**
+ * Calls a function for each element in an array, and if the function returns
+ * true adds the element to a new array.
+ *
+ * See {@link http://tinyurl.com/developer-mozilla-org-array-filter}
+ *
+ * @param {IArrayLike<T>|string} arr Array or array
+ * like object over which to iterate.
+ * @param {?function(this:S, T, number, ?):boolean} f The function to call for
+ * every element. This function
+ * takes 3 arguments (the element, the index and the array) and must
+ * return a Boolean. If the return value is true the element is added to the
+ * result array. If it is false the element is not included.
+ * @param {S=} opt_obj The object to be used as the value of 'this'
+ * within f.
+ * @return {!Array<T>} a new array in which only elements that passed the test
+ * are present.
+ * @template T,S
+ */
+goog.array.filter = goog.NATIVE_ARRAY_PROTOTYPES &&
+ (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.filter) ?
+ function(arr, f, opt_obj) {
+ goog.asserts.assert(arr.length != null);
+
+ return Array.prototype.filter.call(arr, f, opt_obj);
+ } :
+ function(arr, f, opt_obj) {
+ var l = arr.length; // must be fixed during loop... see docs
+ 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]; // in case f mutates arr2
+ if (f.call(/** @type {?} */ (opt_obj), val, i, arr)) {
+ res[resLength++] = val;
+ }
+ }
+ }
+ return res;
+ };
+
+
+/**
+ * Calls a function for each element in an array and inserts the result into a
+ * new array.
+ *
+ * See {@link http://tinyurl.com/developer-mozilla-org-array-map}
+ *
+ * @param {IArrayLike<VALUE>|string} arr Array or array like object
+ * over which to iterate.
+ * @param {function(this:THIS, VALUE, number, ?): RESULT} f The function to call
+ * for every element. This function takes 3 arguments (the element,
+ * the index and the array) and should return something. The result will be
+ * inserted into a new array.
+ * @param {THIS=} opt_obj The object to be used as the value of 'this' within f.
+ * @return {!Array<RESULT>} a new array with the results from f.
+ * @template THIS, VALUE, RESULT
+ */
+goog.array.map = goog.NATIVE_ARRAY_PROTOTYPES &&
+ (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.map) ?
+ function(arr, f, opt_obj) {
+ goog.asserts.assert(arr.length != null);
+
+ return Array.prototype.map.call(arr, f, opt_obj);
+ } :
+ function(arr, f, opt_obj) {
+ var l = arr.length; // must be fixed during loop... see docs
+ 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(/** @type {?} */ (opt_obj), arr2[i], i, arr);
+ }
+ }
+ return res;
+ };
+
+
+/**
+ * Passes every element of an array into a function and accumulates the result.
+ *
+ * See {@link http://tinyurl.com/developer-mozilla-org-array-reduce}
+ *
+ * For example:
+ * var a = [1, 2, 3, 4];
+ * goog.array.reduce(a, function(r, v, i, arr) {return r + v;}, 0);
+ * returns 10
+ *
+ * @param {IArrayLike<T>|string} arr Array or array
+ * like object over which to iterate.
+ * @param {function(this:S, R, T, number, ?) : R} f The function to call for
+ * every element. This function
+ * takes 4 arguments (the function's previous result or the initial value,
+ * the value of the current array element, the current array index, and the
+ * array itself)
+ * function(previousValue, currentValue, index, array).
+ * @param {?} val The initial value to pass into the function on the first call.
+ * @param {S=} opt_obj The object to be used as the value of 'this'
+ * within f.
+ * @return {R} Result of evaluating f repeatedly across the values of the array.
+ * @template T,S,R
+ */
+goog.array.reduce = goog.NATIVE_ARRAY_PROTOTYPES &&
+ (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.reduce) ?
+ function(arr, f, val, opt_obj) {
+ goog.asserts.assert(arr.length != null);
+ if (opt_obj) {
+ f = goog.bind(f, opt_obj);
+ }
+ return Array.prototype.reduce.call(arr, f, val);
+ } :
+ function(arr, f, val, opt_obj) {
+ var rval = val;
+ goog.array.forEach(arr, function(val, index) {
+ rval = f.call(/** @type {?} */ (opt_obj), rval, val, index, arr);
+ });
+ return rval;
+ };
+
+
+/**
+ * Passes every element of an array into a function and accumulates the result,
+ * starting from the last element and working towards the first.
+ *
+ * See {@link http://tinyurl.com/developer-mozilla-org-array-reduceright}
+ *
+ * For example:
+ * var a = ['a', 'b', 'c'];
+ * goog.array.reduceRight(a, function(r, v, i, arr) {return r + v;}, '');
+ * returns 'cba'
+ *
+ * @param {IArrayLike<T>|string} arr Array or array
+ * like object over which to iterate.
+ * @param {?function(this:S, R, T, number, ?) : R} f The function to call for
+ * every element. This function
+ * takes 4 arguments (the function's previous result or the initial value,
+ * the value of the current array element, the current array index, and the
+ * array itself)
+ * function(previousValue, currentValue, index, array).
+ * @param {?} val The initial value to pass into the function on the first call.
+ * @param {S=} opt_obj The object to be used as the value of 'this'
+ * within f.
+ * @return {R} Object returned as a result of evaluating f repeatedly across the
+ * values of the array.
+ * @template T,S,R
+ */
+goog.array.reduceRight = goog.NATIVE_ARRAY_PROTOTYPES &&
+ (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.reduceRight) ?
+ function(arr, f, val, opt_obj) {
+ goog.asserts.assert(arr.length != null);
+ goog.asserts.assert(f != null);
+ if (opt_obj) {
+ f = goog.bind(f, opt_obj);
+ }
+ return Array.prototype.reduceRight.call(arr, f, val);
+ } :
+ function(arr, f, val, opt_obj) {
+ var rval = val;
+ goog.array.forEachRight(arr, function(val, index) {
+ rval = f.call(/** @type {?} */ (opt_obj), rval, val, index, arr);
+ });
+ return rval;
+ };
+
+
+/**
+ * Calls f for each element of an array. If any call returns true, some()
+ * returns true (without checking the remaining elements). If all calls
+ * return false, some() returns false.
+ *
+ * See {@link http://tinyurl.com/developer-mozilla-org-array-some}
+ *
+ * @param {IArrayLike<T>|string} arr Array or array
+ * like object over which to iterate.
+ * @param {?function(this:S, T, number, ?) : boolean} f The function to call for
+ * for every element. This function takes 3 arguments (the element, the
+ * index and the array) and should return a boolean.
+ * @param {S=} opt_obj The object to be used as the value of 'this'
+ * within f.
+ * @return {boolean} true if any element passes the test.
+ * @template T,S
+ */
+goog.array.some = goog.NATIVE_ARRAY_PROTOTYPES &&
+ (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.some) ?
+ function(arr, f, opt_obj) {
+ goog.asserts.assert(arr.length != null);
+
+ return Array.prototype.some.call(arr, f, opt_obj);
+ } :
+ function(arr, f, opt_obj) {
+ var l = arr.length; // must be fixed during loop... see docs
+ var arr2 = goog.isString(arr) ? arr.split('') : arr;
+ for (var i = 0; i < l; i++) {
+ if (i in arr2 && f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr)) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+
+/**
+ * Call f for each element of an array. If all calls return true, every()
+ * returns true. If any call returns false, every() returns false and
+ * does not continue to check the remaining elements.
+ *
+ * See {@link http://tinyurl.com/developer-mozilla-org-array-every}
+ *
+ * @param {IArrayLike<T>|string} arr Array or array
+ * like object over which to iterate.
+ * @param {?function(this:S, T, number, ?) : boolean} f The function to call for
+ * for every element. This function takes 3 arguments (the element, the
+ * index and the array) and should return a boolean.
+ * @param {S=} opt_obj The object to be used as the value of 'this'
+ * within f.
+ * @return {boolean} false if any element fails the test.
+ * @template T,S
+ */
+goog.array.every = goog.NATIVE_ARRAY_PROTOTYPES &&
+ (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.every) ?
+ function(arr, f, opt_obj) {
+ goog.asserts.assert(arr.length != null);
+
+ return Array.prototype.every.call(arr, f, opt_obj);
+ } :
+ function(arr, f, opt_obj) {
+ var l = arr.length; // must be fixed during loop... see docs
+ var arr2 = goog.isString(arr) ? arr.split('') : arr;
+ for (var i = 0; i < l; i++) {
+ if (i in arr2 && !f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr)) {
+ return false;
+ }
+ }
+ return true;
+ };
+
+
+/**
+ * Counts the array elements that fulfill the predicate, i.e. for which the
+ * callback function returns true. Skips holes in the array.
+ *
+ * @param {!IArrayLike<T>|string} arr Array or array like object
+ * over which to iterate.
+ * @param {function(this: S, T, number, ?): boolean} f The function to call for
+ * every element. Takes 3 arguments (the element, the index and the array).
+ * @param {S=} opt_obj The object to be used as the value of 'this' within f.
+ * @return {number} The number of the matching elements.
+ * @template T,S
+ */
+goog.array.count = function(arr, f, opt_obj) {
+ var count = 0;
+ goog.array.forEach(arr, function(element, index, arr) {
+ if (f.call(/** @type {?} */ (opt_obj), element, index, arr)) {
+ ++count;
+ }
+ }, opt_obj);
+ return count;
+};
+
+
+/**
+ * Search an array for the first element that satisfies a given condition and
+ * return that element.
+ * @param {IArrayLike<T>|string} arr Array or array
+ * like object over which to iterate.
+ * @param {?function(this:S, T, number, ?) : boolean} f The function to call
+ * for every element. This function takes 3 arguments (the element, the
+ * index and the array) and should return a boolean.
+ * @param {S=} opt_obj An optional "this" context for the function.
+ * @return {T|null} The first array element that passes the test, or null if no
+ * element is found.
+ * @template T,S
+ */
+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];
+};
+
+
+/**
+ * Search an array for the first element that satisfies a given condition and
+ * return its index.
+ * @param {IArrayLike<T>|string} arr Array or array
+ * like object over which to iterate.
+ * @param {?function(this:S, T, number, ?) : boolean} f The function to call for
+ * every element. This function
+ * takes 3 arguments (the element, the index and the array) and should
+ * return a boolean.
+ * @param {S=} opt_obj An optional "this" context for the function.
+ * @return {number} The index of the first array element that passes the test,
+ * or -1 if no element is found.
+ * @template T,S
+ */
+goog.array.findIndex = function(arr, f, opt_obj) {
+ var l = arr.length; // must be fixed during loop... see docs
+ var arr2 = goog.isString(arr) ? arr.split('') : arr;
+ for (var i = 0; i < l; i++) {
+ if (i in arr2 && f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr)) {
+ return i;
+ }
+ }
+ return -1;
+};
+
+
+/**
+ * Search an array (in reverse order) for the last element that satisfies a
+ * given condition and return that element.
+ * @param {IArrayLike<T>|string} arr Array or array
+ * like object over which to iterate.
+ * @param {?function(this:S, T, number, ?) : boolean} f The function to call
+ * for every element. This function
+ * takes 3 arguments (the element, the index and the array) and should
+ * return a boolean.
+ * @param {S=} opt_obj An optional "this" context for the function.
+ * @return {T|null} The last array element that passes the test, or null if no
+ * element is found.
+ * @template T,S
+ */
+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];
+};
+
+
+/**
+ * Search an array (in reverse order) for the last element that satisfies a
+ * given condition and return its index.
+ * @param {IArrayLike<T>|string} arr Array or array
+ * like object over which to iterate.
+ * @param {?function(this:S, T, number, ?) : boolean} f The function to call
+ * for every element. This function
+ * takes 3 arguments (the element, the index and the array) and should
+ * return a boolean.
+ * @param {S=} opt_obj An optional "this" context for the function.
+ * @return {number} The index of the last array element that passes the test,
+ * or -1 if no element is found.
+ * @template T,S
+ */
+goog.array.findIndexRight = function(arr, f, opt_obj) {
+ var l = arr.length; // must be fixed during loop... see docs
+ var arr2 = goog.isString(arr) ? arr.split('') : arr;
+ for (var i = l - 1; i >= 0; i--) {
+ if (i in arr2 && f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr)) {
+ return i;
+ }
+ }
+ return -1;
+};
+
+
+/**
+ * Whether the array contains the given object.
+ * @param {IArrayLike<?>|string} arr The array to test for the presence of the
+ * element.
+ * @param {*} obj The object for which to test.
+ * @return {boolean} true if obj is present.
+ */
+goog.array.contains = function(arr, obj) {
+ return goog.array.indexOf(arr, obj) >= 0;
+};
+
+
+/**
+ * Whether the array is empty.
+ * @param {IArrayLike<?>|string} arr The array to test.
+ * @return {boolean} true if empty.
+ */
+goog.array.isEmpty = function(arr) {
+ return arr.length == 0;
+};
+
+
+/**
+ * Clears the array.
+ * @param {IArrayLike<?>} arr Array or array like object to clear.
+ */
+goog.array.clear = function(arr) {
+ // For non real arrays we don't have the magic length so we delete the
+ // indices.
+ if (!goog.isArray(arr)) {
+ for (var i = arr.length - 1; i >= 0; i--) {
+ delete arr[i];
+ }
+ }
+ arr.length = 0;
+};
+
+
+/**
+ * Pushes an item into an array, if it's not already in the array.
+ * @param {Array<T>} arr Array into which to insert the item.
+ * @param {T} obj Value to add.
+ * @template T
+ */
+goog.array.insert = function(arr, obj) {
+ if (!goog.array.contains(arr, obj)) {
+ arr.push(obj);
+ }
+};
+
+
+/**
+ * Inserts an object at the given index of the array.
+ * @param {IArrayLike<?>} arr The array to modify.
+ * @param {*} obj The object to insert.
+ * @param {number=} opt_i The index at which to insert the object. If omitted,
+ * treated as 0. A negative index is counted from the end of the array.
+ */
+goog.array.insertAt = function(arr, obj, opt_i) {
+ goog.array.splice(arr, opt_i, 0, obj);
+};
+
+
+/**
+ * Inserts at the given index of the array, all elements of another array.
+ * @param {IArrayLike<?>} arr The array to modify.
+ * @param {IArrayLike<?>} elementsToAdd The array of elements to add.
+ * @param {number=} opt_i The index at which to insert the object. If omitted,
+ * treated as 0. A negative index is counted from the end of the array.
+ */
+goog.array.insertArrayAt = function(arr, elementsToAdd, opt_i) {
+ goog.partial(goog.array.splice, arr, opt_i, 0).apply(null, elementsToAdd);
+};
+
+
+/**
+ * Inserts an object into an array before a specified object.
+ * @param {Array<T>} arr The array to modify.
+ * @param {T} obj The object to insert.
+ * @param {T=} opt_obj2 The object before which obj should be inserted. If obj2
+ * is omitted or not found, obj is inserted at the end of the array.
+ * @template T
+ */
+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);
+ }
+};
+
+
+/**
+ * Removes the first occurrence of a particular value from an array.
+ * @param {IArrayLike<T>} arr Array from which to remove
+ * value.
+ * @param {T} obj Object to remove.
+ * @return {boolean} True if an element was removed.
+ * @template T
+ */
+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;
+};
+
+
+/**
+ * Removes the last occurrence of a particular value from an array.
+ * @param {!IArrayLike<T>} arr Array from which to remove value.
+ * @param {T} obj Object to remove.
+ * @return {boolean} True if an element was removed.
+ * @template T
+ */
+goog.array.removeLast = function(arr, obj) {
+ var i = goog.array.lastIndexOf(arr, obj);
+ if (i >= 0) {
+ goog.array.removeAt(arr, i);
+ return true;
+ }
+ return false;
+};
+
+
+/**
+ * Removes from an array the element at index i
+ * @param {IArrayLike<?>} arr Array or array like object from which to
+ * remove value.
+ * @param {number} i The index to remove.
+ * @return {boolean} True if an element was removed.
+ */
+goog.array.removeAt = function(arr, i) {
+ goog.asserts.assert(arr.length != null);
+
+ // use generic form of splice
+ // splice returns the removed items and if successful the length of that
+ // will be 1
+ return Array.prototype.splice.call(arr, i, 1).length == 1;
+};
+
+
+/**
+ * Removes the first value that satisfies the given condition.
+ * @param {IArrayLike<T>} arr Array or array
+ * like object over which to iterate.
+ * @param {?function(this:S, T, number, ?) : boolean} f The function to call
+ * for every element. This function
+ * takes 3 arguments (the element, the index and the array) and should
+ * return a boolean.
+ * @param {S=} opt_obj An optional "this" context for the function.
+ * @return {boolean} True if an element was removed.
+ * @template T,S
+ */
+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;
+};
+
+
+/**
+ * Removes all values that satisfy the given condition.
+ * @param {IArrayLike<T>} arr Array or array
+ * like object over which to iterate.
+ * @param {?function(this:S, T, number, ?) : boolean} f The function to call
+ * for every element. This function
+ * takes 3 arguments (the element, the index and the array) and should
+ * return a boolean.
+ * @param {S=} opt_obj An optional "this" context for the function.
+ * @return {number} The number of items removed
+ * @template T,S
+ */
+goog.array.removeAllIf = function(arr, f, opt_obj) {
+ var removedCount = 0;
+ goog.array.forEachRight(arr, function(val, index) {
+ if (f.call(/** @type {?} */ (opt_obj), val, index, arr)) {
+ if (goog.array.removeAt(arr, index)) {
+ removedCount++;
+ }
+ }
+ });
+ return removedCount;
+};
+
+
+/**
+ * Returns a new array that is the result of joining the arguments. If arrays
+ * are passed then their items are added, however, if non-arrays are passed they
+ * will be added to the return array as is.
+ *
+ * Note that ArrayLike objects will be added as is, rather than having their
+ * items added.
+ *
+ * goog.array.concat([1, 2], [3, 4]) -> [1, 2, 3, 4]
+ * goog.array.concat(0, [1, 2]) -> [0, 1, 2]
+ * goog.array.concat([1, 2], null) -> [1, 2, null]
+ *
+ * There is bug in all current versions of IE (6, 7 and 8) where arrays created
+ * in an iframe become corrupted soon (not immediately) after the iframe is
+ * destroyed. This is common if loading data via goog.net.IframeIo, for example.
+ * This corruption only affects the concat method which will start throwing
+ * Catastrophic Errors (#-2147418113).
+ *
+ * See http://endoflow.com/scratch/corrupted-arrays.html for a test case.
+ *
+ * Internally goog.array should use this, so that all methods will continue to
+ * work on these broken array objects.
+ *
+ * @param {...*} var_args Items to concatenate. Arrays will have each item
+ * added, while primitives and objects will be added as is.
+ * @return {!Array<?>} The new resultant array.
+ */
+goog.array.concat = function(var_args) {
+ return Array.prototype.concat.apply([], arguments);
+};
+
+
+/**
+ * Returns a new array that contains the contents of all the arrays passed.
+ * @param {...!Array<T>} var_args
+ * @return {!Array<T>}
+ * @template T
+ */
+goog.array.join = function(var_args) {
+ return Array.prototype.concat.apply([], arguments);
+};
+
+
+/**
+ * Converts an object to an array.
+ * @param {IArrayLike<T>|string} object The object to convert to an
+ * array.
+ * @return {!Array<T>} The object converted into an array. If object has a
+ * length property, every property indexed with a non-negative number
+ * less than length will be included in the result. If object does not
+ * have a length property, an empty array will be returned.
+ * @template T
+ */
+goog.array.toArray = function(object) {
+ var length = object.length;
+
+ // If length is not a number the following it false. This case is kept for
+ // backwards compatibility since there are callers that pass objects that are
+ // not array like.
+ if (length > 0) {
+ var rv = new Array(length);
+ for (var i = 0; i < length; i++) {
+ rv[i] = object[i];
+ }
+ return rv;
+ }
+ return [];
+};
+
+
+/**
+ * Does a shallow copy of an array.
+ * @param {IArrayLike<T>|string} arr Array or array-like object to
+ * clone.
+ * @return {!Array<T>} Clone of the input array.
+ * @template T
+ */
+goog.array.clone = goog.array.toArray;
+
+
+/**
+ * Extends an array with another array, element, or "array like" object.
+ * This function operates 'in-place', it does not create a new Array.
+ *
+ * Example:
+ * var a = [];
+ * goog.array.extend(a, [0, 1]);
+ * a; // [0, 1]
+ * goog.array.extend(a, 2);
+ * a; // [0, 1, 2]
+ *
+ * @param {Array<VALUE>} arr1 The array to modify.
+ * @param {...(Array<VALUE>|VALUE)} var_args The elements or arrays of elements
+ * to add to arr1.
+ * @template VALUE
+ */
+goog.array.extend = function(arr1, var_args) {
+ for (var i = 1; i < arguments.length; i++) {
+ var arr2 = arguments[i];
+ if (goog.isArrayLike(arr2)) {
+ var len1 = arr1.length || 0;
+ var len2 = arr2.length || 0;
+ arr1.length = len1 + len2;
+ for (var j = 0; j < len2; j++) {
+ arr1[len1 + j] = arr2[j];
+ }
+ } else {
+ arr1.push(arr2);
+ }
+ }
+};
+
+
+/**
+ * Adds or removes elements from an array. This is a generic version of Array
+ * splice. This means that it might work on other objects similar to arrays,
+ * such as the arguments object.
+ *
+ * @param {IArrayLike<T>} arr The array to modify.
+ * @param {number|undefined} index The index at which to start changing the
+ * array. If not defined, treated as 0.
+ * @param {number} howMany How many elements to remove (0 means no removal. A
+ * value below 0 is treated as zero and so is any other non number. Numbers
+ * are floored).
+ * @param {...T} var_args Optional, additional elements to insert into the
+ * array.
+ * @return {!Array<T>} the removed elements.
+ * @template T
+ */
+goog.array.splice = function(arr, index, howMany, var_args) {
+ goog.asserts.assert(arr.length != null);
+
+ return Array.prototype.splice.apply(arr, goog.array.slice(arguments, 1));
+};
+
+
+/**
+ * Returns a new array from a segment of an array. This is a generic version of
+ * Array slice. This means that it might work on other objects similar to
+ * arrays, such as the arguments object.
+ *
+ * @param {IArrayLike<T>|string} arr The array from
+ * which to copy a segment.
+ * @param {number} start The index of the first element to copy.
+ * @param {number=} opt_end The index after the last element to copy.
+ * @return {!Array<T>} A new array containing the specified segment of the
+ * original array.
+ * @template T
+ */
+goog.array.slice = function(arr, start, opt_end) {
+ goog.asserts.assert(arr.length != null);
+
+ // passing 1 arg to slice is not the same as passing 2 where the second is
+ // null or undefined (in that case the second argument is treated as 0).
+ // we could use slice on the arguments object and then use apply instead of
+ // testing the length
+ if (arguments.length <= 2) {
+ return Array.prototype.slice.call(arr, start);
+ } else {
+ return Array.prototype.slice.call(arr, start, opt_end);
+ }
+};
+
+
+/**
+ * Removes all duplicates from an array (retaining only the first
+ * occurrence of each array element). This function modifies the
+ * array in place and doesn't change the order of the non-duplicate items.
+ *
+ * For objects, duplicates are identified as having the same unique ID as
+ * defined by {@link goog.getUid}.
+ *
+ * Alternatively you can specify a custom hash function that returns a unique
+ * value for each item in the array it should consider unique.
+ *
+ * Runtime: N,
+ * Worstcase space: 2N (no dupes)
+ *
+ * @param {IArrayLike<T>} arr The array from which to remove
+ * duplicates.
+ * @param {Array=} opt_rv An optional array in which to return the results,
+ * instead of performing the removal inplace. If specified, the original
+ * array will remain unchanged.
+ * @param {function(T):string=} opt_hashFn An optional function to use to
+ * apply to every item in the array. This function should return a unique
+ * value for each item in the array it should consider unique.
+ * @template T
+ */
+goog.array.removeDuplicates = function(arr, opt_rv, opt_hashFn) {
+ var returnArray = opt_rv || arr;
+ var defaultHashFn = function(item) {
+ // Prefix each type with a single character representing the type to
+ // prevent conflicting keys (e.g. true and 'true').
+ return goog.isObject(item) ? 'o' + goog.getUid(item) :
+ (typeof item).charAt(0) + item;
+ };
+ var hashFn = opt_hashFn || defaultHashFn;
+
+ var seen = {}, cursorInsert = 0, cursorRead = 0;
+ while (cursorRead < arr.length) {
+ var current = arr[cursorRead++];
+ var key = hashFn(current);
+ if (!Object.prototype.hasOwnProperty.call(seen, key)) {
+ seen[key] = true;
+ returnArray[cursorInsert++] = current;
+ }
+ }
+ returnArray.length = cursorInsert;
+};
+
+
+/**
+ * Searches the specified array for the specified target using the binary
+ * search algorithm. If no opt_compareFn is specified, elements are compared
+ * using <code>goog.array.defaultCompare</code>, which compares the elements
+ * using the built in < and > operators. This will produce the expected
+ * behavior for homogeneous arrays of String(s) and Number(s). The array
+ * specified <b>must</b> be sorted in ascending order (as defined by the
+ * comparison function). If the array is not sorted, results are undefined.
+ * If the array contains multiple instances of the specified target value, any
+ * of these instances may be found.
+ *
+ * Runtime: O(log n)
+ *
+ * @param {IArrayLike<VALUE>} arr The array to be searched.
+ * @param {TARGET} target The sought value.
+ * @param {function(TARGET, VALUE): number=} opt_compareFn Optional comparison
+ * function by which the array is ordered. Should take 2 arguments to
+ * compare, and return a negative number, zero, or a positive number
+ * depending on whether the first argument is less than, equal to, or
+ * greater than the second.
+ * @return {number} Lowest index of the target value if found, otherwise
+ * (-(insertion point) - 1). The insertion point is where the value should
+ * be inserted into arr to preserve the sorted property. Return value >= 0
+ * iff target is found.
+ * @template TARGET, VALUE
+ */
+goog.array.binarySearch = function(arr, target, opt_compareFn) {
+ return goog.array.binarySearch_(
+ arr, opt_compareFn || goog.array.defaultCompare, false /* isEvaluator */,
+ target);
+};
+
+
+/**
+ * Selects an index in the specified array using the binary search algorithm.
+ * The evaluator receives an element and determines whether the desired index
+ * is before, at, or after it. The evaluator must be consistent (formally,
+ * goog.array.map(goog.array.map(arr, evaluator, opt_obj), goog.math.sign)
+ * must be monotonically non-increasing).
+ *
+ * Runtime: O(log n)
+ *
+ * @param {IArrayLike<VALUE>} arr The array to be searched.
+ * @param {function(this:THIS, VALUE, number, ?): number} evaluator
+ * Evaluator function that receives 3 arguments (the element, the index and
+ * the array). Should return a negative number, zero, or a positive number
+ * depending on whether the desired index is before, at, or after the
+ * element passed to it.
+ * @param {THIS=} opt_obj The object to be used as the value of 'this'
+ * within evaluator.
+ * @return {number} Index of the leftmost element matched by the evaluator, if
+ * such exists; otherwise (-(insertion point) - 1). The insertion point is
+ * the index of the first element for which the evaluator returns negative,
+ * or arr.length if no such element exists. The return value is non-negative
+ * iff a match is found.
+ * @template THIS, VALUE
+ */
+goog.array.binarySelect = function(arr, evaluator, opt_obj) {
+ return goog.array.binarySearch_(
+ arr, evaluator, true /* isEvaluator */, undefined /* opt_target */,
+ opt_obj);
+};
+
+
+/**
+ * Implementation of a binary search algorithm which knows how to use both
+ * comparison functions and evaluators. If an evaluator is provided, will call
+ * the evaluator with the given optional data object, conforming to the
+ * interface defined in binarySelect. Otherwise, if a comparison function is
+ * provided, will call the comparison function against the given data object.
+ *
+ * This implementation purposefully does not use goog.bind or goog.partial for
+ * performance reasons.
+ *
+ * Runtime: O(log n)
+ *
+ * @param {IArrayLike<?>} arr The array to be searched.
+ * @param {function(?, ?, ?): number | function(?, ?): number} compareFn
+ * Either an evaluator or a comparison function, as defined by binarySearch
+ * and binarySelect above.
+ * @param {boolean} isEvaluator Whether the function is an evaluator or a
+ * comparison function.
+ * @param {?=} opt_target If the function is a comparison function, then
+ * this is the target to binary search for.
+ * @param {Object=} opt_selfObj If the function is an evaluator, this is an
+ * optional this object for the evaluator.
+ * @return {number} Lowest index of the target value if found, otherwise
+ * (-(insertion point) - 1). The insertion point is where the value should
+ * be inserted into arr to preserve the sorted property. Return value >= 0
+ * iff target is found.
+ * @private
+ */
+goog.array.binarySearch_ = function(
+ arr, compareFn, isEvaluator, opt_target, opt_selfObj) {
+ var left = 0; // inclusive
+ var right = arr.length; // exclusive
+ var found;
+ while (left < right) {
+ var middle = (left + right) >> 1;
+ var compareResult;
+ if (isEvaluator) {
+ compareResult = compareFn.call(opt_selfObj, arr[middle], middle, arr);
+ } else {
+ // NOTE(dimvar): To avoid this cast, we'd have to use function overloading
+ // for the type of binarySearch_, which the type system can't express yet.
+ compareResult = /** @type {function(?, ?): number} */ (compareFn)(
+ opt_target, arr[middle]);
+ }
+ if (compareResult > 0) {
+ left = middle + 1;
+ } else {
+ right = middle;
+ // We are looking for the lowest index so we can't return immediately.
+ found = !compareResult;
+ }
+ }
+ // left is the index if found, or the insertion point otherwise.
+ // ~left is a shorthand for -left - 1.
+ return found ? left : ~left;
+};
+
+
+/**
+ * Sorts the specified array into ascending order. If no opt_compareFn is
+ * specified, elements are compared using
+ * <code>goog.array.defaultCompare</code>, which compares the elements using
+ * the built in < and > operators. This will produce the expected behavior
+ * for homogeneous arrays of String(s) and Number(s), unlike the native sort,
+ * but will give unpredictable results for heterogeneous lists of strings and
+ * numbers with different numbers of digits.
+ *
+ * This sort is not guaranteed to be stable.
+ *
+ * Runtime: Same as <code>Array.prototype.sort</code>
+ *
+ * @param {Array<T>} arr The array to be sorted.
+ * @param {?function(T,T):number=} opt_compareFn Optional comparison
+ * function by which the
+ * array is to be ordered. Should take 2 arguments to compare, and return a
+ * negative number, zero, or a positive number depending on whether the
+ * first argument is less than, equal to, or greater than the second.
+ * @template T
+ */
+goog.array.sort = function(arr, opt_compareFn) {
+ // TODO(arv): Update type annotation since null is not accepted.
+ arr.sort(opt_compareFn || goog.array.defaultCompare);
+};
+
+
+/**
+ * Sorts the specified array into ascending order in a stable way. If no
+ * opt_compareFn is specified, elements are compared using
+ * <code>goog.array.defaultCompare</code>, which compares the elements using
+ * the built in < and > operators. This will produce the expected behavior
+ * for homogeneous arrays of String(s) and Number(s).
+ *
+ * Runtime: Same as <code>Array.prototype.sort</code>, plus an additional
+ * O(n) overhead of copying the array twice.
+ *
+ * @param {Array<T>} arr The array to be sorted.
+ * @param {?function(T, T): number=} opt_compareFn Optional comparison function
+ * by which the array is to be ordered. Should take 2 arguments to compare,
+ * and return a negative number, zero, or a positive number depending on
+ * whether the first argument is less than, equal to, or greater than the
+ * second.
+ * @template T
+ */
+goog.array.stableSort = function(arr, opt_compareFn) {
+ var compArr = new Array(arr.length);
+ for (var i = 0; i < arr.length; i++) {
+ compArr[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(compArr, stableCompareFn);
+ for (var i = 0; i < arr.length; i++) {
+ arr[i] = compArr[i].value;
+ }
+};
+
+
+/**
+ * Sort the specified array into ascending order based on item keys
+ * returned by the specified key function.
+ * If no opt_compareFn is specified, the keys are compared in ascending order
+ * using <code>goog.array.defaultCompare</code>.
+ *
+ * Runtime: O(S(f(n)), where S is runtime of <code>goog.array.sort</code>
+ * and f(n) is runtime of the key function.
+ *
+ * @param {Array<T>} arr The array to be sorted.
+ * @param {function(T): K} keyFn Function taking array element and returning
+ * a key used for sorting this element.
+ * @param {?function(K, K): number=} opt_compareFn Optional comparison function
+ * by which the keys are to be ordered. Should take 2 arguments to compare,
+ * and return a negative number, zero, or a positive number depending on
+ * whether the first argument is less than, equal to, or greater than the
+ * second.
+ * @template T,K
+ */
+goog.array.sortByKey = function(arr, keyFn, opt_compareFn) {
+ var keyCompareFn = opt_compareFn || goog.array.defaultCompare;
+ goog.array.sort(
+ arr, function(a, b) { return keyCompareFn(keyFn(a), keyFn(b)); });
+};
+
+
+/**
+ * Sorts an array of objects by the specified object key and compare
+ * function. If no compare function is provided, the key values are
+ * compared in ascending order using <code>goog.array.defaultCompare</code>.
+ * This won't work for keys that get renamed by the compiler. So use
+ * {'foo': 1, 'bar': 2} rather than {foo: 1, bar: 2}.
+ * @param {Array<Object>} arr An array of objects to sort.
+ * @param {string} key The object key to sort by.
+ * @param {Function=} opt_compareFn The function to use to compare key
+ * values.
+ */
+goog.array.sortObjectsByKey = function(arr, key, opt_compareFn) {
+ goog.array.sortByKey(arr, function(obj) { return obj[key]; }, opt_compareFn);
+};
+
+
+/**
+ * Tells if the array is sorted.
+ * @param {!Array<T>} arr The array.
+ * @param {?function(T,T):number=} opt_compareFn Function to compare the
+ * array elements.
+ * Should take 2 arguments to compare, and return a negative number, zero,
+ * or a positive number depending on whether the first argument is less
+ * than, equal to, or greater than the second.
+ * @param {boolean=} opt_strict If true no equal elements are allowed.
+ * @return {boolean} Whether the array is sorted.
+ * @template T
+ */
+goog.array.isSorted = function(arr, opt_compareFn, opt_strict) {
+ var compare = opt_compareFn || goog.array.defaultCompare;
+ for (var i = 1; i < arr.length; i++) {
+ var compareResult = compare(arr[i - 1], arr[i]);
+ if (compareResult > 0 || compareResult == 0 && opt_strict) {
+ return false;
+ }
+ }
+ return true;
+};
+
+
+/**
+ * Compares two arrays for equality. Two arrays are considered equal if they
+ * have the same length and their corresponding elements are equal according to
+ * the comparison function.
+ *
+ * @param {IArrayLike<?>} arr1 The first array to compare.
+ * @param {IArrayLike<?>} arr2 The second array to compare.
+ * @param {Function=} opt_equalsFn Optional comparison function.
+ * Should take 2 arguments to compare, and return true if the arguments
+ * are equal. Defaults to {@link goog.array.defaultCompareEquality} which
+ * compares the elements using the built-in '===' operator.
+ * @return {boolean} Whether the two arrays are equal.
+ */
+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;
+};
+
+
+/**
+ * 3-way array compare function.
+ * @param {!IArrayLike<VALUE>} arr1 The first array to
+ * compare.
+ * @param {!IArrayLike<VALUE>} arr2 The second array to
+ * compare.
+ * @param {function(VALUE, VALUE): number=} opt_compareFn Optional comparison
+ * function by which the array is to be ordered. Should take 2 arguments to
+ * compare, and return a negative number, zero, or a positive number
+ * depending on whether the first argument is less than, equal to, or
+ * greater than the second.
+ * @return {number} Negative number, zero, or a positive number depending on
+ * whether the first argument is less than, equal to, or greater than the
+ * second.
+ * @template VALUE
+ */
+goog.array.compare3 = function(arr1, arr2, opt_compareFn) {
+ var compare = opt_compareFn || goog.array.defaultCompare;
+ var l = Math.min(arr1.length, arr2.length);
+ for (var i = 0; i < l; i++) {
+ var result = compare(arr1[i], arr2[i]);
+ if (result != 0) {
+ return result;
+ }
+ }
+ return goog.array.defaultCompare(arr1.length, arr2.length);
+};
+
+
+/**
+ * Compares its two arguments for order, using the built in < and >
+ * operators.
+ * @param {VALUE} a The first object to be compared.
+ * @param {VALUE} b The second object to be compared.
+ * @return {number} A negative number, zero, or a positive number as the first
+ * argument is less than, equal to, or greater than the second,
+ * respectively.
+ * @template VALUE
+ */
+goog.array.defaultCompare = function(a, b) {
+ return a > b ? 1 : a < b ? -1 : 0;
+};
+
+
+/**
+ * Compares its two arguments for inverse order, using the built in < and >
+ * operators.
+ * @param {VALUE} a The first object to be compared.
+ * @param {VALUE} b The second object to be compared.
+ * @return {number} A negative number, zero, or a positive number as the first
+ * argument is greater than, equal to, or less than the second,
+ * respectively.
+ * @template VALUE
+ */
+goog.array.inverseDefaultCompare = function(a, b) {
+ return -goog.array.defaultCompare(a, b);
+};
+
+
+/**
+ * Compares its two arguments for equality, using the built in === operator.
+ * @param {*} a The first object to compare.
+ * @param {*} b The second object to compare.
+ * @return {boolean} True if the two arguments are equal, false otherwise.
+ */
+goog.array.defaultCompareEquality = function(a, b) {
+ return a === b;
+};
+
+
+/**
+ * Inserts a value into a sorted array. The array is not modified if the
+ * value is already present.
+ * @param {IArrayLike<VALUE>} array The array to modify.
+ * @param {VALUE} value The object to insert.
+ * @param {function(VALUE, VALUE): number=} opt_compareFn Optional comparison
+ * function by which the array is ordered. Should take 2 arguments to
+ * compare, and return a negative number, zero, or a positive number
+ * depending on whether the first argument is less than, equal to, or
+ * greater than the second.
+ * @return {boolean} True if an element was inserted.
+ * @template VALUE
+ */
+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;
+};
+
+
+/**
+ * Removes a value from a sorted array.
+ * @param {!IArrayLike<VALUE>} array The array to modify.
+ * @param {VALUE} value The object to remove.
+ * @param {function(VALUE, VALUE): number=} opt_compareFn Optional comparison
+ * function by which the array is ordered. Should take 2 arguments to
+ * compare, and return a negative number, zero, or a positive number
+ * depending on whether the first argument is less than, equal to, or
+ * greater than the second.
+ * @return {boolean} True if an element was removed.
+ * @template VALUE
+ */
+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;
+};
+
+
+/**
+ * Splits an array into disjoint buckets according to a splitting function.
+ * @param {Array<T>} array The array.
+ * @param {function(this:S, T, number, !Array<T>):?} sorter Function to call for
+ * every element. This takes 3 arguments (the element, the index and the
+ * array) and must return a valid object key (a string, number, etc), or
+ * undefined, if that object should not be placed in a bucket.
+ * @param {S=} opt_obj The object to be used as the value of 'this' within
+ * sorter.
+ * @return {!Object<!Array<T>>} An object, with keys being all of the unique
+ * return values of sorter, and values being arrays containing the items for
+ * which the splitter returned that key.
+ * @template T,S
+ */
+goog.array.bucket = function(array, sorter, opt_obj) {
+ var buckets = {};
+
+ for (var i = 0; i < array.length; i++) {
+ var value = array[i];
+ var key = sorter.call(/** @type {?} */ (opt_obj), value, i, array);
+ if (goog.isDef(key)) {
+ // Push the value to the right bucket, creating it if necessary.
+ var bucket = buckets[key] || (buckets[key] = []);
+ bucket.push(value);
+ }
+ }
+
+ return buckets;
+};
+
+
+/**
+ * Creates a new object built from the provided array and the key-generation
+ * function.
+ * @param {IArrayLike<T>} arr Array or array like object over
+ * which to iterate whose elements will be the values in the new object.
+ * @param {?function(this:S, T, number, ?) : string} keyFunc The function to
+ * call for every element. This function takes 3 arguments (the element, the
+ * index and the array) and should return a string that will be used as the
+ * key for the element in the new object. If the function returns the same
+ * key for more than one element, the value for that key is
+ * implementation-defined.
+ * @param {S=} opt_obj The object to be used as the value of 'this'
+ * within keyFunc.
+ * @return {!Object<T>} The new object.
+ * @template T,S
+ */
+goog.array.toObject = function(arr, keyFunc, opt_obj) {
+ var ret = {};
+ goog.array.forEach(arr, function(element, index) {
+ ret[keyFunc.call(/** @type {?} */ (opt_obj), element, index, arr)] =
+ element;
+ });
+ return ret;
+};
+
+
+/**
+ * Creates a range of numbers in an arithmetic progression.
+ *
+ * Range takes 1, 2, or 3 arguments:
+ * <pre>
+ * range(5) is the same as range(0, 5, 1) and produces [0, 1, 2, 3, 4]
+ * range(2, 5) is the same as range(2, 5, 1) and produces [2, 3, 4]
+ * range(-2, -5, -1) produces [-2, -3, -4]
+ * range(-2, -5, 1) produces [], since stepping by 1 wouldn't ever reach -5.
+ * </pre>
+ *
+ * @param {number} startOrEnd The starting value of the range if an end argument
+ * is provided. Otherwise, the start value is 0, and this is the end value.
+ * @param {number=} opt_end The optional end value of the range.
+ * @param {number=} opt_step The step size between range values. Defaults to 1
+ * if opt_step is undefined or 0.
+ * @return {!Array<number>} An array of numbers for the requested range. May be
+ * an empty array if adding the step would not converge toward the end
+ * value.
+ */
+goog.array.range = function(startOrEnd, opt_end, opt_step) {
+ var array = [];
+ var start = 0;
+ var end = startOrEnd;
+ var step = opt_step || 1;
+ if (opt_end !== undefined) {
+ start = startOrEnd;
+ end = opt_end;
+ }
+
+ if (step * (end - start) < 0) {
+ // Sign mismatch: start + step will never reach the end value.
+ return [];
+ }
+
+ if (step > 0) {
+ for (var i = start; i < end; i += step) {
+ array.push(i);
+ }
+ } else {
+ for (var i = start; i > end; i += step) {
+ array.push(i);
+ }
+ }
+ return array;
+};
+
+
+/**
+ * Returns an array consisting of the given value repeated N times.
+ *
+ * @param {VALUE} value The value to repeat.
+ * @param {number} n The repeat count.
+ * @return {!Array<VALUE>} An array with the repeated value.
+ * @template VALUE
+ */
+goog.array.repeat = function(value, n) {
+ var array = [];
+ for (var i = 0; i < n; i++) {
+ array[i] = value;
+ }
+ return array;
+};
+
+
+/**
+ * Returns an array consisting of every argument with all arrays
+ * expanded in-place recursively.
+ *
+ * @param {...*} var_args The values to flatten.
+ * @return {!Array<?>} An array containing the flattened values.
+ */
+goog.array.flatten = function(var_args) {
+ var CHUNK_SIZE = 8192;
+
+ var result = [];
+ for (var i = 0; i < arguments.length; i++) {
+ var element = arguments[i];
+ if (goog.isArray(element)) {
+ for (var c = 0; c < element.length; c += CHUNK_SIZE) {
+ var chunk = goog.array.slice(element, c, c + CHUNK_SIZE);
+ var recurseResult = goog.array.flatten.apply(null, chunk);
+ for (var r = 0; r < recurseResult.length; r++) {
+ result.push(recurseResult[r]);
+ }
+ }
+ } else {
+ result.push(element);
+ }
+ }
+ return result;
+};
+
+
+/**
+ * Rotates an array in-place. After calling this method, the element at
+ * index i will be the element previously at index (i - n) %
+ * array.length, for all values of i between 0 and array.length - 1,
+ * inclusive.
+ *
+ * For example, suppose list comprises [t, a, n, k, s]. After invoking
+ * rotate(array, 1) (or rotate(array, -4)), array will comprise [s, t, a, n, k].
+ *
+ * @param {!Array<T>} array The array to rotate.
+ * @param {number} n The amount to rotate.
+ * @return {!Array<T>} The array.
+ * @template T
+ */
+goog.array.rotate = function(array, n) {
+ goog.asserts.assert(array.length != null);
+
+ if (array.length) {
+ n %= array.length;
+ if (n > 0) {
+ Array.prototype.unshift.apply(array, array.splice(-n, n));
+ } else if (n < 0) {
+ Array.prototype.push.apply(array, array.splice(0, -n));
+ }
+ }
+ return array;
+};
+
+
+/**
+ * Moves one item of an array to a new position keeping the order of the rest
+ * of the items. Example use case: keeping a list of JavaScript objects
+ * synchronized with the corresponding list of DOM elements after one of the
+ * elements has been dragged to a new position.
+ * @param {!IArrayLike<?>} arr The array to modify.
+ * @param {number} fromIndex Index of the item to move between 0 and
+ * {@code arr.length - 1}.
+ * @param {number} toIndex Target index between 0 and {@code arr.length - 1}.
+ */
+goog.array.moveItem = function(arr, fromIndex, toIndex) {
+ goog.asserts.assert(fromIndex >= 0 && fromIndex < arr.length);
+ goog.asserts.assert(toIndex >= 0 && toIndex < arr.length);
+ // Remove 1 item at fromIndex.
+ var removedItems = Array.prototype.splice.call(arr, fromIndex, 1);
+ // Insert the removed item at toIndex.
+ Array.prototype.splice.call(arr, toIndex, 0, removedItems[0]);
+ // We don't use goog.array.insertAt and goog.array.removeAt, because they're
+ // significantly slower than splice.
+};
+
+
+/**
+ * Creates a new array for which the element at position i is an array of the
+ * ith element of the provided arrays. The returned array will only be as long
+ * as the shortest array provided; additional values are ignored. For example,
+ * the result of zipping [1, 2] and [3, 4, 5] is [[1,3], [2, 4]].
+ *
+ * This is similar to the zip() function in Python. See {@link
+ * http://docs.python.org/library/functions.html#zip}
+ *
+ * @param {...!IArrayLike<?>} var_args Arrays to be combined.
+ * @return {!Array<!Array<?>>} A new array of arrays created from
+ * provided arrays.
+ */
+goog.array.zip = function(var_args) {
+ if (!arguments.length) {
+ return [];
+ }
+ var result = [];
+ var minLen = arguments[0].length;
+ for (var i = 1; i < arguments.length; i++) {
+ if (arguments[i].length < minLen) {
+ minLen = arguments[i].length;
+ }
+ }
+ for (var i = 0; i < minLen; i++) {
+ var value = [];
+ for (var j = 0; j < arguments.length; j++) {
+ value.push(arguments[j][i]);
+ }
+ result.push(value);
+ }
+ return result;
+};
+
+
+/**
+ * Shuffles the values in the specified array using the Fisher-Yates in-place
+ * shuffle (also known as the Knuth Shuffle). By default, calls Math.random()
+ * and so resets the state of that random number generator. Similarly, may reset
+ * the state of the any other specified random number generator.
+ *
+ * Runtime: O(n)
+ *
+ * @param {!Array<?>} arr The array to be shuffled.
+ * @param {function():number=} opt_randFn Optional random function to use for
+ * shuffling.
+ * Takes no arguments, and returns a random number on the interval [0, 1).
+ * Defaults to Math.random() using JavaScript's built-in Math library.
+ */
+goog.array.shuffle = function(arr, opt_randFn) {
+ var randFn = opt_randFn || Math.random;
+
+ for (var i = arr.length - 1; i > 0; i--) {
+ // Choose a random array index in [0, i] (inclusive with i).
+ var j = Math.floor(randFn() * (i + 1));
+
+ var tmp = arr[i];
+ arr[i] = arr[j];
+ arr[j] = tmp;
+ }
+};
+
+
+/**
+ * Returns a new array of elements from arr, based on the indexes of elements
+ * provided by index_arr. For example, the result of index copying
+ * ['a', 'b', 'c'] with index_arr [1,0,0,2] is ['b', 'a', 'a', 'c'].
+ *
+ * @param {!Array<T>} arr The array to get a indexed copy from.
+ * @param {!Array<number>} index_arr An array of indexes to get from arr.
+ * @return {!Array<T>} A new array of elements from arr in index_arr order.
+ * @template T
+ */
+goog.array.copyByIndex = function(arr, index_arr) {
+ var result = [];
+ goog.array.forEach(index_arr, function(index) { result.push(arr[index]); });
+ return result;
+};
+
+
+/**
+ * Maps each element of the input array into zero or more elements of the output
+ * array.
+ *
+ * @param {!IArrayLike<VALUE>|string} arr Array or array like object
+ * over which to iterate.
+ * @param {function(this:THIS, VALUE, number, ?): !Array<RESULT>} f The function
+ * to call for every element. This function takes 3 arguments (the element,
+ * the index and the array) and should return an array. The result will be
+ * used to extend a new array.
+ * @param {THIS=} opt_obj The object to be used as the value of 'this' within f.
+ * @return {!Array<RESULT>} a new array with the concatenation of all arrays
+ * returned from f.
+ * @template THIS, VALUE, RESULT
+ */
+goog.array.concatMap = function(arr, f, opt_obj) {
+ return goog.array.concat.apply([], goog.array.map(arr, f, opt_obj));
+};
diff --git a/chromium/third_party/ink/closure/asserts/asserts.js b/chromium/third_party/ink/closure/asserts/asserts.js
new file mode 100644
index 00000000000..89cad0a0422
--- /dev/null
+++ b/chromium/third_party/ink/closure/asserts/asserts.js
@@ -0,0 +1,391 @@
+// Copyright 2008 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Utilities to check the preconditions, postconditions and
+ * invariants runtime.
+ *
+ * Methods in this package should be given special treatment by the compiler
+ * for type-inference. For example, <code>goog.asserts.assert(foo)</code>
+ * will restrict <code>foo</code> to a truthy value.
+ *
+ * The compiler has an option to disable asserts. So code like:
+ * <code>
+ * var x = goog.asserts.assert(foo()); goog.asserts.assert(bar());
+ * </code>
+ * will be transformed into:
+ * <code>
+ * var x = foo();
+ * </code>
+ * The compiler will leave in foo() (because its return value is used),
+ * but it will remove bar() because it assumes it does not have side-effects.
+ *
+ * @author pallosp@google.com (Peter Pallos)
+ * @author agrieve@google.com (Andrew Grieve)
+ */
+
+goog.provide('goog.asserts');
+goog.provide('goog.asserts.AssertionError');
+
+goog.require('goog.debug.Error');
+goog.require('goog.dom.NodeType');
+goog.require('goog.string');
+
+
+/**
+ * @define {boolean} Whether to strip out asserts or to leave them in.
+ */
+goog.define('goog.asserts.ENABLE_ASSERTS', goog.DEBUG);
+
+
+
+/**
+ * Error object for failed assertions.
+ * @param {string} messagePattern The pattern that was used to form message.
+ * @param {!Array<*>} messageArgs The items to substitute into the pattern.
+ * @constructor
+ * @extends {goog.debug.Error}
+ * @final
+ */
+goog.asserts.AssertionError = function(messagePattern, messageArgs) {
+ messageArgs.unshift(messagePattern);
+ goog.debug.Error.call(this, goog.string.subs.apply(null, messageArgs));
+ // Remove the messagePattern afterwards to avoid permanently modifying the
+ // passed in array.
+ messageArgs.shift();
+
+ /**
+ * The message pattern used to format the error message. Error handlers can
+ * use this to uniquely identify the assertion.
+ * @type {string}
+ */
+ this.messagePattern = messagePattern;
+};
+goog.inherits(goog.asserts.AssertionError, goog.debug.Error);
+
+
+/** @override */
+goog.asserts.AssertionError.prototype.name = 'AssertionError';
+
+
+/**
+ * The default error handler.
+ * @param {!goog.asserts.AssertionError} e The exception to be handled.
+ */
+goog.asserts.DEFAULT_ERROR_HANDLER = function(e) {
+ throw e;
+};
+
+
+/**
+ * The handler responsible for throwing or logging assertion errors.
+ * @private {function(!goog.asserts.AssertionError)}
+ */
+goog.asserts.errorHandler_ = goog.asserts.DEFAULT_ERROR_HANDLER;
+
+
+/**
+ * Throws an exception with the given message and "Assertion failed" prefixed
+ * onto it.
+ * @param {string} defaultMessage The message to use if givenMessage is empty.
+ * @param {Array<*>} defaultArgs The substitution arguments for defaultMessage.
+ * @param {string|undefined} givenMessage Message supplied by the caller.
+ * @param {Array<*>} givenArgs The substitution arguments for givenMessage.
+ * @throws {goog.asserts.AssertionError} When the value is not a number.
+ * @private
+ */
+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;
+ }
+ // The '' + works around an Opera 10 bug in the unit tests. Without it,
+ // a stack trace is added to var message above. With this, a stack trace is
+ // not added until this line (it causes the extra garbage to be added after
+ // the assertion message instead of in the middle of it).
+ var e = new goog.asserts.AssertionError('' + message, args || []);
+ goog.asserts.errorHandler_(e);
+};
+
+
+/**
+ * Sets a custom error handler that can be used to customize the behavior of
+ * assertion failures, for example by turning all assertion failures into log
+ * messages.
+ * @param {function(!goog.asserts.AssertionError)} errorHandler
+ */
+goog.asserts.setErrorHandler = function(errorHandler) {
+ if (goog.asserts.ENABLE_ASSERTS) {
+ goog.asserts.errorHandler_ = errorHandler;
+ }
+};
+
+
+/**
+ * Checks if the condition evaluates to true if goog.asserts.ENABLE_ASSERTS is
+ * true.
+ * @template T
+ * @param {T} condition The condition to check.
+ * @param {string=} opt_message Error message in case of failure.
+ * @param {...*} var_args The items to substitute into the failure message.
+ * @return {T} The value of the condition.
+ * @throws {goog.asserts.AssertionError} When the condition evaluates to false.
+ */
+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;
+};
+
+
+/**
+ * Fails if goog.asserts.ENABLE_ASSERTS is true. This function is useful in case
+ * when we want to add a check in the unreachable area like switch-case
+ * statement:
+ *
+ * <pre>
+ * switch(type) {
+ * case FOO: doSomething(); break;
+ * case BAR: doSomethingElse(); break;
+ * default: goog.asserts.fail('Unrecognized type: ' + type);
+ * // We have only 2 types - "default:" section is unreachable code.
+ * }
+ * </pre>
+ *
+ * @param {string=} opt_message Error message in case of failure.
+ * @param {...*} var_args The items to substitute into the failure message.
+ * @throws {goog.asserts.AssertionError} Failure.
+ */
+goog.asserts.fail = function(opt_message, var_args) {
+ if (goog.asserts.ENABLE_ASSERTS) {
+ goog.asserts.errorHandler_(
+ new goog.asserts.AssertionError(
+ 'Failure' + (opt_message ? ': ' + opt_message : ''),
+ Array.prototype.slice.call(arguments, 1)));
+ }
+};
+
+
+/**
+ * Checks if the value is a number if goog.asserts.ENABLE_ASSERTS is true.
+ * @param {*} value The value to check.
+ * @param {string=} opt_message Error message in case of failure.
+ * @param {...*} var_args The items to substitute into the failure message.
+ * @return {number} The value, guaranteed to be a number when asserts enabled.
+ * @throws {goog.asserts.AssertionError} When the value is not a number.
+ */
+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 /** @type {number} */ (value);
+};
+
+
+/**
+ * Checks if the value is a string if goog.asserts.ENABLE_ASSERTS is true.
+ * @param {*} value The value to check.
+ * @param {string=} opt_message Error message in case of failure.
+ * @param {...*} var_args The items to substitute into the failure message.
+ * @return {string} The value, guaranteed to be a string when asserts enabled.
+ * @throws {goog.asserts.AssertionError} When the value is not a string.
+ */
+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 /** @type {string} */ (value);
+};
+
+
+/**
+ * Checks if the value is a function if goog.asserts.ENABLE_ASSERTS is true.
+ * @param {*} value The value to check.
+ * @param {string=} opt_message Error message in case of failure.
+ * @param {...*} var_args The items to substitute into the failure message.
+ * @return {!Function} The value, guaranteed to be a function when asserts
+ * enabled.
+ * @throws {goog.asserts.AssertionError} When the value is not a function.
+ */
+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 /** @type {!Function} */ (value);
+};
+
+
+/**
+ * Checks if the value is an Object if goog.asserts.ENABLE_ASSERTS is true.
+ * @param {*} value The value to check.
+ * @param {string=} opt_message Error message in case of failure.
+ * @param {...*} var_args The items to substitute into the failure message.
+ * @return {!Object} The value, guaranteed to be a non-null object.
+ * @throws {goog.asserts.AssertionError} When the value is not an object.
+ */
+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 /** @type {!Object} */ (value);
+};
+
+
+/**
+ * Checks if the value is an Array if goog.asserts.ENABLE_ASSERTS is true.
+ * @param {*} value The value to check.
+ * @param {string=} opt_message Error message in case of failure.
+ * @param {...*} var_args The items to substitute into the failure message.
+ * @return {!Array<?>} The value, guaranteed to be a non-null array.
+ * @throws {goog.asserts.AssertionError} When the value is not an array.
+ */
+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 /** @type {!Array<?>} */ (value);
+};
+
+
+/**
+ * Checks if the value is a boolean if goog.asserts.ENABLE_ASSERTS is true.
+ * @param {*} value The value to check.
+ * @param {string=} opt_message Error message in case of failure.
+ * @param {...*} var_args The items to substitute into the failure message.
+ * @return {boolean} The value, guaranteed to be a boolean when asserts are
+ * enabled.
+ * @throws {goog.asserts.AssertionError} When the value is not a boolean.
+ */
+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 /** @type {boolean} */ (value);
+};
+
+
+/**
+ * Checks if the value is a DOM Element if goog.asserts.ENABLE_ASSERTS is true.
+ * @param {*} value The value to check.
+ * @param {string=} opt_message Error message in case of failure.
+ * @param {...*} var_args The items to substitute into the failure message.
+ * @return {!Element} The value, likely to be a DOM Element when asserts are
+ * enabled.
+ * @throws {goog.asserts.AssertionError} When the value is not an Element.
+ */
+goog.asserts.assertElement = function(value, opt_message, var_args) {
+ if (goog.asserts.ENABLE_ASSERTS &&
+ (!goog.isObject(value) || value.nodeType != goog.dom.NodeType.ELEMENT)) {
+ goog.asserts.doAssertFailure_(
+ 'Expected Element but got %s: %s.', [goog.typeOf(value), value],
+ opt_message, Array.prototype.slice.call(arguments, 2));
+ }
+ return /** @type {!Element} */ (value);
+};
+
+
+/**
+ * Checks if the value is an instance of the user-defined type if
+ * goog.asserts.ENABLE_ASSERTS is true.
+ *
+ * The compiler may tighten the type returned by this function.
+ *
+ * @param {?} value The value to check.
+ * @param {function(new: T, ...)} type A user-defined constructor.
+ * @param {string=} opt_message Error message in case of failure.
+ * @param {...*} var_args The items to substitute into the failure message.
+ * @throws {goog.asserts.AssertionError} When the value is not an instance of
+ * type.
+ * @return {T}
+ * @template T
+ */
+goog.asserts.assertInstanceof = function(value, type, opt_message, var_args) {
+ if (goog.asserts.ENABLE_ASSERTS && !(value instanceof type)) {
+ goog.asserts.doAssertFailure_(
+ 'Expected instanceof %s but got %s.',
+ [goog.asserts.getType_(type), goog.asserts.getType_(value)],
+ opt_message, Array.prototype.slice.call(arguments, 3));
+ }
+ return value;
+};
+
+
+/**
+ * Checks whether the value is a finite number, if goog.asserts.ENABLE_ASSERTS
+ * is true.
+ *
+ * @param {*} value The value to check.
+ * @param {string=} opt_message Error message in case of failure.
+ * @param {...*} var_args The items to substitute into the failure message.
+ * @throws {goog.asserts.AssertionError} When the value is not a number, or is
+ * a non-finite number such as NaN, Infinity or -Infinity.
+ * @return {number} The value initially passed in.
+ */
+goog.asserts.assertFinite = function(value, opt_message, var_args) {
+ if (goog.asserts.ENABLE_ASSERTS &&
+ (typeof value != 'number' || !isFinite(value))) {
+ goog.asserts.doAssertFailure_(
+ 'Expected %s to be a finite number but it is not.', [value],
+ opt_message, Array.prototype.slice.call(arguments, 2));
+ }
+ return /** @type {number} */ (value);
+};
+
+/**
+ * Checks that no enumerable keys are present in Object.prototype. Such keys
+ * would break most code that use {@code for (var ... in ...)} loops.
+ */
+goog.asserts.assertObjectPrototypeIsIntact = function() {
+ for (var key in Object.prototype) {
+ goog.asserts.fail(key + ' should not be enumerable in Object.prototype.');
+ }
+};
+
+
+/**
+ * Returns the type of a value. If a constructor is passed, and a suitable
+ * string cannot be found, 'unknown type name' will be returned.
+ * @param {*} value A constructor, object, or primitive.
+ * @return {string} The best display name for the value, or 'unknown type name'.
+ * @private
+ */
+goog.asserts.getType_ = function(value) {
+ if (value instanceof Function) {
+ return value.displayName || value.name || 'unknown type name';
+ } else if (value instanceof Object) {
+ return value.constructor.displayName || value.constructor.name ||
+ Object.prototype.toString.call(value);
+ } else {
+ return value === null ? 'null' : typeof value;
+ }
+};
diff --git a/chromium/third_party/ink/closure/base.js b/chromium/third_party/ink/closure/base.js
new file mode 100644
index 00000000000..4d46cd73913
--- /dev/null
+++ b/chromium/third_party/ink/closure/base.js
@@ -0,0 +1,2962 @@
+// Copyright 2006 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Bootstrap for the Google JS Library (Closure).
+ *
+ * In uncompiled mode base.js will attempt to load Closure's deps file, unless
+ * the global <code>CLOSURE_NO_DEPS</code> is set to true. This allows projects
+ * to include their own deps file(s) from different locations.
+ *
+ * Avoid including base.js more than once. This is strictly discouraged and not
+ * supported. goog.require(...) won't work properly in that case.
+ *
+ * @provideGoog
+ */
+
+
+/**
+ * @define {boolean} Overridden to true by the compiler.
+ */
+var COMPILED = false;
+
+
+/**
+ * Base namespace for the Closure library. Checks to see goog is already
+ * defined in the current scope before assigning to prevent clobbering if
+ * base.js is loaded more than once.
+ *
+ * @const
+ */
+var goog = goog || {};
+
+
+/**
+ * Reference to the global context. In most cases this will be 'window'.
+ */
+goog.global = this;
+
+
+/**
+ * A hook for overriding the define values in uncompiled mode.
+ *
+ * In uncompiled mode, {@code CLOSURE_UNCOMPILED_DEFINES} may be defined before
+ * loading base.js. If a key is defined in {@code CLOSURE_UNCOMPILED_DEFINES},
+ * {@code goog.define} will use the value instead of the default value. This
+ * allows flags to be overwritten without compilation (this is normally
+ * accomplished with the compiler's "define" flag).
+ *
+ * Example:
+ * <pre>
+ * var CLOSURE_UNCOMPILED_DEFINES = {'goog.DEBUG': false};
+ * </pre>
+ *
+ * @type {Object<string, (string|number|boolean)>|undefined}
+ */
+goog.global.CLOSURE_UNCOMPILED_DEFINES;
+
+
+/**
+ * A hook for overriding the define values in uncompiled or compiled mode,
+ * like CLOSURE_UNCOMPILED_DEFINES but effective in compiled code. In
+ * uncompiled code CLOSURE_UNCOMPILED_DEFINES takes precedence.
+ *
+ * Also unlike CLOSURE_UNCOMPILED_DEFINES the values must be number, boolean or
+ * string literals or the compiler will emit an error.
+ *
+ * While any @define value may be set, only those set with goog.define will be
+ * effective for uncompiled code.
+ *
+ * Example:
+ * <pre>
+ * var CLOSURE_DEFINES = {'goog.DEBUG': false} ;
+ * </pre>
+ *
+ * @type {Object<string, (string|number|boolean)>|undefined}
+ */
+goog.global.CLOSURE_DEFINES;
+
+
+/**
+ * Returns true if the specified value is not undefined.
+ *
+ * @param {?} val Variable to test.
+ * @return {boolean} Whether variable is defined.
+ */
+goog.isDef = function(val) {
+ // void 0 always evaluates to undefined and hence we do not need to depend on
+ // the definition of the global variable named 'undefined'.
+ return val !== void 0;
+};
+
+/**
+ * Returns true if the specified value is a string.
+ * @param {?} val Variable to test.
+ * @return {boolean} Whether variable is a string.
+ */
+goog.isString = function(val) {
+ return typeof val == 'string';
+};
+
+
+/**
+ * Returns true if the specified value is a boolean.
+ * @param {?} val Variable to test.
+ * @return {boolean} Whether variable is boolean.
+ */
+goog.isBoolean = function(val) {
+ return typeof val == 'boolean';
+};
+
+
+/**
+ * Returns true if the specified value is a number.
+ * @param {?} val Variable to test.
+ * @return {boolean} Whether variable is a number.
+ */
+goog.isNumber = function(val) {
+ return typeof val == 'number';
+};
+
+
+/**
+ * Builds an object structure for the provided namespace path, ensuring that
+ * names that already exist are not overwritten. For example:
+ * "a.b.c" -> a = {};a.b={};a.b.c={};
+ * Used by goog.provide and goog.exportSymbol.
+ * @param {string} name name of the object that this file defines.
+ * @param {*=} opt_object the object to expose at the end of the path.
+ * @param {Object=} opt_objectToExportTo The object to add the path to; default
+ * is `goog.global`.
+ * @private
+ */
+goog.exportPath_ = function(name, opt_object, opt_objectToExportTo) {
+ var parts = name.split('.');
+ var cur = opt_objectToExportTo || goog.global;
+
+ // Internet Explorer exhibits strange behavior when throwing errors from
+ // methods externed in this manner. See the testExportSymbolExceptions in
+ // base_test.html for an example.
+ 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)) {
+ // last part and we have an object; use it
+ cur[part] = opt_object;
+ } else if (cur[part] && cur[part] !== Object.prototype[part]) {
+ cur = cur[part];
+ } else {
+ cur = cur[part] = {};
+ }
+ }
+};
+
+
+/**
+ * Defines a named value. In uncompiled mode, the value is retrieved from
+ * CLOSURE_DEFINES or CLOSURE_UNCOMPILED_DEFINES if the object is defined and
+ * has the property specified, and otherwise used the defined defaultValue.
+ * When compiled the default can be overridden using the compiler
+ * options or the value set in the CLOSURE_DEFINES object.
+ *
+ * @param {string} name The distinguished name to provide.
+ * @param {string|number|boolean} defaultValue
+ */
+goog.define = function(name, defaultValue) {
+ var value = defaultValue;
+ if (!COMPILED) {
+ if (goog.global.CLOSURE_UNCOMPILED_DEFINES &&
+ // Anti DOM-clobbering runtime check (b/37736576).
+ /** @type {?} */ (goog.global.CLOSURE_UNCOMPILED_DEFINES).nodeType ===
+ undefined &&
+ Object.prototype.hasOwnProperty.call(
+ goog.global.CLOSURE_UNCOMPILED_DEFINES, name)) {
+ value = goog.global.CLOSURE_UNCOMPILED_DEFINES[name];
+ } else if (
+ goog.global.CLOSURE_DEFINES &&
+ // Anti DOM-clobbering runtime check (b/37736576).
+ /** @type {?} */ (goog.global.CLOSURE_DEFINES).nodeType === undefined &&
+ Object.prototype.hasOwnProperty.call(
+ goog.global.CLOSURE_DEFINES, name)) {
+ value = goog.global.CLOSURE_DEFINES[name];
+ }
+ }
+ goog.exportPath_(name, value);
+};
+
+
+/**
+ * @define {boolean} DEBUG is provided as a convenience so that debugging code
+ * that should not be included in a production. It can be easily stripped
+ * by specifying --define goog.DEBUG=false to the Closure Compiler aka
+ * JSCompiler. For example, most toString() methods should be declared inside an
+ * "if (goog.DEBUG)" conditional because they are generally used for debugging
+ * purposes and it is difficult for the JSCompiler to statically determine
+ * whether they are used.
+ */
+goog.define('goog.DEBUG', true);
+
+
+/**
+ * @define {string} LOCALE defines the locale being used for compilation. It is
+ * used to select locale specific data to be compiled in js binary. BUILD rule
+ * can specify this value by "--define goog.LOCALE=<locale_name>" as a compiler
+ * option.
+ *
+ * Take into account that the locale code format is important. You should use
+ * the canonical Unicode format with hyphen as a delimiter. Language must be
+ * lowercase, Language Script - Capitalized, Region - UPPERCASE.
+ * There are few examples: pt-BR, en, en-US, sr-Latin-BO, zh-Hans-CN.
+ *
+ * See more info about locale codes here:
+ * http://www.unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers
+ *
+ * For language codes you should use values defined by ISO 693-1. See it here
+ * http://www.w3.org/WAI/ER/IG/ert/iso639.htm. There is only one exception from
+ * this rule: the Hebrew language. For legacy reasons the old code (iw) should
+ * be used instead of the new code (he).
+ *
+ * MOE:begin_intracomment_strip
+ * See http://g3doc/i18n/identifiers/g3doc/synonyms.
+ * MOE:end_intracomment_strip
+ */
+goog.define('goog.LOCALE', 'en'); // default to en
+
+
+/**
+ * @define {boolean} Whether this code is running on trusted sites.
+ *
+ * On untrusted sites, several native functions can be defined or overridden by
+ * external libraries like Prototype, Datejs, and JQuery and setting this flag
+ * to false forces closure to use its own implementations when possible.
+ *
+ * If your JavaScript can be loaded by a third party site and you are wary about
+ * relying on non-standard implementations, specify
+ * "--define goog.TRUSTED_SITE=false" to the compiler.
+ */
+goog.define('goog.TRUSTED_SITE', true);
+
+
+/**
+ * @define {boolean} Whether a project is expected to be running in strict mode.
+ *
+ * This define can be used to trigger alternate implementations compatible with
+ * running in EcmaScript Strict mode or warn about unavailable functionality.
+ * @see https://goo.gl/PudQ4y
+ *
+ */
+goog.define('goog.STRICT_MODE_COMPATIBLE', false);
+
+
+/**
+ * @define {boolean} Whether code that calls {@link goog.setTestOnly} should
+ * be disallowed in the compilation unit.
+ */
+goog.define('goog.DISALLOW_TEST_ONLY_CODE', COMPILED && !goog.DEBUG);
+
+
+/**
+ * @define {boolean} Whether to use a Chrome app CSP-compliant method for
+ * loading scripts via goog.require. @see appendScriptSrcNode_.
+ */
+goog.define('goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING', false);
+
+
+/**
+ * Defines a namespace in Closure.
+ *
+ * A namespace may only be defined once in a codebase. It may be defined using
+ * goog.provide() or goog.module().
+ *
+ * The presence of one or more goog.provide() calls in a file indicates
+ * that the file defines the given objects/namespaces.
+ * Provided symbols must not be null or undefined.
+ *
+ * In addition, goog.provide() creates the object stubs for a namespace
+ * (for example, goog.provide("goog.foo.bar") will create the object
+ * goog.foo.bar if it does not already exist).
+ *
+ * Build tools also scan for provide/require/module statements
+ * to discern dependencies, build dependency files (see deps.js), etc.
+ *
+ * @see goog.require
+ * @see goog.module
+ * @param {string} name Namespace provided by this file in the form
+ * "goog.package.part".
+ */
+goog.provide = function(name) {
+ if (goog.isInModuleLoader_()) {
+ throw new Error('goog.provide can not be used within a goog.module.');
+ }
+ if (!COMPILED) {
+ // Ensure that the same namespace isn't provided twice.
+ // A goog.module/goog.provide maps a goog.require to a specific file
+ if (goog.isProvided_(name)) {
+ throw new Error('Namespace "' + name + '" already declared.');
+ }
+ }
+
+ goog.constructNamespace_(name);
+};
+
+
+/**
+ * @param {string} name Namespace provided by this file in the form
+ * "goog.package.part".
+ * @param {Object=} opt_obj The object to embed in the namespace.
+ * @private
+ */
+goog.constructNamespace_ = function(name, opt_obj) {
+ if (!COMPILED) {
+ delete goog.implicitNamespaces_[name];
+
+ var namespace = name;
+ while ((namespace = namespace.substring(0, namespace.lastIndexOf('.')))) {
+ if (goog.getObjectByName(namespace)) {
+ break;
+ }
+ goog.implicitNamespaces_[namespace] = true;
+ }
+ }
+
+ goog.exportPath_(name, opt_obj);
+};
+
+
+/**
+ * Module identifier validation regexp.
+ * Note: This is a conservative check, it is very possible to be more lenient,
+ * the primary exclusion here is "/" and "\" and a leading ".", these
+ * restrictions are intended to leave the door open for using goog.require
+ * with relative file paths rather than module identifiers.
+ * @private
+ */
+goog.VALID_MODULE_RE_ = /^[a-zA-Z_$][a-zA-Z0-9._$]*$/;
+
+
+/**
+ * Defines a module in Closure.
+ *
+ * Marks that this file must be loaded as a module and claims the namespace.
+ *
+ * A namespace may only be defined once in a codebase. It may be defined using
+ * goog.provide() or goog.module().
+ *
+ * goog.module() has three requirements:
+ * - goog.module may not be used in the same file as goog.provide.
+ * - goog.module must be the first statement in the file.
+ * - only one goog.module is allowed per file.
+ *
+ * When a goog.module annotated file is loaded, it is enclosed in
+ * a strict function closure. This means that:
+ * - any variables declared in a goog.module file are private to the file
+ * (not global), though the compiler is expected to inline the module.
+ * - The code must obey all the rules of "strict" JavaScript.
+ * - the file will be marked as "use strict"
+ *
+ * NOTE: unlike goog.provide, goog.module does not declare any symbols by
+ * itself. If declared symbols are desired, use
+ * goog.module.declareLegacyNamespace().
+ *
+ * MOE:begin_intracomment_strip
+ * See the goog.module announcement at http://go/goog.module-announce
+ * MOE:end_intracomment_strip
+ *
+ * See the public goog.module proposal: http://goo.gl/Va1hin
+ *
+ * @param {string} name Namespace provided by this file in the form
+ * "goog.package.part", is expected but not required.
+ * @return {void}
+ */
+goog.module = function(name) {
+ if (!goog.isString(name) || !name ||
+ name.search(goog.VALID_MODULE_RE_) == -1) {
+ throw new Error('Invalid module identifier');
+ }
+ if (!goog.isInModuleLoader_()) {
+ throw new Error(
+ 'Module ' + name + ' has been loaded incorrectly. Note, ' +
+ 'modules cannot be loaded as normal scripts. They require some kind of ' +
+ 'pre-processing step. You\'re likely trying to load a module via a ' +
+ 'script tag or as a part of a concatenated bundle without rewriting the ' +
+ 'module. For more info see: ' +
+ 'https://github.com/google/closure-library/wiki/goog.module:-an-ES6-module-like-alternative-to-goog.provide.');
+ }
+ if (goog.moduleLoaderState_.moduleName) {
+ throw new Error('goog.module may only be called once per module.');
+ }
+
+ // Store the module name for the loader.
+ goog.moduleLoaderState_.moduleName = name;
+ if (!COMPILED) {
+ // Ensure that the same namespace isn't provided twice.
+ // A goog.module/goog.provide maps a goog.require to a specific file
+ if (goog.isProvided_(name)) {
+ throw new Error('Namespace "' + name + '" already declared.');
+ }
+ delete goog.implicitNamespaces_[name];
+ }
+};
+
+
+/**
+ * @param {string} name The module identifier.
+ * @return {?} The module exports for an already loaded module or null.
+ *
+ * Note: This is not an alternative to goog.require, it does not
+ * indicate a hard dependency, instead it is used to indicate
+ * an optional dependency or to access the exports of a module
+ * that has already been loaded.
+ * @suppress {missingProvide}
+ */
+goog.module.get = function(name) {
+ return goog.module.getInternal_(name);
+};
+
+
+/**
+ * @param {string} name The module identifier.
+ * @return {?} The module exports for an already loaded module or null.
+ * @private
+ */
+goog.module.getInternal_ = function(name) {
+ if (!COMPILED) {
+ if (name in goog.loadedModules_) {
+ return goog.loadedModules_[name];
+ } else if (!goog.implicitNamespaces_[name]) {
+ var ns = goog.getObjectByName(name);
+ return ns != null ? ns : null;
+ }
+ }
+ return null;
+};
+
+
+/**
+ * @private {?{moduleName: (string|undefined), declareLegacyNamespace:boolean}}
+ */
+goog.moduleLoaderState_ = null;
+
+
+/**
+ * @private
+ * @return {boolean} Whether a goog.module is currently being initialized.
+ */
+goog.isInModuleLoader_ = function() {
+ return goog.moduleLoaderState_ != null;
+};
+
+
+/**
+ * Provide the module's exports as a globally accessible object under the
+ * module's declared name. This is intended to ease migration to goog.module
+ * for files that have existing usages.
+ * @suppress {missingProvide}
+ */
+goog.module.declareLegacyNamespace = function() {
+ if (!COMPILED && !goog.isInModuleLoader_()) {
+ throw new Error(
+ 'goog.module.declareLegacyNamespace must be called from ' +
+ 'within a goog.module');
+ }
+ if (!COMPILED && !goog.moduleLoaderState_.moduleName) {
+ throw new Error(
+ 'goog.module must be called prior to ' +
+ 'goog.module.declareLegacyNamespace.');
+ }
+ goog.moduleLoaderState_.declareLegacyNamespace = true;
+};
+
+
+/**
+ * Marks that the current file should only be used for testing, and never for
+ * live code in production.
+ *
+ * In the case of unit tests, the message may optionally be an exact namespace
+ * for the test (e.g. 'goog.stringTest'). The linter will then ignore the extra
+ * provide (if not explicitly defined in the code).
+ *
+ * @param {string=} opt_message Optional message to add to the error that's
+ * raised when used in production code.
+ */
+goog.setTestOnly = function(opt_message) {
+ if (goog.DISALLOW_TEST_ONLY_CODE) {
+ opt_message = opt_message || '';
+ throw new Error(
+ 'Importing test-only code into non-debug environment' +
+ (opt_message ? ': ' + opt_message : '.'));
+ }
+};
+
+
+/**
+ * Forward declares a symbol. This is an indication to the compiler that the
+ * symbol may be used in the source yet is not required and may not be provided
+ * in compilation.
+ *
+ * The most common usage of forward declaration is code that takes a type as a
+ * function parameter but does not need to require it. By forward declaring
+ * instead of requiring, no hard dependency is made, and (if not required
+ * elsewhere) the namespace may never be required and thus, not be pulled
+ * into the JavaScript binary. If it is required elsewhere, it will be type
+ * checked as normal.
+ *
+ * Before using goog.forwardDeclare, please read the documentation at
+ * https://github.com/google/closure-compiler/wiki/Bad-Type-Annotation to
+ * understand the options and tradeoffs when working with forward declarations.
+ *
+ * @param {string} name The namespace to forward declare in the form of
+ * "goog.package.part".
+ */
+goog.forwardDeclare = function(name) {};
+
+
+/**
+ * Forward declare type information. Used to assign types to goog.global
+ * referenced object that would otherwise result in unknown type references
+ * and thus block property disambiguation.
+ */
+goog.forwardDeclare('Document');
+goog.forwardDeclare('HTMLScriptElement');
+goog.forwardDeclare('XMLHttpRequest');
+
+
+if (!COMPILED) {
+ /**
+ * Check if the given name has been goog.provided. This will return false for
+ * names that are available only as implicit namespaces.
+ * @param {string} name name of the object to look for.
+ * @return {boolean} Whether the name has been provided.
+ * @private
+ */
+ goog.isProvided_ = function(name) {
+ return (name in goog.loadedModules_) ||
+ (!goog.implicitNamespaces_[name] &&
+ goog.isDefAndNotNull(goog.getObjectByName(name)));
+ };
+
+ /**
+ * Namespaces implicitly defined by goog.provide. For example,
+ * goog.provide('goog.events.Event') implicitly declares that 'goog' and
+ * 'goog.events' must be namespaces.
+ *
+ * @type {!Object<string, (boolean|undefined)>}
+ * @private
+ */
+ goog.implicitNamespaces_ = {'goog.module': true};
+
+ // NOTE: We add goog.module as an implicit namespace as goog.module is defined
+ // here and because the existing module package has not been moved yet out of
+ // the goog.module namespace. This satisifies both the debug loader and
+ // ahead-of-time dependency management.
+}
+
+
+/**
+ * Returns an object based on its fully qualified external name. The object
+ * is not found if null or undefined. If you are using a compilation pass that
+ * renames property names beware that using this function will not find renamed
+ * properties.
+ *
+ * @param {string} name The fully qualified name.
+ * @param {Object=} opt_obj The object within which to look; default is
+ * |goog.global|.
+ * @return {?} The value (object or primitive) or, if not found, null.
+ */
+goog.getObjectByName = function(name, opt_obj) {
+ var parts = name.split('.');
+ var cur = opt_obj || goog.global;
+ for (var i = 0; i < parts.length; i++) {
+ cur = cur[parts[i]];
+ if (!goog.isDefAndNotNull(cur)) {
+ return null;
+ }
+ }
+ return cur;
+};
+
+
+/**
+ * Globalizes a whole namespace, such as goog or goog.lang.
+ *
+ * @param {!Object} obj The namespace to globalize.
+ * @param {Object=} opt_global The object to add the properties to.
+ * @deprecated Properties may be explicitly exported to the global scope, but
+ * this should no longer be done in bulk.
+ */
+goog.globalize = function(obj, opt_global) {
+ var global = opt_global || goog.global;
+ for (var x in obj) {
+ global[x] = obj[x];
+ }
+};
+
+
+/**
+ * Adds a dependency from a file to the files it requires.
+ * @param {string} relPath The path to the js file.
+ * @param {!Array<string>} provides An array of strings with
+ * the names of the objects this file provides.
+ * @param {!Array<string>} requires An array of strings with
+ * the names of the objects this file requires.
+ * @param {boolean|!Object<string>=} opt_loadFlags Parameters indicating
+ * how the file must be loaded. The boolean 'true' is equivalent
+ * to {'module': 'goog'} for backwards-compatibility. Valid properties
+ * and values include {'module': 'goog'} and {'lang': 'es6'}.
+ */
+goog.addDependency = function(relPath, provides, requires, opt_loadFlags) {
+ if (goog.DEPENDENCIES_ENABLED) {
+ var provide, require;
+ var path = relPath.replace(/\\/g, '/');
+ var deps = goog.dependencies_;
+ if (!opt_loadFlags || typeof opt_loadFlags === 'boolean') {
+ opt_loadFlags = opt_loadFlags ? {'module': 'goog'} : {};
+ }
+ for (var i = 0; provide = provides[i]; i++) {
+ deps.nameToPath[provide] = path;
+ deps.loadFlags[path] = opt_loadFlags;
+ }
+ for (var j = 0; require = requires[j]; j++) {
+ if (!(path in deps.requires)) {
+ deps.requires[path] = {};
+ }
+ deps.requires[path][require] = true;
+ }
+ }
+};
+
+
+// MOE:begin_strip
+/**
+ * Whether goog.require should throw an exception if it fails.
+ * @type {boolean}
+ */
+goog.useStrictRequires = false;
+
+
+// MOE:end_strip
+
+
+// NOTE(nnaze): The debug DOM loader was included in base.js as an original way
+// to do "debug-mode" development. The dependency system can sometimes be
+// confusing, as can the debug DOM loader's asynchronous nature.
+//
+// With the DOM loader, a call to goog.require() is not blocking -- the script
+// will not load until some point after the current script. If a namespace is
+// needed at runtime, it needs to be defined in a previous script, or loaded via
+// require() with its registered dependencies.
+//
+// User-defined namespaces may need their own deps file. For a reference on
+// creating a deps file, see:
+// MOE:begin_strip
+// Internally: http://go/deps-files and http://go/be#js_deps
+// MOE:end_strip
+// Externally: https://developers.google.com/closure/library/docs/depswriter
+//
+// Because of legacy clients, the DOM loader can't be easily removed from
+// base.js. Work was done to make it disableable or replaceable for
+// different environments (DOM-less JavaScript interpreters like Rhino or V8,
+// for example). See bootstrap/ for more information.
+
+
+/**
+ * @define {boolean} Whether to enable the debug loader.
+ *
+ * If enabled, a call to goog.require() will attempt to load the namespace by
+ * appending a script tag to the DOM (if the namespace has been registered).
+ *
+ * If disabled, goog.require() will simply assert that the namespace has been
+ * provided (and depend on the fact that some outside tool correctly ordered
+ * the script).
+ */
+goog.define('goog.ENABLE_DEBUG_LOADER', true);
+
+
+/**
+ * @param {string} msg
+ * @private
+ */
+goog.logToConsole_ = function(msg) {
+ if (goog.global.console) {
+ goog.global.console['error'](msg);
+ }
+};
+
+
+/**
+ * Implements a system for the dynamic resolution of dependencies that works in
+ * parallel with the BUILD system. Note that all calls to goog.require will be
+ * stripped by the compiler.
+ * @see goog.provide
+ * @param {string} name Namespace to include (as was given in goog.provide()) in
+ * the form "goog.package.part".
+ * @return {?} If called within a goog.module file, the associated namespace or
+ * module otherwise null.
+ */
+goog.require = function(name) {
+ // If the object already exists we do not need to do anything.
+ if (!COMPILED) {
+ if (goog.ENABLE_DEBUG_LOADER && goog.IS_OLD_IE_) {
+ goog.maybeProcessDeferredDep_(name);
+ }
+
+ if (goog.isProvided_(name)) {
+ if (goog.isInModuleLoader_()) {
+ return goog.module.getInternal_(name);
+ }
+ } else if (goog.ENABLE_DEBUG_LOADER) {
+ var path = goog.getPathFromDeps_(name);
+ if (path) {
+ goog.writeScripts_(path);
+ } else {
+ var errorMessage = 'goog.require could not find: ' + name;
+ goog.logToConsole_(errorMessage);
+
+ // MOE:begin_strip
+
+ // NOTE(nicksantos): We could always throw an error, but this would
+ // break legacy users that depended on this failing silently. Instead,
+ // the compiler should warn us when there are invalid goog.require
+ // calls. For now, we simply give clients a way to turn strict mode on.
+ if (goog.useStrictRequires) {
+ throw new Error(errorMessage);
+ }
+
+ // In external Closure, always error.
+ // MOE:end_strip_and_replace throw new Error(errorMessage);
+ }
+ }
+
+ return null;
+ }
+};
+
+
+/**
+ * Path for included scripts.
+ * @type {string}
+ */
+goog.basePath = '';
+
+
+/**
+ * A hook for overriding the base path.
+ * @type {string|undefined}
+ */
+goog.global.CLOSURE_BASE_PATH;
+
+
+/**
+ * Whether to attempt to load Closure's deps file. By default, when uncompiled,
+ * deps files will attempt to be loaded.
+ * @type {boolean|undefined}
+ */
+goog.global.CLOSURE_NO_DEPS;
+
+
+/**
+ * A function to import a single script. This is meant to be overridden when
+ * Closure is being run in non-HTML contexts, such as web workers. It's defined
+ * in the global scope so that it can be set before base.js is loaded, which
+ * allows deps.js to be imported properly.
+ *
+ * The function is passed the script source, which is a relative URI. It should
+ * return true if the script was imported, false otherwise.
+ * @type {(function(string): boolean)|undefined}
+ */
+goog.global.CLOSURE_IMPORT_SCRIPT;
+
+
+/**
+ * Null function used for default values of callbacks, etc.
+ * @return {void} Nothing.
+ */
+goog.nullFunction = function() {};
+
+
+/**
+ * When defining a class Foo with an abstract method bar(), you can do:
+ * Foo.prototype.bar = goog.abstractMethod
+ *
+ * Now if a subclass of Foo fails to override bar(), an error will be thrown
+ * when bar() is invoked.
+ *
+ * @type {!Function}
+ * @throws {Error} when invoked to indicate the method should be overridden.
+ */
+goog.abstractMethod = function() {
+ throw new Error('unimplemented abstract method');
+};
+
+
+/**
+ * Adds a {@code getInstance} static method that always returns the same
+ * instance object.
+ * @param {!Function} ctor The constructor for the class to add the static
+ * method to.
+ */
+goog.addSingletonGetter = function(ctor) {
+ // instance_ is immediately set to prevent issues with sealed constructors
+ // such as are encountered when a constructor is returned as the export object
+ // of a goog.module in unoptimized code.
+ ctor.instance_ = undefined;
+ ctor.getInstance = function() {
+ if (ctor.instance_) {
+ return ctor.instance_;
+ }
+ if (goog.DEBUG) {
+ // NOTE: JSCompiler can't optimize away Array#push.
+ goog.instantiatedSingletons_[goog.instantiatedSingletons_.length] = ctor;
+ }
+ return ctor.instance_ = new ctor;
+ };
+};
+
+
+/**
+ * All singleton classes that have been instantiated, for testing. Don't read
+ * it directly, use the {@code goog.testing.singleton} module. The compiler
+ * removes this variable if unused.
+ * @type {!Array<!Function>}
+ * @private
+ */
+goog.instantiatedSingletons_ = [];
+
+
+/**
+ * @define {boolean} Whether to load goog.modules using {@code eval} when using
+ * the debug loader. This provides a better debugging experience as the
+ * source is unmodified and can be edited using Chrome Workspaces or similar.
+ * However in some environments the use of {@code eval} is banned
+ * so we provide an alternative.
+ */
+goog.define('goog.LOAD_MODULE_USING_EVAL', true);
+
+
+/**
+ * @define {boolean} Whether the exports of goog.modules should be sealed when
+ * possible.
+ */
+goog.define('goog.SEAL_MODULE_EXPORTS', goog.DEBUG);
+
+
+/**
+ * The registry of initialized modules:
+ * the module identifier to module exports map.
+ * @private @const {!Object<string, ?>}
+ */
+goog.loadedModules_ = {};
+
+
+/**
+ * True if goog.dependencies_ is available.
+ * @const {boolean}
+ */
+goog.DEPENDENCIES_ENABLED = !COMPILED && goog.ENABLE_DEBUG_LOADER;
+
+
+/**
+ * @define {string} How to decide whether to transpile. Valid values
+ * are 'always', 'never', and 'detect'. The default ('detect') is to
+ * use feature detection to determine which language levels need
+ * transpilation.
+ */
+// NOTE(sdh): we could expand this to accept a language level to bypass
+// detection: e.g. goog.TRANSPILE == 'es5' would transpile ES6 files but
+// would leave ES3 and ES5 files alone.
+goog.define('goog.TRANSPILE', 'detect');
+
+
+/**
+ * @define {string} Path to the transpiler. Executing the script at this
+ * path (relative to base.js) should define a function $jscomp.transpile.
+ */
+goog.define('goog.TRANSPILER', 'transpile.js');
+
+
+if (goog.DEPENDENCIES_ENABLED) {
+ /**
+ * This object is used to keep track of dependencies and other data that is
+ * used for loading scripts.
+ * @private
+ * @type {{
+ * loadFlags: !Object<string, !Object<string, string>>,
+ * nameToPath: !Object<string, string>,
+ * requires: !Object<string, !Object<string, boolean>>,
+ * visited: !Object<string, boolean>,
+ * written: !Object<string, boolean>,
+ * deferred: !Object<string, string>
+ * }}
+ */
+ goog.dependencies_ = {
+ loadFlags: {}, // 1 to 1
+
+ nameToPath: {}, // 1 to 1
+
+ requires: {}, // 1 to many
+
+ // Used when resolving dependencies to prevent us from visiting file twice.
+ visited: {},
+
+ written: {}, // Used to keep track of script files we have written.
+
+ deferred: {} // Used to track deferred module evaluations in old IEs
+ };
+
+
+ /**
+ * Tries to detect whether is in the context of an HTML document.
+ * @return {boolean} True if it looks like HTML document.
+ * @private
+ */
+ goog.inHtmlDocument_ = function() {
+ /** @type {Document} */
+ var doc = goog.global.document;
+ return doc != null && 'write' in doc; // XULDocument misses write.
+ };
+
+
+ /**
+ * Tries to detect the base path of base.js script that bootstraps Closure.
+ * @private
+ */
+ goog.findBasePath_ = function() {
+ if (goog.isDef(goog.global.CLOSURE_BASE_PATH) &&
+ // Anti DOM-clobbering runtime check (b/37736576).
+ goog.isString(goog.global.CLOSURE_BASE_PATH)) {
+ goog.basePath = goog.global.CLOSURE_BASE_PATH;
+ return;
+ } else if (!goog.inHtmlDocument_()) {
+ return;
+ }
+ /** @type {Document} */
+ var doc = goog.global.document;
+ // If we have a currentScript available, use it exclusively.
+ var currentScript = doc.currentScript;
+ if (currentScript) {
+ var scripts = [currentScript];
+ } else {
+ var scripts = doc.getElementsByTagName('SCRIPT');
+ }
+ // Search backwards since the current script is in almost all cases the one
+ // that has base.js.
+ for (var i = scripts.length - 1; i >= 0; --i) {
+ var script = /** @type {!HTMLScriptElement} */ (scripts[i]);
+ var src = script.src;
+ var qmark = src.lastIndexOf('?');
+ var l = qmark == -1 ? src.length : qmark;
+ if (src.substr(l - 7, 7) == 'base.js') {
+ goog.basePath = src.substr(0, l - 7);
+ return;
+ }
+ }
+ };
+
+
+ /**
+ * Imports a script if, and only if, that script hasn't already been imported.
+ * (Must be called at execution time)
+ * @param {string} src Script source.
+ * @param {string=} opt_sourceText The optionally source text to evaluate
+ * @private
+ */
+ goog.importScript_ = function(src, opt_sourceText) {
+ var importScript =
+ goog.global.CLOSURE_IMPORT_SCRIPT || goog.writeScriptTag_;
+ if (importScript(src, opt_sourceText)) {
+ goog.dependencies_.written[src] = true;
+ }
+ };
+
+
+ /**
+ * Whether the browser is IE9 or earlier, which needs special handling
+ * for deferred modules.
+ * @const @private {boolean}
+ */
+ goog.IS_OLD_IE_ =
+ !!(!goog.global.atob && goog.global.document && goog.global.document.all);
+
+
+ /**
+ * Whether IE9 or earlier is waiting on a dependency. This ensures that
+ * deferred modules that have no non-deferred dependencies actually get
+ * loaded, since if we defer them and then never pull in a non-deferred
+ * script, then `goog.loadQueuedModules_` will never be called. Instead,
+ * if not waiting on anything we simply don't defer in the first place.
+ * @private {boolean}
+ */
+ goog.oldIeWaiting_ = false;
+
+
+ /**
+ * Given a URL initiate retrieval and execution of a script that needs
+ * pre-processing.
+ * @param {string} src Script source URL.
+ * @param {boolean} isModule Whether this is a goog.module.
+ * @param {boolean} needsTranspile Whether this source needs transpilation.
+ * @private
+ */
+ goog.importProcessedScript_ = function(src, isModule, needsTranspile) {
+ // In an attempt to keep browsers from timing out loading scripts using
+ // synchronous XHRs, put each load in its own script block.
+ var bootstrap = 'goog.retrieveAndExec_("' + src + '", ' + isModule + ', ' +
+ needsTranspile + ');';
+
+ goog.importScript_('', bootstrap);
+ };
+
+
+ /** @private {!Array<string>} */
+ goog.queuedModules_ = [];
+
+
+ /**
+ * Return an appropriate module text. Suitable to insert into
+ * a script tag (that is unescaped).
+ * @param {string} srcUrl
+ * @param {string} scriptText
+ * @return {string}
+ * @private
+ */
+ goog.wrapModule_ = function(srcUrl, scriptText) {
+ if (!goog.LOAD_MODULE_USING_EVAL || !goog.isDef(goog.global.JSON)) {
+ return '' +
+ 'goog.loadModule(function(exports) {' +
+ '"use strict";' + scriptText +
+ '\n' + // terminate any trailing single line comment.
+ ';return exports' +
+ '});' +
+ '\n//# sourceURL=' + srcUrl + '\n';
+ } else {
+ return '' +
+ 'goog.loadModule(' +
+ goog.global.JSON.stringify(
+ scriptText + '\n//# sourceURL=' + srcUrl + '\n') +
+ ');';
+ }
+ };
+
+ // On IE9 and earlier, it is necessary to handle
+ // deferred module loads. In later browsers, the
+ // code to be evaluated is simply inserted as a script
+ // block in the correct order. To eval deferred
+ // code at the right time, we piggy back on goog.require to call
+ // goog.maybeProcessDeferredDep_.
+ //
+ // The goog.requires are used both to bootstrap
+ // the loading process (when no deps are available) and
+ // declare that they should be available.
+ //
+ // Here we eval the sources, if all the deps are available
+ // either already eval'd or goog.require'd. This will
+ // be the case when all the dependencies have already
+ // been loaded, and the dependent module is loaded.
+ //
+ // But this alone isn't sufficient because it is also
+ // necessary to handle the case where there is no root
+ // that is not deferred. For that there we register for an event
+ // and trigger goog.loadQueuedModules_ handle any remaining deferred
+ // evaluations.
+
+ /**
+ * Handle any remaining deferred goog.module evals.
+ * @private
+ */
+ goog.loadQueuedModules_ = function() {
+ var count = goog.queuedModules_.length;
+ if (count > 0) {
+ var queue = goog.queuedModules_;
+ goog.queuedModules_ = [];
+ for (var i = 0; i < count; i++) {
+ var path = queue[i];
+ goog.maybeProcessDeferredPath_(path);
+ }
+ }
+ goog.oldIeWaiting_ = false;
+ };
+
+
+ /**
+ * Eval the named module if its dependencies are
+ * available.
+ * @param {string} name The module to load.
+ * @private
+ */
+ goog.maybeProcessDeferredDep_ = function(name) {
+ if (goog.isDeferredModule_(name) && goog.allDepsAreAvailable_(name)) {
+ var path = goog.getPathFromDeps_(name);
+ goog.maybeProcessDeferredPath_(goog.basePath + path);
+ }
+ };
+
+ /**
+ * @param {string} name The module to check.
+ * @return {boolean} Whether the name represents a
+ * module whose evaluation has been deferred.
+ * @private
+ */
+ goog.isDeferredModule_ = function(name) {
+ var path = goog.getPathFromDeps_(name);
+ var loadFlags = path && goog.dependencies_.loadFlags[path] || {};
+ var languageLevel = loadFlags['lang'] || 'es3';
+ if (path && (loadFlags['module'] == 'goog' ||
+ goog.needsTranspile_(languageLevel))) {
+ var abspath = goog.basePath + path;
+ return (abspath) in goog.dependencies_.deferred;
+ }
+ return false;
+ };
+
+ /**
+ * @param {string} name The module to check.
+ * @return {boolean} Whether the name represents a
+ * module whose declared dependencies have all been loaded
+ * (eval'd or a deferred module load)
+ * @private
+ */
+ goog.allDepsAreAvailable_ = function(name) {
+ var path = goog.getPathFromDeps_(name);
+ if (path && (path in goog.dependencies_.requires)) {
+ for (var requireName in goog.dependencies_.requires[path]) {
+ if (!goog.isProvided_(requireName) &&
+ !goog.isDeferredModule_(requireName)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ };
+
+
+ /**
+ * @param {string} abspath
+ * @private
+ */
+ goog.maybeProcessDeferredPath_ = function(abspath) {
+ if (abspath in goog.dependencies_.deferred) {
+ var src = goog.dependencies_.deferred[abspath];
+ delete goog.dependencies_.deferred[abspath];
+ goog.globalEval(src);
+ }
+ };
+
+
+ /**
+ * Load a goog.module from the provided URL. This is not a general purpose
+ * code loader and does not support late loading code, that is it should only
+ * be used during page load. This method exists to support unit tests and
+ * "debug" loaders that would otherwise have inserted script tags. Under the
+ * hood this needs to use a synchronous XHR and is not recommeneded for
+ * production code.
+ *
+ * The module's goog.requires must have already been satisified; an exception
+ * will be thrown if this is not the case. This assumption is that no
+ * "deps.js" file exists, so there is no way to discover and locate the
+ * module-to-be-loaded's dependencies and no attempt is made to do so.
+ *
+ * There should only be one attempt to load a module. If
+ * "goog.loadModuleFromUrl" is called for an already loaded module, an
+ * exception will be throw.
+ *
+ * @param {string} url The URL from which to attempt to load the goog.module.
+ */
+ goog.loadModuleFromUrl = function(url) {
+ // Because this executes synchronously, we don't need to do any additional
+ // bookkeeping. When "goog.loadModule" the namespace will be marked as
+ // having been provided which is sufficient.
+ goog.retrieveAndExec_(url, true, false);
+ };
+
+
+ /**
+ * Writes a new script pointing to {@code src} directly into the DOM.
+ *
+ * NOTE: This method is not CSP-compliant. @see goog.appendScriptSrcNode_ for
+ * the fallback mechanism.
+ *
+ * @param {string} src The script URL.
+ * @private
+ */
+ goog.writeScriptSrcNode_ = function(src) {
+ goog.global.document.write(
+ '<script type="text/javascript" src="' + src + '"></' +
+ 'script>');
+ };
+
+
+ /**
+ * Appends a new script node to the DOM using a CSP-compliant mechanism. This
+ * method exists as a fallback for document.write (which is not allowed in a
+ * strict CSP context, e.g., Chrome apps).
+ *
+ * NOTE: This method is not analogous to using document.write to insert a
+ * <script> tag; specifically, the user agent will execute a script added by
+ * document.write immediately after the current script block finishes
+ * executing, whereas the DOM-appended script node will not be executed until
+ * the entire document is parsed and executed. That is to say, this script is
+ * added to the end of the script execution queue.
+ *
+ * The page must not attempt to call goog.required entities until after the
+ * document has loaded, e.g., in or after the window.onload callback.
+ *
+ * @param {string} src The script URL.
+ * @private
+ */
+ goog.appendScriptSrcNode_ = function(src) {
+ /** @type {Document} */
+ var doc = goog.global.document;
+ var scriptEl =
+ /** @type {HTMLScriptElement} */ (doc.createElement('script'));
+ scriptEl.type = 'text/javascript';
+ scriptEl.src = src;
+ scriptEl.defer = false;
+ scriptEl.async = false;
+ doc.head.appendChild(scriptEl);
+ };
+
+
+ /**
+ * The default implementation of the import function. Writes a script tag to
+ * import the script.
+ *
+ * @param {string} src The script url.
+ * @param {string=} opt_sourceText The optionally source text to evaluate
+ * @return {boolean} True if the script was imported, false otherwise.
+ * @private
+ */
+ goog.writeScriptTag_ = function(src, opt_sourceText) {
+ if (goog.inHtmlDocument_()) {
+ /** @type {!HTMLDocument} */
+ var doc = goog.global.document;
+
+ // If the user tries to require a new symbol after document load,
+ // something has gone terribly wrong. Doing a document.write would
+ // wipe out the page. This does not apply to the CSP-compliant method
+ // of writing script tags.
+ if (!goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING &&
+ doc.readyState == 'complete') {
+ // Certain test frameworks load base.js multiple times, which tries
+ // to write deps.js each time. If that happens, just fail silently.
+ // These frameworks wipe the page between each load of base.js, so this
+ // is OK.
+ var isDeps = /\bdeps.js$/.test(src);
+ if (isDeps) {
+ return false;
+ } else {
+ throw new Error('Cannot write "' + src + '" after document load');
+ }
+ }
+
+ if (opt_sourceText === undefined) {
+ if (!goog.IS_OLD_IE_) {
+ if (goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING) {
+ goog.appendScriptSrcNode_(src);
+ } else {
+ goog.writeScriptSrcNode_(src);
+ }
+ } else {
+ goog.oldIeWaiting_ = true;
+ var state = ' onreadystatechange=\'goog.onScriptLoad_(this, ' +
+ ++goog.lastNonModuleScriptIndex_ + ')\' ';
+ doc.write(
+ '<script type="text/javascript" src="' + src + '"' + state +
+ '></' +
+ 'script>');
+ }
+ } else {
+ doc.write(
+ '<script type="text/javascript">' +
+ goog.protectScriptTag_(opt_sourceText) + '</' +
+ 'script>');
+ }
+ return true;
+ } else {
+ return false;
+ }
+ };
+
+ /**
+ * Rewrites closing script tags in input to avoid ending an enclosing script
+ * tag.
+ *
+ * @param {string} str
+ * @return {string}
+ * @private
+ */
+ goog.protectScriptTag_ = function(str) {
+ return str.replace(/<\/(SCRIPT)/ig, '\\x3c/$1');
+ };
+
+ /**
+ * Determines whether the given language needs to be transpiled.
+ * @param {string} lang
+ * @return {boolean}
+ * @private
+ */
+ goog.needsTranspile_ = function(lang) {
+ if (goog.TRANSPILE == 'always') {
+ return true;
+ } else if (goog.TRANSPILE == 'never') {
+ return false;
+ } else if (!goog.requiresTranspilation_) {
+ goog.requiresTranspilation_ = goog.createRequiresTranspilation_();
+ }
+ if (lang in goog.requiresTranspilation_) {
+ return goog.requiresTranspilation_[lang];
+ } else {
+ throw new Error('Unknown language mode: ' + lang);
+ }
+ };
+
+ /** @private {?Object<string, boolean>} */
+ goog.requiresTranspilation_ = null;
+
+
+ /** @private {number} */
+ goog.lastNonModuleScriptIndex_ = 0;
+
+
+ /**
+ * A readystatechange handler for legacy IE
+ * @param {?} script
+ * @param {number} scriptIndex
+ * @return {boolean}
+ * @private
+ */
+ goog.onScriptLoad_ = function(script, scriptIndex) {
+ // for now load the modules when we reach the last script,
+ // later allow more inter-mingling.
+ if (script.readyState == 'complete' &&
+ goog.lastNonModuleScriptIndex_ == scriptIndex) {
+ goog.loadQueuedModules_();
+ }
+ return true;
+ };
+
+ /**
+ * Resolves dependencies based on the dependencies added using addDependency
+ * and calls importScript_ in the correct order.
+ * @param {string} pathToLoad The path from which to start discovering
+ * dependencies.
+ * @private
+ */
+ goog.writeScripts_ = function(pathToLoad) {
+ /** @type {!Array<string>} The scripts we need to write this time. */
+ var scripts = [];
+ var seenScript = {};
+ var deps = goog.dependencies_;
+
+ /** @param {string} path */
+ function visitNode(path) {
+ if (path in deps.written) {
+ return;
+ }
+
+ // We have already visited this one. We can get here if we have cyclic
+ // dependencies.
+ if (path in deps.visited) {
+ return;
+ }
+
+ deps.visited[path] = true;
+
+ if (path in deps.requires) {
+ for (var requireName in deps.requires[path]) {
+ // If the required name is defined, we assume that it was already
+ // bootstrapped by other means.
+ if (!goog.isProvided_(requireName)) {
+ if (requireName in deps.nameToPath) {
+ visitNode(deps.nameToPath[requireName]);
+ } else {
+ throw new Error('Undefined nameToPath for ' + requireName);
+ }
+ }
+ }
+ }
+
+ if (!(path in seenScript)) {
+ seenScript[path] = true;
+ scripts.push(path);
+ }
+ }
+
+ visitNode(pathToLoad);
+
+ // record that we are going to load all these scripts.
+ for (var i = 0; i < scripts.length; i++) {
+ var path = scripts[i];
+ goog.dependencies_.written[path] = true;
+ }
+
+ // If a module is loaded synchronously then we need to
+ // clear the current inModuleLoader value, and restore it when we are
+ // done loading the current "requires".
+ var moduleState = goog.moduleLoaderState_;
+ goog.moduleLoaderState_ = null;
+
+ for (var i = 0; i < scripts.length; i++) {
+ var path = scripts[i];
+ if (path) {
+ var loadFlags = deps.loadFlags[path] || {};
+ var languageLevel = loadFlags['lang'] || 'es3';
+ var needsTranspile = goog.needsTranspile_(languageLevel);
+ if (loadFlags['module'] == 'goog' || needsTranspile) {
+ goog.importProcessedScript_(
+ goog.basePath + path, loadFlags['module'] == 'goog',
+ needsTranspile);
+ } else {
+ goog.importScript_(goog.basePath + path);
+ }
+ } else {
+ goog.moduleLoaderState_ = moduleState;
+ throw new Error('Undefined script input');
+ }
+ }
+
+ // restore the current "module loading state"
+ goog.moduleLoaderState_ = moduleState;
+ };
+
+
+ /**
+ * Looks at the dependency rules and tries to determine the script file that
+ * fulfills a particular rule.
+ * @param {string} rule In the form goog.namespace.Class or project.script.
+ * @return {?string} Url corresponding to the rule, or null.
+ * @private
+ */
+ goog.getPathFromDeps_ = function(rule) {
+ if (rule in goog.dependencies_.nameToPath) {
+ return goog.dependencies_.nameToPath[rule];
+ } else {
+ return null;
+ }
+ };
+
+ goog.findBasePath_();
+
+ // Allow projects to manage the deps files themselves.
+ if (!goog.global.CLOSURE_NO_DEPS) {
+ goog.importScript_(goog.basePath + 'deps.js');
+ }
+}
+
+
+/**
+ * @package {?boolean}
+ * Visible for testing.
+ */
+goog.hasBadLetScoping = null;
+
+
+/**
+ * @return {boolean}
+ * @package Visible for testing.
+ */
+goog.useSafari10Workaround = function() {
+ if (goog.hasBadLetScoping == null) {
+ var hasBadLetScoping;
+ try {
+ hasBadLetScoping = !eval(
+ '"use strict";' +
+ 'let x = 1; function f() { return typeof x; };' +
+ 'f() == "number";');
+ } catch (e) {
+ // Assume that ES6 syntax isn't supported.
+ hasBadLetScoping = false;
+ }
+ goog.hasBadLetScoping = hasBadLetScoping;
+ }
+ return goog.hasBadLetScoping;
+};
+
+
+/**
+ * @param {string} moduleDef
+ * @return {string}
+ * @package Visible for testing.
+ */
+goog.workaroundSafari10EvalBug = function(moduleDef) {
+ return '(function(){' + moduleDef +
+ '\n' + // Terminate any trailing single line comment.
+ ';' + // Terminate any trailing expression.
+ '})();\n';
+};
+
+
+/**
+ * @param {function(?):?|string} moduleDef The module definition.
+ */
+goog.loadModule = function(moduleDef) {
+ // NOTE: we allow function definitions to be either in the from
+ // of a string to eval (which keeps the original source intact) or
+ // in a eval forbidden environment (CSP) we allow a function definition
+ // which in its body must call {@code goog.module}, and return the exports
+ // of the module.
+ var previousState = goog.moduleLoaderState_;
+ try {
+ goog.moduleLoaderState_ = {
+ moduleName: undefined,
+ declareLegacyNamespace: false
+ };
+ var exports;
+ if (goog.isFunction(moduleDef)) {
+ exports = moduleDef.call(undefined, {});
+ } else if (goog.isString(moduleDef)) {
+ if (goog.useSafari10Workaround()) {
+ moduleDef = goog.workaroundSafari10EvalBug(moduleDef);
+ }
+
+ exports = goog.loadModuleFromSource_.call(undefined, moduleDef);
+ } else {
+ throw new Error('Invalid module definition');
+ }
+
+ var moduleName = goog.moduleLoaderState_.moduleName;
+ if (!goog.isString(moduleName) || !moduleName) {
+ throw new Error('Invalid module name \"' + moduleName + '\"');
+ }
+
+ // Don't seal legacy namespaces as they may be uses as a parent of
+ // another namespace
+ if (goog.moduleLoaderState_.declareLegacyNamespace) {
+ goog.constructNamespace_(moduleName, exports);
+ } else if (
+ goog.SEAL_MODULE_EXPORTS && Object.seal && typeof exports == 'object' &&
+ exports != null) {
+ Object.seal(exports);
+ }
+
+ goog.loadedModules_[moduleName] = exports;
+ } finally {
+ goog.moduleLoaderState_ = previousState;
+ }
+};
+
+
+/**
+ * @private @const
+ */
+goog.loadModuleFromSource_ = /** @type {function(string):?} */ (function() {
+ // NOTE: we avoid declaring parameters or local variables here to avoid
+ // masking globals or leaking values into the module definition.
+ 'use strict';
+ var exports = {};
+ eval(arguments[0]);
+ return exports;
+});
+
+
+/**
+ * Normalize a file path by removing redundant ".." and extraneous "." file
+ * path components.
+ * @param {string} path
+ * @return {string}
+ * @private
+ */
+goog.normalizePath_ = function(path) {
+ var components = path.split('/');
+ var i = 0;
+ while (i < components.length) {
+ if (components[i] == '.') {
+ components.splice(i, 1);
+ } else if (
+ i && components[i] == '..' && components[i - 1] &&
+ components[i - 1] != '..') {
+ components.splice(--i, 2);
+ } else {
+ i++;
+ }
+ }
+ return components.join('/');
+};
+
+
+/**
+ * Provides a hook for loading a file when using Closure's goog.require() API
+ * with goog.modules. In particular this hook is provided to support Node.js.
+ *
+ * @type {(function(string):string)|undefined}
+ */
+goog.global.CLOSURE_LOAD_FILE_SYNC;
+
+
+/**
+ * Loads file by synchronous XHR. Should not be used in production environments.
+ * @param {string} src Source URL.
+ * @return {?string} File contents, or null if load failed.
+ * @private
+ */
+goog.loadFileSync_ = function(src) {
+ if (goog.global.CLOSURE_LOAD_FILE_SYNC) {
+ return goog.global.CLOSURE_LOAD_FILE_SYNC(src);
+ } else {
+ try {
+ /** @type {XMLHttpRequest} */
+ var xhr = new goog.global['XMLHttpRequest']();
+ xhr.open('get', src, false);
+ xhr.send();
+ // NOTE: Successful http: requests have a status of 200, but successful
+ // file: requests may have a status of zero. Any other status, or a
+ // thrown exception (particularly in case of file: requests) indicates
+ // some sort of error, which we treat as a missing or unavailable file.
+ return xhr.status == 0 || xhr.status == 200 ? xhr.responseText : null;
+ } catch (err) {
+ // No need to rethrow or log, since errors should show up on their own.
+ return null;
+ }
+ }
+};
+
+
+/**
+ * Retrieve and execute a script that needs some sort of wrapping.
+ * @param {string} src Script source URL.
+ * @param {boolean} isModule Whether to load as a module.
+ * @param {boolean} needsTranspile Whether to transpile down to ES3.
+ * @private
+ */
+goog.retrieveAndExec_ = function(src, isModule, needsTranspile) {
+ if (!COMPILED) {
+ // The full but non-canonicalized URL for later use.
+ var originalPath = src;
+ // Canonicalize the path, removing any /./ or /../ since Chrome's debugging
+ // console doesn't auto-canonicalize XHR loads as it does <script> srcs.
+ src = goog.normalizePath_(src);
+
+ var importScript =
+ goog.global.CLOSURE_IMPORT_SCRIPT || goog.writeScriptTag_;
+
+ var scriptText = goog.loadFileSync_(src);
+ if (scriptText == null) {
+ throw new Error('Load of "' + src + '" failed');
+ }
+
+ if (needsTranspile) {
+ scriptText = goog.transpile_.call(goog.global, scriptText, src);
+ }
+
+ if (isModule) {
+ scriptText = goog.wrapModule_(src, scriptText);
+ } else {
+ scriptText += '\n//# sourceURL=' + src;
+ }
+ var isOldIE = goog.IS_OLD_IE_;
+ if (isOldIE && goog.oldIeWaiting_) {
+ goog.dependencies_.deferred[originalPath] = scriptText;
+ goog.queuedModules_.push(originalPath);
+ } else {
+ importScript(src, scriptText);
+ }
+ }
+};
+
+
+/**
+ * Lazily retrieves the transpiler and applies it to the source.
+ * @param {string} code JS code.
+ * @param {string} path Path to the code.
+ * @return {string} The transpiled code.
+ * @private
+ */
+goog.transpile_ = function(code, path) {
+ var jscomp = goog.global['$jscomp'];
+ if (!jscomp) {
+ goog.global['$jscomp'] = jscomp = {};
+ }
+ var transpile = jscomp.transpile;
+ if (!transpile) {
+ var transpilerPath = goog.basePath + goog.TRANSPILER;
+ var transpilerCode = goog.loadFileSync_(transpilerPath);
+ if (transpilerCode) {
+ // This must be executed synchronously, since by the time we know we
+ // need it, we're about to load and write the ES6 code synchronously,
+ // so a normal script-tag load will be too slow.
+ eval(transpilerCode + '\n//# sourceURL=' + transpilerPath);
+ // Even though the transpiler is optional, if $gwtExport is found, it's
+ // a sign the transpiler was loaded and the $jscomp.transpile *should*
+ // be there.
+ if (goog.global['$gwtExport'] && goog.global['$gwtExport']['$jscomp'] &&
+ !goog.global['$gwtExport']['$jscomp']['transpile']) {
+ throw new Error(
+ 'The transpiler did not properly export the "transpile" ' +
+ 'method. $gwtExport: ' + JSON.stringify(goog.global['$gwtExport']));
+ }
+ // transpile.js only exports a single $jscomp function, transpile. We
+ // grab just that and add it to the existing definition of $jscomp which
+ // contains the polyfills.
+ goog.global['$jscomp'].transpile =
+ goog.global['$gwtExport']['$jscomp']['transpile'];
+ jscomp = goog.global['$jscomp'];
+ transpile = jscomp.transpile;
+ }
+ }
+ if (!transpile) {
+ // The transpiler is an optional component. If it's not available then
+ // replace it with a pass-through function that simply logs.
+ var suffix = ' requires transpilation but no transpiler was found.';
+ // MOE:begin_strip
+ suffix += // Provide a more appropriate message internally.
+ ' Please add "//javascript/closure:transpiler" as a data ' +
+ 'dependency to ensure it is included.';
+ // MOE:end_strip
+ transpile = jscomp.transpile = function(code, path) {
+ // TODO(sdh): figure out some way to get this error to show up
+ // in test results, noting that the failure may occur in many
+ // different ways, including in loadModule() before the test
+ // runner even comes up.
+ goog.logToConsole_(path + suffix);
+ return code;
+ };
+ }
+ // Note: any transpilation errors/warnings will be logged to the console.
+ return transpile(code, path);
+};
+
+
+//==============================================================================
+// Language Enhancements
+//==============================================================================
+
+
+/**
+ * This is a "fixed" version of the typeof operator. It differs from the typeof
+ * operator in such a way that null returns 'null' and arrays return 'array'.
+ * @param {?} value The value to get the type of.
+ * @return {string} The name of the type.
+ */
+goog.typeOf = function(value) {
+ var s = typeof value;
+ if (s == 'object') {
+ if (value) {
+ // Check these first, so we can avoid calling Object.prototype.toString if
+ // possible.
+ //
+ // IE improperly marshals typeof across execution contexts, but a
+ // cross-context object will still return false for "instanceof Object".
+ if (value instanceof Array) {
+ return 'array';
+ } else if (value instanceof Object) {
+ return s;
+ }
+
+ // HACK: In order to use an Object prototype method on the arbitrary
+ // value, the compiler requires the value be cast to type Object,
+ // even though the ECMA spec explicitly allows it.
+ var className = Object.prototype.toString.call(
+ /** @type {!Object} */ (value));
+ // In Firefox 3.6, attempting to access iframe window objects' length
+ // property throws an NS_ERROR_FAILURE, so we need to special-case it
+ // here.
+ if (className == '[object Window]') {
+ return 'object';
+ }
+
+ // We cannot always use constructor == Array or instanceof Array because
+ // different frames have different Array objects. In IE6, if the iframe
+ // where the array was created is destroyed, the array loses its
+ // prototype. Then dereferencing val.splice here throws an exception, so
+ // we can't use goog.isFunction. Calling typeof directly returns 'unknown'
+ // so that will work. In this case, this function will return false and
+ // most array functions will still work because the array is still
+ // array-like (supports length and []) even though it has lost its
+ // prototype.
+ // Mark Miller noticed that Object.prototype.toString
+ // allows access to the unforgeable [[Class]] property.
+ // 15.2.4.2 Object.prototype.toString ( )
+ // When the toString method is called, the following steps are taken:
+ // 1. Get the [[Class]] property of this object.
+ // 2. Compute a string value by concatenating the three strings
+ // "[object ", Result(1), and "]".
+ // 3. Return Result(2).
+ // and this behavior survives the destruction of the execution context.
+ if ((className == '[object Array]' ||
+ // In IE all non value types are wrapped as objects across window
+ // boundaries (not iframe though) so we have to do object detection
+ // for this edge case.
+ typeof value.length == 'number' &&
+ typeof value.splice != 'undefined' &&
+ typeof value.propertyIsEnumerable != 'undefined' &&
+ !value.propertyIsEnumerable('splice')
+
+ )) {
+ return 'array';
+ }
+ // HACK: There is still an array case that fails.
+ // function ArrayImpostor() {}
+ // ArrayImpostor.prototype = [];
+ // var impostor = new ArrayImpostor;
+ // this can be fixed by getting rid of the fast path
+ // (value instanceof Array) and solely relying on
+ // (value && Object.prototype.toString.vall(value) === '[object Array]')
+ // but that would require many more function calls and is not warranted
+ // unless closure code is receiving objects from untrusted sources.
+
+ // IE in cross-window calls does not correctly marshal the function type
+ // (it appears just as an object) so we cannot use just typeof val ==
+ // 'function'. However, if the object has a call property, it is a
+ // function.
+ if ((className == '[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') {
+ // In Safari typeof nodeList returns 'function', and on Firefox typeof
+ // behaves similarly for HTML{Applet,Embed,Object}, Elements and RegExps. We
+ // would like to return object for those and we can detect an invalid
+ // function by making sure that the function object has a call method.
+ return 'object';
+ }
+ return s;
+};
+
+
+/**
+ * Returns true if the specified value is null.
+ * @param {?} val Variable to test.
+ * @return {boolean} Whether variable is null.
+ */
+goog.isNull = function(val) {
+ return val === null;
+};
+
+
+/**
+ * Returns true if the specified value is defined and not null.
+ * @param {?} val Variable to test.
+ * @return {boolean} Whether variable is defined and not null.
+ */
+goog.isDefAndNotNull = function(val) {
+ // Note that undefined == null.
+ return val != null;
+};
+
+
+/**
+ * Returns true if the specified value is an array.
+ * @param {?} val Variable to test.
+ * @return {boolean} Whether variable is an array.
+ */
+goog.isArray = function(val) {
+ return goog.typeOf(val) == 'array';
+};
+
+
+/**
+ * Returns true if the object looks like an array. To qualify as array like
+ * the value needs to be either a NodeList or an object with a Number length
+ * property. As a special case, a function value is not array like, because its
+ * length property is fixed to correspond to the number of expected arguments.
+ * @param {?} val Variable to test.
+ * @return {boolean} Whether variable is an array.
+ */
+goog.isArrayLike = function(val) {
+ var type = goog.typeOf(val);
+ // We do not use goog.isObject here in order to exclude function values.
+ return type == 'array' || type == 'object' && typeof val.length == 'number';
+};
+
+
+/**
+ * Returns true if the object looks like a Date. To qualify as Date-like the
+ * value needs to be an object and have a getFullYear() function.
+ * @param {?} val Variable to test.
+ * @return {boolean} Whether variable is a like a Date.
+ */
+goog.isDateLike = function(val) {
+ return goog.isObject(val) && typeof val.getFullYear == 'function';
+};
+
+
+/**
+ * Returns true if the specified value is a function.
+ * @param {?} val Variable to test.
+ * @return {boolean} Whether variable is a function.
+ */
+goog.isFunction = function(val) {
+ return goog.typeOf(val) == 'function';
+};
+
+
+/**
+ * Returns true if the specified value is an object. This includes arrays and
+ * functions.
+ * @param {?} val Variable to test.
+ * @return {boolean} Whether variable is an object.
+ */
+goog.isObject = function(val) {
+ var type = typeof val;
+ return type == 'object' && val != null || type == 'function';
+ // return Object(val) === val also works, but is slower, especially if val is
+ // not an object.
+};
+
+
+/**
+ * Gets a unique ID for an object. This mutates the object so that further calls
+ * with the same object as a parameter returns the same value. The unique ID is
+ * guaranteed to be unique across the current session amongst objects that are
+ * passed into {@code getUid}. There is no guarantee that the ID is unique or
+ * consistent across sessions. It is unsafe to generate unique ID for function
+ * prototypes.
+ *
+ * @param {Object} obj The object to get the unique ID for.
+ * @return {number} The unique ID for the object.
+ */
+goog.getUid = function(obj) {
+ // TODO(arv): Make the type stricter, do not accept null.
+
+ // In Opera window.hasOwnProperty exists but always returns false so we avoid
+ // using it. As a consequence the unique ID generated for BaseClass.prototype
+ // and SubClass.prototype will be the same.
+ return obj[goog.UID_PROPERTY_] ||
+ (obj[goog.UID_PROPERTY_] = ++goog.uidCounter_);
+};
+
+
+/**
+ * Whether the given object is already assigned a unique ID.
+ *
+ * This does not modify the object.
+ *
+ * @param {!Object} obj The object to check.
+ * @return {boolean} Whether there is an assigned unique id for the object.
+ */
+goog.hasUid = function(obj) {
+ return !!obj[goog.UID_PROPERTY_];
+};
+
+
+/**
+ * Removes the unique ID from an object. This is useful if the object was
+ * previously mutated using {@code goog.getUid} in which case the mutation is
+ * undone.
+ * @param {Object} obj The object to remove the unique ID field from.
+ */
+goog.removeUid = function(obj) {
+ // TODO(arv): Make the type stricter, do not accept null.
+
+ // In IE, DOM nodes are not instances of Object and throw an exception if we
+ // try to delete. Instead we try to use removeAttribute.
+ if (obj !== null && 'removeAttribute' in obj) {
+ obj.removeAttribute(goog.UID_PROPERTY_);
+ }
+
+ try {
+ delete obj[goog.UID_PROPERTY_];
+ } catch (ex) {
+ }
+};
+
+
+/**
+ * Name for unique ID property. Initialized in a way to help avoid collisions
+ * with other closure JavaScript on the same page.
+ * @type {string}
+ * @private
+ */
+goog.UID_PROPERTY_ = 'closure_uid_' + ((Math.random() * 1e9) >>> 0);
+
+
+/**
+ * Counter for UID.
+ * @type {number}
+ * @private
+ */
+goog.uidCounter_ = 0;
+
+
+/**
+ * Adds a hash code field to an object. The hash code is unique for the
+ * given object.
+ * @param {Object} obj The object to get the hash code for.
+ * @return {number} The hash code for the object.
+ * @deprecated Use goog.getUid instead.
+ */
+goog.getHashCode = goog.getUid;
+
+
+/**
+ * Removes the hash code field from an object.
+ * @param {Object} obj The object to remove the field from.
+ * @deprecated Use goog.removeUid instead.
+ */
+goog.removeHashCode = goog.removeUid;
+
+
+/**
+ * Clones a value. The input may be an Object, Array, or basic type. Objects and
+ * arrays will be cloned recursively.
+ *
+ * WARNINGS:
+ * <code>goog.cloneObject</code> does not detect reference loops. Objects that
+ * refer to themselves will cause infinite recursion.
+ *
+ * <code>goog.cloneObject</code> is unaware of unique identifiers, and copies
+ * UIDs created by <code>getUid</code> into cloned results.
+ *
+ * @param {*} obj The value to clone.
+ * @return {*} A clone of the input value.
+ * @deprecated goog.cloneObject is unsafe. Prefer the goog.object methods.
+ */
+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;
+};
+
+
+/**
+ * A native implementation of goog.bind.
+ * @param {?function(this:T, ...)} fn A function to partially apply.
+ * @param {T} selfObj Specifies the object which this should point to when the
+ * function is run.
+ * @param {...*} var_args Additional arguments that are partially applied to the
+ * function.
+ * @return {!Function} A partially-applied form of the function goog.bind() was
+ * invoked as a method of.
+ * @template T
+ * @private
+ */
+goog.bindNative_ = function(fn, selfObj, var_args) {
+ return /** @type {!Function} */ (fn.call.apply(fn.bind, arguments));
+};
+
+
+/**
+ * A pure-JS implementation of goog.bind.
+ * @param {?function(this:T, ...)} fn A function to partially apply.
+ * @param {T} selfObj Specifies the object which this should point to when the
+ * function is run.
+ * @param {...*} var_args Additional arguments that are partially applied to the
+ * function.
+ * @return {!Function} A partially-applied form of the function goog.bind() was
+ * invoked as a method of.
+ * @template T
+ * @private
+ */
+goog.bindJs_ = function(fn, selfObj, var_args) {
+ if (!fn) {
+ throw new Error();
+ }
+
+ if (arguments.length > 2) {
+ var boundArgs = Array.prototype.slice.call(arguments, 2);
+ return function() {
+ // Prepend the bound arguments to the current arguments.
+ var newArgs = Array.prototype.slice.call(arguments);
+ Array.prototype.unshift.apply(newArgs, boundArgs);
+ return fn.apply(selfObj, newArgs);
+ };
+
+ } else {
+ return function() {
+ return fn.apply(selfObj, arguments);
+ };
+ }
+};
+
+
+/**
+ * Partially applies this function to a particular 'this object' and zero or
+ * more arguments. The result is a new function with some arguments of the first
+ * function pre-filled and the value of this 'pre-specified'.
+ *
+ * Remaining arguments specified at call-time are appended to the pre-specified
+ * ones.
+ *
+ * Also see: {@link #partial}.
+ *
+ * Usage:
+ * <pre>var barMethBound = goog.bind(myFunction, myObj, 'arg1', 'arg2');
+ * barMethBound('arg3', 'arg4');</pre>
+ *
+ * @param {?function(this:T, ...)} fn A function to partially apply.
+ * @param {T} selfObj Specifies the object which this should point to when the
+ * function is run.
+ * @param {...*} var_args Additional arguments that are partially applied to the
+ * function.
+ * @return {!Function} A partially-applied form of the function goog.bind() was
+ * invoked as a method of.
+ * @template T
+ * @suppress {deprecated} See above.
+ */
+goog.bind = function(fn, selfObj, var_args) {
+ // TODO(nicksantos): narrow the type signature.
+ if (Function.prototype.bind &&
+ // NOTE(nicksantos): Somebody pulled base.js into the default Chrome
+ // extension environment. This means that for Chrome extensions, they get
+ // the implementation of Function.prototype.bind that calls goog.bind
+ // instead of the native one. Even worse, we don't want to introduce a
+ // circular dependency between goog.bind and Function.prototype.bind, so
+ // we have to hack this to make sure it works correctly.
+ Function.prototype.bind.toString().indexOf('native code') != -1) {
+ goog.bind = goog.bindNative_;
+ } else {
+ goog.bind = goog.bindJs_;
+ }
+ return goog.bind.apply(null, arguments);
+};
+
+
+/**
+ * Like goog.bind(), except that a 'this object' is not required. Useful when
+ * the target function is already bound.
+ *
+ * Usage:
+ * var g = goog.partial(f, arg1, arg2);
+ * g(arg3, arg4);
+ *
+ * @param {Function} fn A function to partially apply.
+ * @param {...*} var_args Additional arguments that are partially applied to fn.
+ * @return {!Function} A partially-applied form of the function goog.partial()
+ * was invoked as a method of.
+ */
+goog.partial = function(fn, var_args) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ return function() {
+ // Clone the array (with slice()) and append additional arguments
+ // to the existing arguments.
+ var newArgs = args.slice();
+ newArgs.push.apply(newArgs, arguments);
+ return fn.apply(this, newArgs);
+ };
+};
+
+
+/**
+ * Copies all the members of a source object to a target object. This method
+ * does not work on all browsers for all objects that contain keys such as
+ * toString or hasOwnProperty. Use goog.object.extend for this purpose.
+ * @param {Object} target Target.
+ * @param {Object} source Source.
+ */
+goog.mixin = function(target, source) {
+ for (var x in source) {
+ target[x] = source[x];
+ }
+
+ // For IE7 or lower, the for-in-loop does not contain any properties that are
+ // not enumerable on the prototype object (for example, isPrototypeOf from
+ // Object.prototype) but also it will not include 'replace' on objects that
+ // extend String and change 'replace' (not that it is common for anyone to
+ // extend anything except Object).
+};
+
+
+/**
+ * @return {number} An integer value representing the number of milliseconds
+ * between midnight, January 1, 1970 and the current time.
+ */
+goog.now = (goog.TRUSTED_SITE && Date.now) || (function() {
+ // Unary plus operator converts its operand to a number which in
+ // the case of
+ // a date is done by calling getTime().
+ return +new Date();
+ });
+
+
+/**
+ * Evals JavaScript in the global scope. In IE this uses execScript, other
+ * browsers use goog.global.eval. If goog.global.eval does not evaluate in the
+ * global scope (for example, in Safari), appends a script tag instead.
+ * Throws an exception if neither execScript or eval is defined.
+ * @param {string} script JavaScript string.
+ */
+goog.globalEval = function(script) {
+ if (goog.global.execScript) {
+ goog.global.execScript(script, 'JavaScript');
+ } else if (goog.global.eval) {
+ // Test to see if eval works
+ if (goog.evalWorksForGlobals_ == null) {
+ goog.global.eval('var _evalTest_ = 1;');
+ if (typeof goog.global['_evalTest_'] != 'undefined') {
+ try {
+ delete goog.global['_evalTest_'];
+ } catch (ignore) {
+ // Microsoft edge fails the deletion above in strict mode.
+ }
+ goog.evalWorksForGlobals_ = true;
+ } else {
+ goog.evalWorksForGlobals_ = false;
+ }
+ }
+
+ if (goog.evalWorksForGlobals_) {
+ goog.global.eval(script);
+ } else {
+ /** @type {Document} */
+ var doc = goog.global.document;
+ var scriptElt =
+ /** @type {!HTMLScriptElement} */ (doc.createElement('SCRIPT'));
+ scriptElt.type = 'text/javascript';
+ scriptElt.defer = false;
+ // Note(pupius): can't use .innerHTML since "t('<test>')" will fail and
+ // .text doesn't work in Safari 2. Therefore we append a text node.
+ scriptElt.appendChild(doc.createTextNode(script));
+ doc.body.appendChild(scriptElt);
+ doc.body.removeChild(scriptElt);
+ }
+ } else {
+ throw new Error('goog.globalEval not available');
+ }
+};
+
+
+/**
+ * Indicates whether or not we can call 'eval' directly to eval code in the
+ * global scope. Set to a Boolean by the first call to goog.globalEval (which
+ * empirically tests whether eval works for globals). @see goog.globalEval
+ * @type {?boolean}
+ * @private
+ */
+goog.evalWorksForGlobals_ = null;
+
+
+/**
+ * Optional map of CSS class names to obfuscated names used with
+ * goog.getCssName().
+ * @private {!Object<string, string>|undefined}
+ * @see goog.setCssNameMapping
+ */
+goog.cssNameMapping_;
+
+
+/**
+ * Optional obfuscation style for CSS class names. Should be set to either
+ * 'BY_WHOLE' or 'BY_PART' if defined.
+ * @type {string|undefined}
+ * @private
+ * @see goog.setCssNameMapping
+ */
+goog.cssNameMappingStyle_;
+
+
+
+/**
+ * A hook for modifying the default behavior goog.getCssName. The function
+ * if present, will recieve the standard output of the goog.getCssName as
+ * its input.
+ *
+ * @type {(function(string):string)|undefined}
+ */
+goog.global.CLOSURE_CSS_NAME_MAP_FN;
+
+
+/**
+ * Handles strings that are intended to be used as CSS class names.
+ *
+ * This function works in tandem with @see goog.setCssNameMapping.
+ *
+ * Without any mapping set, the arguments are simple joined with a hyphen and
+ * passed through unaltered.
+ *
+ * When there is a mapping, there are two possible styles in which these
+ * mappings are used. In the BY_PART style, each part (i.e. in between hyphens)
+ * of the passed in css name is rewritten according to the map. In the BY_WHOLE
+ * style, the full css name is looked up in the map directly. If a rewrite is
+ * not specified by the map, the compiler will output a warning.
+ *
+ * When the mapping is passed to the compiler, it will replace calls to
+ * goog.getCssName with the strings from the mapping, e.g.
+ * var x = goog.getCssName('foo');
+ * var y = goog.getCssName(this.baseClass, 'active');
+ * becomes:
+ * var x = 'foo';
+ * var y = this.baseClass + '-active';
+ *
+ * If one argument is passed it will be processed, if two are passed only the
+ * modifier will be processed, as it is assumed the first argument was generated
+ * as a result of calling goog.getCssName.
+ *
+ * @param {string} className The class name.
+ * @param {string=} opt_modifier A modifier to be appended to the class name.
+ * @return {string} The class name or the concatenation of the class name and
+ * the modifier.
+ */
+goog.getCssName = function(className, opt_modifier) {
+ // String() is used for compatibility with compiled soy where the passed
+ // className can be non-string objects.
+ if (String(className).charAt(0) == '.') {
+ throw new Error(
+ 'className passed in goog.getCssName must not start with ".".' +
+ ' You passed: ' + className);
+ }
+
+ var getMapping = function(cssName) {
+ return goog.cssNameMapping_[cssName] || cssName;
+ };
+
+ var renameByParts = function(cssName) {
+ // Remap all the parts individually.
+ var parts = cssName.split('-');
+ var mapped = [];
+ for (var i = 0; i < parts.length; i++) {
+ mapped.push(getMapping(parts[i]));
+ }
+ return mapped.join('-');
+ };
+
+ var rename;
+ if (goog.cssNameMapping_) {
+ rename =
+ goog.cssNameMappingStyle_ == 'BY_WHOLE' ? getMapping : renameByParts;
+ } else {
+ rename = function(a) {
+ return a;
+ };
+ }
+
+ var result =
+ opt_modifier ? className + '-' + rename(opt_modifier) : rename(className);
+
+ // The special CLOSURE_CSS_NAME_MAP_FN allows users to specify further
+ // processing of the class name.
+ if (goog.global.CLOSURE_CSS_NAME_MAP_FN) {
+ return goog.global.CLOSURE_CSS_NAME_MAP_FN(result);
+ }
+
+ return result;
+};
+
+
+/**
+ * Sets the map to check when returning a value from goog.getCssName(). Example:
+ * <pre>
+ * goog.setCssNameMapping({
+ * "goog": "a",
+ * "disabled": "b",
+ * });
+ *
+ * var x = goog.getCssName('goog');
+ * // The following evaluates to: "a a-b".
+ * goog.getCssName('goog') + ' ' + goog.getCssName(x, 'disabled')
+ * </pre>
+ * When declared as a map of string literals to string literals, the JSCompiler
+ * will replace all calls to goog.getCssName() using the supplied map if the
+ * --process_closure_primitives flag is set.
+ *
+ * @param {!Object} mapping A map of strings to strings where keys are possible
+ * arguments to goog.getCssName() and values are the corresponding values
+ * that should be returned.
+ * @param {string=} opt_style The style of css name mapping. There are two valid
+ * options: 'BY_PART', and 'BY_WHOLE'.
+ * @see goog.getCssName for a description.
+ */
+goog.setCssNameMapping = function(mapping, opt_style) {
+ goog.cssNameMapping_ = mapping;
+ goog.cssNameMappingStyle_ = opt_style;
+};
+
+
+/**
+ * To use CSS renaming in compiled mode, one of the input files should have a
+ * call to goog.setCssNameMapping() with an object literal that the JSCompiler
+ * can extract and use to replace all calls to goog.getCssName(). In uncompiled
+ * mode, JavaScript code should be loaded before this base.js file that declares
+ * a global variable, CLOSURE_CSS_NAME_MAPPING, which is used below. This is
+ * to ensure that the mapping is loaded before any calls to goog.getCssName()
+ * are made in uncompiled mode.
+ *
+ * A hook for overriding the CSS name mapping.
+ * @type {!Object<string, string>|undefined}
+ */
+goog.global.CLOSURE_CSS_NAME_MAPPING;
+
+
+if (!COMPILED && goog.global.CLOSURE_CSS_NAME_MAPPING) {
+ // This does not call goog.setCssNameMapping() because the JSCompiler
+ // requires that goog.setCssNameMapping() be called with an object literal.
+ goog.cssNameMapping_ = goog.global.CLOSURE_CSS_NAME_MAPPING;
+}
+
+
+/**
+ * Gets a localized message.
+ *
+ * This function is a compiler primitive. If you give the compiler a localized
+ * message bundle, it will replace the string at compile-time with a localized
+ * version, and expand goog.getMsg call to a concatenated string.
+ *
+ * Messages must be initialized in the form:
+ * <code>
+ * var MSG_NAME = goog.getMsg('Hello {$placeholder}', {'placeholder': 'world'});
+ * </code>
+ *
+ * This function produces a string which should be treated as plain text. Use
+ * {@link goog.html.SafeHtmlFormatter} in conjunction with goog.getMsg to
+ * produce SafeHtml.
+ *
+ * @param {string} str Translatable string, places holders in the form {$foo}.
+ * @param {Object<string, string>=} opt_values Maps place holder name to value.
+ * @return {string} message with placeholders filled.
+ */
+goog.getMsg = function(str, opt_values) {
+ if (opt_values) {
+ str = str.replace(/\{\$([^}]+)}/g, function(match, key) {
+ return (opt_values != null && key in opt_values) ? opt_values[key] :
+ match;
+ });
+ }
+ return str;
+};
+
+
+/**
+ * Gets a localized message. If the message does not have a translation, gives a
+ * fallback message.
+ *
+ * This is useful when introducing a new message that has not yet been
+ * translated into all languages.
+ *
+ * This function is a compiler primitive. Must be used in the form:
+ * <code>var x = goog.getMsgWithFallback(MSG_A, MSG_B);</code>
+ * where MSG_A and MSG_B were initialized with goog.getMsg.
+ *
+ * @param {string} a The preferred message.
+ * @param {string} b The fallback message.
+ * @return {string} The best translated message.
+ */
+goog.getMsgWithFallback = function(a, b) {
+ return a;
+};
+
+
+/**
+ * Exposes an unobfuscated global namespace path for the given object.
+ * Note that fields of the exported object *will* be obfuscated, unless they are
+ * exported in turn via this function or goog.exportProperty.
+ *
+ * Also handy for making public items that are defined in anonymous closures.
+ *
+ * ex. goog.exportSymbol('public.path.Foo', Foo);
+ *
+ * ex. goog.exportSymbol('public.path.Foo.staticFunction', Foo.staticFunction);
+ * public.path.Foo.staticFunction();
+ *
+ * ex. goog.exportSymbol('public.path.Foo.prototype.myMethod',
+ * Foo.prototype.myMethod);
+ * new public.path.Foo().myMethod();
+ *
+ * @param {string} publicPath Unobfuscated name to export.
+ * @param {*} object Object the name should point to.
+ * @param {Object=} opt_objectToExportTo The object to add the path to; default
+ * is goog.global.
+ */
+goog.exportSymbol = function(publicPath, object, opt_objectToExportTo) {
+ goog.exportPath_(publicPath, object, opt_objectToExportTo);
+};
+
+
+/**
+ * Exports a property unobfuscated into the object's namespace.
+ * ex. goog.exportProperty(Foo, 'staticFunction', Foo.staticFunction);
+ * ex. goog.exportProperty(Foo.prototype, 'myMethod', Foo.prototype.myMethod);
+ * @param {Object} object Object whose static property is being exported.
+ * @param {string} publicName Unobfuscated name to export.
+ * @param {*} symbol Object the name should point to.
+ */
+goog.exportProperty = function(object, publicName, symbol) {
+ object[publicName] = symbol;
+};
+
+
+/**
+ * Inherit the prototype methods from one constructor into another.
+ *
+ * Usage:
+ * <pre>
+ * function ParentClass(a, b) { }
+ * ParentClass.prototype.foo = function(a) { };
+ *
+ * function ChildClass(a, b, c) {
+ * ChildClass.base(this, 'constructor', a, b);
+ * }
+ * goog.inherits(ChildClass, ParentClass);
+ *
+ * var child = new ChildClass('a', 'b', 'see');
+ * child.foo(); // This works.
+ * </pre>
+ *
+ * @param {!Function} childCtor Child class.
+ * @param {!Function} parentCtor Parent class.
+ */
+goog.inherits = function(childCtor, parentCtor) {
+ /** @constructor */
+ function tempCtor() {}
+ tempCtor.prototype = parentCtor.prototype;
+ childCtor.superClass_ = parentCtor.prototype;
+ childCtor.prototype = new tempCtor();
+ /** @override */
+ childCtor.prototype.constructor = childCtor;
+
+ /**
+ * Calls superclass constructor/method.
+ *
+ * This function is only available if you use goog.inherits to
+ * express inheritance relationships between classes.
+ *
+ * NOTE: This is a replacement for goog.base and for superClass_
+ * property defined in childCtor.
+ *
+ * @param {!Object} me Should always be "this".
+ * @param {string} methodName The method name to call. Calling
+ * superclass constructor can be done with the special string
+ * 'constructor'.
+ * @param {...*} var_args The arguments to pass to superclass
+ * method/constructor.
+ * @return {*} The return value of the superclass method/constructor.
+ */
+ childCtor.base = function(me, methodName, var_args) {
+ // Copying using loop to avoid deop due to passing arguments object to
+ // function. This is faster in many JS engines as of late 2014.
+ var args = new Array(arguments.length - 2);
+ for (var i = 2; i < arguments.length; i++) {
+ args[i - 2] = arguments[i];
+ }
+ return parentCtor.prototype[methodName].apply(me, args);
+ };
+};
+
+
+/**
+ * Call up to the superclass.
+ *
+ * If this is called from a constructor, then this calls the superclass
+ * constructor with arguments 1-N.
+ *
+ * If this is called from a prototype method, then you must pass the name of the
+ * method as the second argument to this function. If you do not, you will get a
+ * runtime error. This calls the superclass' method with arguments 2-N.
+ *
+ * This function only works if you use goog.inherits to express inheritance
+ * relationships between your classes.
+ *
+ * This function is a compiler primitive. At compile-time, the compiler will do
+ * macro expansion to remove a lot of the extra overhead that this function
+ * introduces. The compiler will also enforce a lot of the assumptions that this
+ * function makes, and treat it as a compiler error if you break them.
+ *
+ * @param {!Object} me Should always be "this".
+ * @param {*=} opt_methodName The method name if calling a super method.
+ * @param {...*} var_args The rest of the arguments.
+ * @return {*} The return value of the superclass method.
+ * @suppress {es5Strict} This method can not be used in strict mode, but
+ * all Closure Library consumers must depend on this file.
+ * @deprecated goog.base is not strict mode compatible. Prefer the static
+ * "base" method added to the constructor by goog.inherits
+ * or ES6 classes and the "super" keyword.
+ */
+goog.base = function(me, opt_methodName, var_args) {
+ var caller = arguments.callee.caller;
+
+ if (goog.STRICT_MODE_COMPATIBLE || (goog.DEBUG && !caller)) {
+ throw new Error(
+ 'arguments.caller not defined. goog.base() cannot be used ' +
+ 'with strict mode code. See ' +
+ 'http://www.ecma-international.org/ecma-262/5.1/#sec-C');
+ }
+
+ if (caller.superClass_) {
+ // Copying using loop to avoid deop due to passing arguments object to
+ // function. This is faster in many JS engines as of late 2014.
+ var ctorArgs = new Array(arguments.length - 1);
+ for (var i = 1; i < arguments.length; i++) {
+ ctorArgs[i - 1] = arguments[i];
+ }
+ // This is a constructor. Call the superclass constructor.
+ return caller.superClass_.constructor.apply(me, ctorArgs);
+ }
+
+ // Copying using loop to avoid deop due to passing arguments object to
+ // function. This is faster in many JS engines as of late 2014.
+ var args = new Array(arguments.length - 2);
+ for (var i = 2; i < arguments.length; i++) {
+ args[i - 2] = arguments[i];
+ }
+ 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 we did not find the caller in the prototype chain, then one of two
+ // things happened:
+ // 1) The caller is an instance method.
+ // 2) This method was not called by the right caller.
+ if (me[opt_methodName] === caller) {
+ return me.constructor.prototype[opt_methodName].apply(me, args);
+ } else {
+ throw new Error(
+ 'goog.base called from a method of one name ' +
+ 'to a method of a different name');
+ }
+};
+
+
+/**
+ * Allow for aliasing within scope functions. This function exists for
+ * uncompiled code - in compiled code the calls will be inlined and the aliases
+ * applied. In uncompiled code the function is simply run since the aliases as
+ * written are valid JavaScript.
+ *
+ * MOE:begin_intracomment_strip
+ * See the goog.scope document at http://go/goog.scope
+ * MOE:end_intracomment_strip
+ *
+ * @param {function()} fn Function to call. This function can contain aliases
+ * to namespaces (e.g. "var dom = goog.dom") or classes
+ * (e.g. "var Timer = goog.Timer").
+ */
+goog.scope = function(fn) {
+ if (goog.isInModuleLoader_()) {
+ throw new Error('goog.scope is not supported within a goog.module.');
+ }
+ fn.call(goog.global);
+};
+
+
+/*
+ * To support uncompiled, strict mode bundles that use eval to divide source
+ * like so:
+ * eval('someSource;//# sourceUrl sourcefile.js');
+ * We need to export the globally defined symbols "goog" and "COMPILED".
+ * Exporting "goog" breaks the compiler optimizations, so we required that
+ * be defined externally.
+ * NOTE: We don't use goog.exportSymbol here because we don't want to trigger
+ * extern generation when that compiler option is enabled.
+ */
+if (!COMPILED) {
+ goog.global['COMPILED'] = COMPILED;
+}
+
+
+//==============================================================================
+// goog.defineClass implementation
+//==============================================================================
+
+
+/**
+ * Creates a restricted form of a Closure "class":
+ * - from the compiler's perspective, the instance returned from the
+ * constructor is sealed (no new properties may be added). This enables
+ * better checks.
+ * - the compiler will rewrite this definition to a form that is optimal
+ * for type checking and optimization (initially this will be a more
+ * traditional form).
+ *
+ * @param {Function} superClass The superclass, Object or null.
+ * @param {goog.defineClass.ClassDescriptor} def
+ * An object literal describing
+ * the class. It may have the following properties:
+ * "constructor": the constructor function
+ * "statics": an object literal containing methods to add to the constructor
+ * as "static" methods or a function that will receive the constructor
+ * function as its only parameter to which static properties can
+ * be added.
+ * all other properties are added to the prototype.
+ * @return {!Function} The class constructor.
+ */
+goog.defineClass = function(superClass, def) {
+ // TODO(johnlenz): consider making the superClass an optional parameter.
+ var constructor = def.constructor;
+ var statics = def.statics;
+ // Wrap the constructor prior to setting up the prototype and static methods.
+ if (!constructor || constructor == Object.prototype.constructor) {
+ constructor = function() {
+ throw new Error(
+ 'cannot instantiate an interface (no constructor defined).');
+ };
+ }
+
+ var cls = goog.defineClass.createSealingConstructor_(constructor, superClass);
+ if (superClass) {
+ goog.inherits(cls, superClass);
+ }
+
+ // Remove all the properties that should not be copied to the prototype.
+ delete def.constructor;
+ delete def.statics;
+
+ goog.defineClass.applyProperties_(cls.prototype, def);
+ if (statics != null) {
+ if (statics instanceof Function) {
+ statics(cls);
+ } else {
+ goog.defineClass.applyProperties_(cls, statics);
+ }
+ }
+
+ return cls;
+};
+
+
+/**
+ * @typedef {{
+ * constructor: (!Function|undefined),
+ * statics: (Object|undefined|function(Function):void)
+ * }}
+ */
+goog.defineClass.ClassDescriptor;
+
+
+/**
+ * @define {boolean} Whether the instances returned by goog.defineClass should
+ * be sealed when possible.
+ *
+ * When sealing is disabled the constructor function will not be wrapped by
+ * goog.defineClass, making it incompatible with ES6 class methods.
+ */
+goog.define('goog.defineClass.SEAL_CLASS_INSTANCES', goog.DEBUG);
+
+
+/**
+ * If goog.defineClass.SEAL_CLASS_INSTANCES is enabled and Object.seal is
+ * defined, this function will wrap the constructor in a function that seals the
+ * results of the provided constructor function.
+ *
+ * @param {!Function} ctr The constructor whose results maybe be sealed.
+ * @param {Function} superClass The superclass constructor.
+ * @return {!Function} The replacement constructor.
+ * @private
+ */
+goog.defineClass.createSealingConstructor_ = function(ctr, superClass) {
+ if (!goog.defineClass.SEAL_CLASS_INSTANCES) {
+ // Do now wrap the constructor when sealing is disabled. Angular code
+ // depends on this for injection to work properly.
+ return ctr;
+ }
+
+ // Compute whether the constructor is sealable at definition time, rather
+ // than when the instance is being constructed.
+ var superclassSealable = !goog.defineClass.isUnsealable_(superClass);
+
+ /**
+ * @this {Object}
+ * @return {?}
+ */
+ var wrappedCtr = function() {
+ // Don't seal an instance of a subclass when it calls the constructor of
+ // its super class as there is most likely still setup to do.
+ var instance = ctr.apply(this, arguments) || this;
+ instance[goog.UID_PROPERTY_] = instance[goog.UID_PROPERTY_];
+
+ if (this.constructor === wrappedCtr && superclassSealable &&
+ Object.seal instanceof Function) {
+ Object.seal(instance);
+ }
+ return instance;
+ };
+
+ return wrappedCtr;
+};
+
+
+/**
+ * @param {Function} ctr The constructor to test.
+ * @return {boolean} Whether the constructor has been tagged as unsealable
+ * using goog.tagUnsealableClass.
+ * @private
+ */
+goog.defineClass.isUnsealable_ = function(ctr) {
+ return ctr && ctr.prototype &&
+ ctr.prototype[goog.UNSEALABLE_CONSTRUCTOR_PROPERTY_];
+};
+
+
+// TODO(johnlenz): share these values with the goog.object
+/**
+ * The names of the fields that are defined on Object.prototype.
+ * @type {!Array<string>}
+ * @private
+ * @const
+ */
+goog.defineClass.OBJECT_PROTOTYPE_FIELDS_ = [
+ 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable',
+ 'toLocaleString', 'toString', 'valueOf'
+];
+
+
+// TODO(johnlenz): share this function with the goog.object
+/**
+ * @param {!Object} target The object to add properties to.
+ * @param {!Object} source The object to copy properties from.
+ * @private
+ */
+goog.defineClass.applyProperties_ = function(target, source) {
+ // TODO(johnlenz): update this to support ES5 getters/setters
+
+ var key;
+ for (key in source) {
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
+ target[key] = source[key];
+ }
+ }
+
+ // For IE the for-in-loop does not contain any properties that are not
+ // enumerable on the prototype object (for example isPrototypeOf from
+ // Object.prototype) and it will also not include 'replace' on objects that
+ // extend String and change 'replace' (not that it is common for anyone to
+ // extend anything except Object).
+ for (var i = 0; i < goog.defineClass.OBJECT_PROTOTYPE_FIELDS_.length; i++) {
+ key = goog.defineClass.OBJECT_PROTOTYPE_FIELDS_[i];
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
+ target[key] = source[key];
+ }
+ }
+};
+
+
+/**
+ * Sealing classes breaks the older idiom of assigning properties on the
+ * prototype rather than in the constructor. As such, goog.defineClass
+ * must not seal subclasses of these old-style classes until they are fixed.
+ * Until then, this marks a class as "broken", instructing defineClass
+ * not to seal subclasses.
+ * @param {!Function} ctr The legacy constructor to tag as unsealable.
+ */
+goog.tagUnsealableClass = function(ctr) {
+ if (!COMPILED && goog.defineClass.SEAL_CLASS_INSTANCES) {
+ ctr.prototype[goog.UNSEALABLE_CONSTRUCTOR_PROPERTY_] = true;
+ }
+};
+
+
+/**
+ * Name for unsealable tag property.
+ * @const @private {string}
+ */
+goog.UNSEALABLE_CONSTRUCTOR_PROPERTY_ = 'goog_defineClass_legacy_unsealable';
+
+
+/**
+ * Returns a newly created map from language mode string to a boolean
+ * indicating whether transpilation should be done for that mode.
+ *
+ * Guaranteed invariant:
+ * For any two modes, l1 and l2 where l2 is a newer mode than l1,
+ * `map[l1] == true` implies that `map[l2] == true`.
+ * @private
+ * @return {!Object<string, boolean>}
+ */
+goog.createRequiresTranspilation_ = function() {
+ var /** !Object<string, boolean> */ requiresTranspilation = {'es3': false};
+ var transpilationRequiredForAllLaterModes = false;
+
+ /**
+ * Adds an entry to requiresTranspliation for the given language mode.
+ *
+ * IMPORTANT: Calls must be made in order from oldest to newest language
+ * mode.
+ * @param {string} modeName
+ * @param {function(): boolean} isSupported Returns true if the JS engine
+ * supports the given mode.
+ */
+ function addNewerLanguageTranspilationCheck(modeName, isSupported) {
+ if (transpilationRequiredForAllLaterModes) {
+ requiresTranspilation[modeName] = true;
+ } else if (isSupported()) {
+ requiresTranspilation[modeName] = false;
+ } else {
+ requiresTranspilation[modeName] = true;
+ transpilationRequiredForAllLaterModes = true;
+ }
+ }
+
+ /**
+ * Does the given code evaluate without syntax errors and return a truthy
+ * result?
+ */
+ function /** boolean */ evalCheck(/** string */ code) {
+ try {
+ return !!eval(code);
+ } catch (ignored) {
+ return false;
+ }
+ }
+
+ var userAgent = goog.global.navigator && goog.global.navigator.userAgent ?
+ goog.global.navigator.userAgent :
+ '';
+
+ // Identify ES3-only browsers by their incorrect treatment of commas.
+ addNewerLanguageTranspilationCheck('es5', function() {
+ return evalCheck('[1,].length==1');
+ });
+ addNewerLanguageTranspilationCheck('es6', function() {
+ // Edge has a non-deterministic (i.e., not reproducible) bug with ES6:
+ // https://github.com/Microsoft/ChakraCore/issues/1496.
+ // MOE:begin_strip
+ // TODO(joeltine): Our internal web-testing version of Edge will need to be
+ // updated before we can remove this check. See http://b/34945376.
+ // MOE:end_strip
+ var re = /Edge\/(\d+)(\.\d)*/i;
+ var edgeUserAgent = userAgent.match(re);
+ if (edgeUserAgent && Number(edgeUserAgent[1]) < 15) {
+ return false;
+ }
+ // Test es6: [FF50 (?), Edge 14 (?), Chrome 50]
+ // (a) default params (specifically shadowing locals),
+ // (b) destructuring, (c) block-scoped functions,
+ // (d) for-of (const), (e) new.target/Reflect.construct
+ var es6fullTest =
+ 'class X{constructor(){if(new.target!=String)throw 1;this.x=42}}' +
+ 'let q=Reflect.construct(X,[],String);if(q.x!=42||!(q instanceof ' +
+ 'String))throw 1;for(const a of[2,3]){if(a==2)continue;function ' +
+ 'f(z={a}){let a=0;return z.a}{function f(){return 0;}}return f()' +
+ '==3}';
+
+ return evalCheck('(()=>{"use strict";' + es6fullTest + '})()');
+ });
+ // TODO(joeltine): Remove es6-impl references for b/31340605.
+ // Consider es6-impl (widely-implemented es6 features) to be supported
+ // whenever es6 is supported. Technically es6-impl is a lower level of
+ // support than es6, but we don't have tests specifically for it.
+ addNewerLanguageTranspilationCheck('es6-impl', function() {
+ return true;
+ });
+ // ** and **= are the only new features in 'es7'
+ addNewerLanguageTranspilationCheck('es7', function() {
+ return evalCheck('2 ** 2 == 4');
+ });
+ // async functions are the only new features in 'es8'
+ addNewerLanguageTranspilationCheck('es8', function() {
+ return evalCheck('async () => 1, true');
+ });
+ return requiresTranspilation;
+};
diff --git a/chromium/third_party/ink/closure/crypt/base64.js b/chromium/third_party/ink/closure/crypt/base64.js
new file mode 100644
index 00000000000..026aa956bdb
--- /dev/null
+++ b/chromium/third_party/ink/closure/crypt/base64.js
@@ -0,0 +1,370 @@
+// Copyright 2007 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Base64 en/decoding. Not much to say here except that we
+ * work with decoded values in arrays of bytes. By "byte" I mean a number
+ * in [0, 255].
+ *
+ * @author doughtie@google.com (Gavin Doughtie)
+ * @author fschneider@google.com (Fritz Schneider)
+ */
+
+goog.provide('goog.crypt.base64');
+
+goog.require('goog.asserts');
+goog.require('goog.crypt');
+goog.require('goog.string');
+goog.require('goog.userAgent');
+goog.require('goog.userAgent.product');
+
+// Static lookup maps, lazily populated by init_()
+
+
+/**
+ * Maps bytes to characters.
+ * @type {Object}
+ * @private
+ */
+goog.crypt.base64.byteToCharMap_ = null;
+
+
+/**
+ * Maps characters to bytes. Used for normal and websafe characters.
+ * @type {Object}
+ * @private
+ */
+goog.crypt.base64.charToByteMap_ = null;
+
+
+/**
+ * Maps bytes to websafe characters.
+ * @type {Object}
+ * @private
+ */
+goog.crypt.base64.byteToCharMapWebSafe_ = null;
+
+
+/**
+ * Our default alphabet, shared between
+ * ENCODED_VALS and ENCODED_VALS_WEBSAFE
+ * @type {string}
+ */
+goog.crypt.base64.ENCODED_VALS_BASE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
+ 'abcdefghijklmnopqrstuvwxyz' +
+ '0123456789';
+
+
+/**
+ * Our default alphabet. Value 64 (=) is special; it means "nothing."
+ * @type {string}
+ */
+goog.crypt.base64.ENCODED_VALS = goog.crypt.base64.ENCODED_VALS_BASE + '+/=';
+
+
+/**
+ * Our websafe alphabet.
+ * @type {string}
+ */
+goog.crypt.base64.ENCODED_VALS_WEBSAFE =
+ goog.crypt.base64.ENCODED_VALS_BASE + '-_.';
+
+
+/**
+ * White list of implementations with known-good native atob and btoa functions.
+ * Listing these explicitly (via the ASSUME_* wrappers) benefits dead-code
+ * removal in per-browser compilations.
+ * @private {boolean}
+ */
+goog.crypt.base64.ASSUME_NATIVE_SUPPORT_ = goog.userAgent.GECKO ||
+ (goog.userAgent.WEBKIT && !goog.userAgent.product.SAFARI) ||
+ goog.userAgent.OPERA;
+
+
+/**
+ * Does this browser have a working btoa function?
+ * @private {boolean}
+ */
+goog.crypt.base64.HAS_NATIVE_ENCODE_ =
+ goog.crypt.base64.ASSUME_NATIVE_SUPPORT_ ||
+ typeof(goog.global.btoa) == 'function';
+
+
+/**
+ * Does this browser have a working atob function?
+ * We blacklist known-bad implementations:
+ * - IE (10+) added atob() but it does not tolerate whitespace on the input.
+ * @private {boolean}
+ */
+goog.crypt.base64.HAS_NATIVE_DECODE_ =
+ goog.crypt.base64.ASSUME_NATIVE_SUPPORT_ ||
+ (!goog.userAgent.product.SAFARI && !goog.userAgent.IE &&
+ typeof(goog.global.atob) == 'function');
+
+
+/**
+ * Base64-encode an array of bytes.
+ *
+ * @param {Array<number>|Uint8Array} input An array of bytes (numbers with
+ * value in [0, 255]) to encode.
+ * @param {boolean=} opt_webSafe True indicates we should use the alternative
+ * alphabet, which does not require escaping for use in URLs.
+ * @return {string} The base64 encoded string.
+ */
+goog.crypt.base64.encodeByteArray = function(input, opt_webSafe) {
+ // Assert avoids runtime dependency on goog.isArrayLike, which helps reduce
+ // size of jscompiler output, and which yields slight performance increase.
+ goog.asserts.assert(
+ goog.isArrayLike(input), 'encodeByteArray takes an array as a parameter');
+
+ goog.crypt.base64.init_();
+
+ var byteToCharMap = opt_webSafe ? goog.crypt.base64.byteToCharMapWebSafe_ :
+ goog.crypt.base64.byteToCharMap_;
+
+ var output = [];
+
+ for (var i = 0; i < input.length; i += 3) {
+ var byte1 = input[i];
+ var haveByte2 = i + 1 < input.length;
+ var byte2 = haveByte2 ? input[i + 1] : 0;
+ var haveByte3 = i + 2 < input.length;
+ var byte3 = haveByte3 ? input[i + 2] : 0;
+
+ var outByte1 = byte1 >> 2;
+ var outByte2 = ((byte1 & 0x03) << 4) | (byte2 >> 4);
+ var outByte3 = ((byte2 & 0x0F) << 2) | (byte3 >> 6);
+ var outByte4 = byte3 & 0x3F;
+
+ if (!haveByte3) {
+ outByte4 = 64;
+
+ if (!haveByte2) {
+ outByte3 = 64;
+ }
+ }
+
+ output.push(
+ byteToCharMap[outByte1], byteToCharMap[outByte2],
+ byteToCharMap[outByte3], byteToCharMap[outByte4]);
+ }
+
+ return output.join('');
+};
+
+
+/**
+ * Base64-encode a string.
+ *
+ * @param {string} input A string to encode.
+ * @param {boolean=} opt_webSafe True indicates we should use the alternative
+ * alphabet, which does not require escaping for use in URLs.
+ * @return {string} The base64 encoded string.
+ */
+goog.crypt.base64.encodeString = function(input, opt_webSafe) {
+ // Shortcut for browsers that implement
+ // a native base64 encoder in the form of "btoa/atob"
+ if (goog.crypt.base64.HAS_NATIVE_ENCODE_ && !opt_webSafe) {
+ return goog.global.btoa(input);
+ }
+ return goog.crypt.base64.encodeByteArray(
+ goog.crypt.stringToByteArray(input), opt_webSafe);
+};
+
+
+/**
+ * Base64-decode a string.
+ *
+ * @param {string} input Input to decode. Any whitespace is ignored, and the
+ * input maybe encoded with either supported alphabet (or a mix thereof).
+ * @param {boolean=} opt_webSafe True indicates we should use the alternative
+ * alphabet, which does not require escaping for use in URLs. Note that
+ * passing false may also still allow webSafe input decoding, when the
+ * fallback decoder is used on browsers without native support.
+ * @return {string} string representing the decoded value.
+ */
+goog.crypt.base64.decodeString = function(input, opt_webSafe) {
+ // Shortcut for browsers that implement
+ // a native base64 encoder in the form of "btoa/atob"
+ if (goog.crypt.base64.HAS_NATIVE_DECODE_ && !opt_webSafe) {
+ return goog.global.atob(input);
+ }
+ var output = '';
+ function pushByte(b) { output += String.fromCharCode(b); }
+
+ goog.crypt.base64.decodeStringInternal_(input, pushByte);
+
+ return output;
+};
+
+
+/**
+ * Base64-decode a string to an Array of numbers.
+ *
+ * In base-64 decoding, groups of four characters are converted into three
+ * bytes. If the encoder did not apply padding, the input length may not
+ * be a multiple of 4.
+ *
+ * In this case, the last group will have fewer than 4 characters, and
+ * padding will be inferred. If the group has one or two characters, it decodes
+ * to one byte. If the group has three characters, it decodes to two bytes.
+ *
+ * @param {string} input Input to decode. Any whitespace is ignored, and the
+ * input maybe encoded with either supported alphabet (or a mix thereof).
+ * @param {boolean=} opt_ignored Unused parameter, retained for compatibility.
+ * @return {!Array<number>} bytes representing the decoded value.
+ */
+goog.crypt.base64.decodeStringToByteArray = function(input, opt_ignored) {
+ var output = [];
+ function pushByte(b) { output.push(b); }
+
+ goog.crypt.base64.decodeStringInternal_(input, pushByte);
+
+ return output;
+};
+
+
+/**
+ * Base64-decode a string to a Uint8Array.
+ *
+ * Note that Uint8Array is not supported on older browsers, e.g. IE < 10.
+ * @see http://caniuse.com/uint8array
+ *
+ * In base-64 decoding, groups of four characters are converted into three
+ * bytes. If the encoder did not apply padding, the input length may not
+ * be a multiple of 4.
+ *
+ * In this case, the last group will have fewer than 4 characters, and
+ * padding will be inferred. If the group has one or two characters, it decodes
+ * to one byte. If the group has three characters, it decodes to two bytes.
+ *
+ * @param {string} input Input to decode. Any whitespace is ignored, and the
+ * input maybe encoded with either supported alphabet (or a mix thereof).
+ * @return {!Uint8Array} bytes representing the decoded value.
+ */
+goog.crypt.base64.decodeStringToUint8Array = function(input) {
+ goog.asserts.assert(
+ !goog.userAgent.IE || goog.userAgent.isVersionOrHigher('10'),
+ 'Browser does not support typed arrays');
+ var len = input.length;
+ // Check if there are trailing '=' as padding in the b64 string.
+ var placeholders = 0;
+ if (input[len - 2] === '=') {
+ placeholders = 2;
+ } else if (input[len - 1] === '=') {
+ placeholders = 1;
+ }
+ var output = new Uint8Array(Math.ceil(len * 3 / 4) - placeholders);
+ var outLen = 0;
+ function pushByte(b) {
+ output[outLen++] = b;
+ }
+
+ goog.crypt.base64.decodeStringInternal_(input, pushByte);
+
+ return output.subarray(0, outLen);
+};
+
+
+/**
+ * @param {string} input Input to decode.
+ * @param {function(number):void} pushByte result accumulator.
+ * @private
+ */
+goog.crypt.base64.decodeStringInternal_ = function(input, pushByte) {
+ goog.crypt.base64.init_();
+
+ var nextCharIndex = 0;
+ /**
+ * @param {number} default_val Used for end-of-input.
+ * @return {number} The next 6-bit value, or the default for end-of-input.
+ */
+ function getByte(default_val) {
+ while (nextCharIndex < input.length) {
+ var ch = input.charAt(nextCharIndex++);
+ var b = goog.crypt.base64.charToByteMap_[ch];
+ if (b != null) {
+ return b; // Common case: decoded the char.
+ }
+ if (!goog.string.isEmptyOrWhitespace(ch)) {
+ throw new Error('Unknown base64 encoding at char: ' + ch);
+ }
+ // We encountered whitespace: loop around to the next input char.
+ }
+ return default_val; // No more input remaining.
+ }
+
+ while (true) {
+ var byte1 = getByte(-1);
+ var byte2 = getByte(0);
+ var byte3 = getByte(64);
+ var byte4 = getByte(64);
+
+ // The common case is that all four bytes are present, so if we have byte4
+ // we can skip over the truncated input special case handling.
+ if (byte4 === 64) {
+ if (byte1 === -1) {
+ return; // Terminal case: no input left to decode.
+ }
+ // Here we know an intermediate number of bytes are missing.
+ // The defaults for byte2, byte3 and byte4 apply the inferred padding
+ // rules per the public API documentation. i.e: 1 byte
+ // missing should yield 2 bytes of output, but 2 or 3 missing bytes yield
+ // a single byte of output. (Recall that 64 corresponds the padding char).
+ }
+
+ var outByte1 = (byte1 << 2) | (byte2 >> 4);
+ pushByte(outByte1);
+
+ if (byte3 != 64) {
+ var outByte2 = ((byte2 << 4) & 0xF0) | (byte3 >> 2);
+ pushByte(outByte2);
+
+ if (byte4 != 64) {
+ var outByte3 = ((byte3 << 6) & 0xC0) | byte4;
+ pushByte(outByte3);
+ }
+ }
+ }
+};
+
+
+/**
+ * Lazy static initialization function. Called before
+ * accessing any of the static map variables.
+ * @private
+ */
+goog.crypt.base64.init_ = function() {
+ if (!goog.crypt.base64.byteToCharMap_) {
+ goog.crypt.base64.byteToCharMap_ = {};
+ goog.crypt.base64.charToByteMap_ = {};
+ goog.crypt.base64.byteToCharMapWebSafe_ = {};
+
+ // We want quick mappings back and forth, so we precompute two maps.
+ for (var i = 0; i < goog.crypt.base64.ENCODED_VALS.length; i++) {
+ goog.crypt.base64.byteToCharMap_[i] =
+ goog.crypt.base64.ENCODED_VALS.charAt(i);
+ goog.crypt.base64.charToByteMap_[goog.crypt.base64.byteToCharMap_[i]] = i;
+ goog.crypt.base64.byteToCharMapWebSafe_[i] =
+ goog.crypt.base64.ENCODED_VALS_WEBSAFE.charAt(i);
+
+ // Be forgiving when decoding and correctly decode both encodings.
+ if (i >= goog.crypt.base64.ENCODED_VALS_BASE.length) {
+ goog.crypt.base64
+ .charToByteMap_[goog.crypt.base64.ENCODED_VALS_WEBSAFE.charAt(i)] =
+ i;
+ }
+ }
+ }
+};
diff --git a/chromium/third_party/ink/closure/crypt/crypt.js b/chromium/third_party/ink/closure/crypt/crypt.js
new file mode 100644
index 00000000000..a0e4f028308
--- /dev/null
+++ b/chromium/third_party/ink/closure/crypt/crypt.js
@@ -0,0 +1,196 @@
+// Copyright 2008 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Namespace with crypto related helper functions.
+ * @author pupius@google.com (Daniel Pupius)
+ */
+
+goog.provide('goog.crypt');
+
+goog.require('goog.array');
+goog.require('goog.asserts');
+
+
+/**
+ * Turns a string into an array of bytes; a "byte" being a JS number in the
+ * range 0-255. Multi-byte characters are written as little-endian.
+ * @param {string} str String value to arrify.
+ * @return {!Array<number>} Array of numbers corresponding to the
+ * UCS character codes of each character in str.
+ */
+goog.crypt.stringToByteArray = function(str) {
+ var output = [], p = 0;
+ for (var i = 0; i < str.length; i++) {
+ var c = str.charCodeAt(i);
+ // NOTE: c <= 0xffff since JavaScript strings are UTF-16.
+ if (c > 0xff) {
+ output[p++] = c & 0xff;
+ c >>= 8;
+ }
+ output[p++] = c;
+ }
+ return output;
+};
+
+
+/**
+ * Turns an array of numbers into the string given by the concatenation of the
+ * characters to which the numbers correspond.
+ * @param {!Uint8Array|!Array<number>} bytes Array of numbers representing
+ * characters.
+ * @return {string} Stringification of the array.
+ */
+goog.crypt.byteArrayToString = function(bytes) {
+ var CHUNK_SIZE = 8192;
+
+ // Special-case the simple case for speed's sake.
+ if (bytes.length <= CHUNK_SIZE) {
+ return String.fromCharCode.apply(null, bytes);
+ }
+
+ // The remaining logic splits conversion by chunks since
+ // Function#apply() has a maximum parameter count.
+ // See discussion: http://goo.gl/LrWmZ9
+
+ var str = '';
+ for (var i = 0; i < bytes.length; i += CHUNK_SIZE) {
+ var chunk = goog.array.slice(bytes, i, i + CHUNK_SIZE);
+ str += String.fromCharCode.apply(null, chunk);
+ }
+ return str;
+};
+
+
+/**
+ * Turns an array of numbers into the hex string given by the concatenation of
+ * the hex values to which the numbers correspond.
+ * @param {Uint8Array|Array<number>} array Array of numbers representing
+ * characters.
+ * @return {string} Hex string.
+ */
+goog.crypt.byteArrayToHex = function(array) {
+ return goog.array
+ .map(
+ array,
+ function(numByte) {
+ var hexByte = numByte.toString(16);
+ return hexByte.length > 1 ? hexByte : '0' + hexByte;
+ })
+ .join('');
+};
+
+
+/**
+ * Converts a hex string into an integer array.
+ * @param {string} hexString Hex string of 16-bit integers (two characters
+ * per integer).
+ * @return {!Array<number>} Array of {0,255} integers for the given string.
+ */
+goog.crypt.hexToByteArray = function(hexString) {
+ goog.asserts.assert(
+ hexString.length % 2 == 0, 'Key string length must be multiple of 2');
+ var arr = [];
+ for (var i = 0; i < hexString.length; i += 2) {
+ arr.push(parseInt(hexString.substring(i, i + 2), 16));
+ }
+ return arr;
+};
+
+
+/**
+ * Converts a JS string to a UTF-8 "byte" array.
+ * @param {string} str 16-bit unicode string.
+ * @return {!Array<number>} UTF-8 byte array.
+ */
+goog.crypt.stringToUtf8ByteArray = function(str) {
+ // TODO(pupius): Use native implementations if/when available
+ var out = [], p = 0;
+ for (var i = 0; i < str.length; i++) {
+ var c = str.charCodeAt(i);
+ if (c < 128) {
+ out[p++] = c;
+ } else if (c < 2048) {
+ out[p++] = (c >> 6) | 192;
+ out[p++] = (c & 63) | 128;
+ } else if (
+ ((c & 0xFC00) == 0xD800) && (i + 1) < str.length &&
+ ((str.charCodeAt(i + 1) & 0xFC00) == 0xDC00)) {
+ // Surrogate Pair
+ c = 0x10000 + ((c & 0x03FF) << 10) + (str.charCodeAt(++i) & 0x03FF);
+ out[p++] = (c >> 18) | 240;
+ out[p++] = ((c >> 12) & 63) | 128;
+ out[p++] = ((c >> 6) & 63) | 128;
+ out[p++] = (c & 63) | 128;
+ } else {
+ out[p++] = (c >> 12) | 224;
+ out[p++] = ((c >> 6) & 63) | 128;
+ out[p++] = (c & 63) | 128;
+ }
+ }
+ return out;
+};
+
+
+/**
+ * Converts a UTF-8 byte array to JavaScript's 16-bit Unicode.
+ * @param {Uint8Array|Array<number>} bytes UTF-8 byte array.
+ * @return {string} 16-bit Unicode string.
+ */
+goog.crypt.utf8ByteArrayToString = function(bytes) {
+ // TODO(pupius): Use native implementations if/when available
+ var out = [], pos = 0, c = 0;
+ while (pos < bytes.length) {
+ var c1 = bytes[pos++];
+ if (c1 < 128) {
+ out[c++] = String.fromCharCode(c1);
+ } else if (c1 > 191 && c1 < 224) {
+ var c2 = bytes[pos++];
+ out[c++] = String.fromCharCode((c1 & 31) << 6 | c2 & 63);
+ } else if (c1 > 239 && c1 < 365) {
+ // Surrogate Pair
+ var c2 = bytes[pos++];
+ var c3 = bytes[pos++];
+ var c4 = bytes[pos++];
+ var u = ((c1 & 7) << 18 | (c2 & 63) << 12 | (c3 & 63) << 6 | c4 & 63) -
+ 0x10000;
+ out[c++] = String.fromCharCode(0xD800 + (u >> 10));
+ out[c++] = String.fromCharCode(0xDC00 + (u & 1023));
+ } else {
+ var c2 = bytes[pos++];
+ var c3 = bytes[pos++];
+ out[c++] =
+ String.fromCharCode((c1 & 15) << 12 | (c2 & 63) << 6 | c3 & 63);
+ }
+ }
+ return out.join('');
+};
+
+
+/**
+ * XOR two byte arrays.
+ * @param {!Uint8Array|!Int8Array|!Array<number>} bytes1 Byte array 1.
+ * @param {!Uint8Array|!Int8Array|!Array<number>} bytes2 Byte array 2.
+ * @return {!Array<number>} Resulting XOR of the two byte arrays.
+ */
+goog.crypt.xorByteArray = function(bytes1, bytes2) {
+ goog.asserts.assert(
+ bytes1.length == bytes2.length, 'XOR array lengths must match');
+
+ var result = [];
+ for (var i = 0; i < bytes1.length; i++) {
+ result.push(bytes1[i] ^ bytes2[i]);
+ }
+ return result;
+};
diff --git a/chromium/third_party/ink/closure/debug/debug.js b/chromium/third_party/ink/closure/debug/debug.js
new file mode 100644
index 00000000000..92d19455bb8
--- /dev/null
+++ b/chromium/third_party/ink/closure/debug/debug.js
@@ -0,0 +1,666 @@
+// Copyright 2006 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Logging and debugging utilities.
+ *
+ * @author pupius@google.com (Daniel Pupius)
+ * @see ../demos/debug.html
+ */
+
+goog.provide('goog.debug');
+
+goog.require('goog.array');
+goog.require('goog.debug.errorcontext');
+goog.require('goog.userAgent');
+
+
+/** @define {boolean} Whether logging should be enabled. */
+goog.define('goog.debug.LOGGING_ENABLED', goog.DEBUG);
+
+
+/** @define {boolean} Whether to force "sloppy" stack building. */
+goog.define('goog.debug.FORCE_SLOPPY_STACKS', false);
+
+
+/**
+ * Catches onerror events fired by windows and similar objects.
+ * @param {function(Object)} logFunc The function to call with the error
+ * information.
+ * @param {boolean=} opt_cancel Whether to stop the error from reaching the
+ * browser.
+ * @param {Object=} opt_target Object that fires onerror events.
+ */
+goog.debug.catchErrors = function(logFunc, opt_cancel, opt_target) {
+ var target = opt_target || goog.global;
+ var oldErrorHandler = target.onerror;
+ var retVal = !!opt_cancel;
+
+ // Chrome interprets onerror return value backwards (http://crbug.com/92062)
+ // until it was fixed in webkit revision r94061 (Webkit 535.3). This
+ // workaround still needs to be skipped in Safari after the webkit change
+ // gets pushed out in Safari.
+ // See https://bugs.webkit.org/show_bug.cgi?id=67119
+ if (goog.userAgent.WEBKIT && !goog.userAgent.isVersionOrHigher('535.3')) {
+ retVal = !retVal;
+ }
+
+ /**
+ * New onerror handler for this target. This onerror handler follows the spec
+ * according to
+ * http://www.whatwg.org/specs/web-apps/current-work/#runtime-script-errors
+ * The spec was changed in August 2013 to support receiving column information
+ * and an error object for all scripts on the same origin or cross origin
+ * scripts with the proper headers. See
+ * https://mikewest.org/2013/08/debugging-runtime-errors-with-window-onerror
+ *
+ * @param {string} message The error message. For cross-origin errors, this
+ * will be scrubbed to just "Script error.". For new browsers that have
+ * updated to follow the latest spec, errors that come from origins that
+ * have proper cross origin headers will not be scrubbed.
+ * @param {string} url The URL of the script that caused the error. The URL
+ * will be scrubbed to "" for cross origin scripts unless the script has
+ * proper cross origin headers and the browser has updated to the latest
+ * spec.
+ * @param {number} line The line number in the script that the error
+ * occurred on.
+ * @param {number=} opt_col The optional column number that the error
+ * occurred on. Only browsers that have updated to the latest spec will
+ * include this.
+ * @param {Error=} opt_error The optional actual error object for this
+ * error that should include the stack. Only browsers that have updated
+ * to the latest spec will inlude this parameter.
+ * @return {boolean} Whether to prevent the error from reaching the browser.
+ */
+ target.onerror = function(message, url, line, opt_col, opt_error) {
+ if (oldErrorHandler) {
+ oldErrorHandler(message, url, line, opt_col, opt_error);
+ }
+ logFunc({
+ message: message,
+ fileName: url,
+ line: line,
+ lineNumber: line,
+ col: opt_col,
+ error: opt_error
+ });
+ return retVal;
+ };
+};
+
+
+/**
+ * Creates a string representing an object and all its properties.
+ * @param {Object|null|undefined} obj Object to expose.
+ * @param {boolean=} opt_showFn Show the functions as well as the properties,
+ * default is false.
+ * @return {string} The string representation of {@code obj}.
+ */
+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');
+};
+
+
+/**
+ * Creates a string representing a given primitive or object, and for an
+ * object, all its properties and nested objects. NOTE: The output will include
+ * Uids on all objects that were exposed. Any added Uids will be removed before
+ * returning.
+ * @param {*} obj Object to expose.
+ * @param {boolean=} opt_showFn Also show properties that are functions (by
+ * default, functions are omitted).
+ * @return {string} A string representation of {@code obj}.
+ */
+goog.debug.deepExpose = function(obj, opt_showFn) {
+ var str = [];
+
+ // Track any objects where deepExpose added a Uid, so they can be cleaned up
+ // before return. We do this globally, rather than only on ancestors so that
+ // if the same object appears in the output, you can see it.
+ var uidsToCleanup = [];
+ var ancestorUids = {};
+
+ 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)) {
+ // Add a Uid if needed. The struct calls implicitly adds them.
+ if (!goog.hasUid(obj)) {
+ uidsToCleanup.push(obj);
+ }
+ var uid = goog.getUid(obj);
+ if (ancestorUids[uid]) {
+ str.push('*** reference loop detected (id=' + uid + ') ***');
+ } else {
+ ancestorUids[uid] = true;
+ 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 + '}');
+ delete ancestorUids[uid];
+ }
+ } else {
+ str.push(obj);
+ }
+ } catch (e) {
+ str.push('*** ' + e + ' ***');
+ }
+ };
+
+ helper(obj, '');
+
+ // Cleanup any Uids that were added by the deepExpose.
+ for (var i = 0; i < uidsToCleanup.length; i++) {
+ goog.removeUid(uidsToCleanup[i]);
+ }
+
+ return str.join('');
+};
+
+
+/**
+ * Recursively outputs a nested array as a string.
+ * @param {Array<?>} arr The array.
+ * @return {string} String representing nested array.
+ */
+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(', ') + ' ]';
+};
+
+
+/**
+ * Normalizes the error/exception object between browsers.
+ * @param {*} err Raw error object.
+ * @return {!{
+ * message: (?|undefined),
+ * name: (?|undefined),
+ * lineNumber: (?|undefined),
+ * fileName: (?|undefined),
+ * stack: (?|undefined)
+ * }} Normalized error object.
+ */
+goog.debug.normalizeErrorObject = function(err) {
+ var href = goog.getObjectByName('window.location.href');
+ if (goog.isString(err)) {
+ return {
+ 'message': err,
+ 'name': 'Unknown error',
+ 'lineNumber': 'Not available',
+ 'fileName': href,
+ 'stack': 'Not available'
+ };
+ }
+
+ var lineNumber, fileName;
+ var threwError = false;
+
+ try {
+ lineNumber = err.lineNumber || err.line || 'Not available';
+ } catch (e) {
+ // Firefox 2 sometimes throws an error when accessing 'lineNumber':
+ // Message: Permission denied to get property UnnamedClass.lineNumber
+ lineNumber = 'Not available';
+ threwError = true;
+ }
+
+ try {
+ fileName = err.fileName || err.filename || err.sourceURL ||
+ // $googDebugFname may be set before a call to eval to set the filename
+ // that the eval is supposed to present.
+ goog.global['$googDebugFname'] || href;
+ } catch (e) {
+ // Firefox 2 may also throw an error when accessing 'filename'.
+ fileName = 'Not available';
+ threwError = true;
+ }
+
+ // The IE Error object contains only the name and the message.
+ // The Safari Error object uses the line and sourceURL fields.
+ if (threwError || !err.lineNumber || !err.fileName || !err.stack ||
+ !err.message || !err.name) {
+ return {
+ 'message': err.message || 'Not available',
+ 'name': err.name || 'UnknownError',
+ 'lineNumber': lineNumber,
+ 'fileName': fileName,
+ 'stack': err.stack || 'Not available'
+ };
+ }
+
+ // Standards error object
+ // Typed !Object. Should be a subtype of the return type, but it's not.
+ return /** @type {?} */ (err);
+};
+
+
+/**
+ * Converts an object to an Error using the object's toString if it's not
+ * already an Error, adds a stacktrace if there isn't one, and optionally adds
+ * an extra message.
+ * @param {*} err The original thrown error, object, or string.
+ * @param {string=} opt_message optional additional message to add to the
+ * error.
+ * @return {!Error} If err is an Error, it is enhanced and returned. Otherwise,
+ * it is converted to an Error which is enhanced and returned.
+ */
+goog.debug.enhanceError = function(err, opt_message) {
+ var error;
+ if (!(err instanceof Error)) {
+ error = Error(err);
+ if (Error.captureStackTrace) {
+ // Trim this function off the call stack, if we can.
+ Error.captureStackTrace(error, goog.debug.enhanceError);
+ }
+ } else {
+ error = err;
+ }
+
+ if (!error.stack) {
+ error.stack = goog.debug.getStacktrace(goog.debug.enhanceError);
+ }
+ if (opt_message) {
+ // find the first unoccupied 'messageX' property
+ var x = 0;
+ while (error['message' + x]) {
+ ++x;
+ }
+ error['message' + x] = String(opt_message);
+ }
+ return error;
+};
+
+
+/**
+ * Converts an object to an Error using the object's toString if it's not
+ * already an Error, adds a stacktrace if there isn't one, and optionally adds
+ * context to the Error, which is reported by the closure error reporter.
+ * @param {*} err The original thrown error, object, or string.
+ * @param {!Object<string, string>=} opt_context Key-value context to add to the
+ * Error.
+ * @return {!Error} If err is an Error, it is enhanced and returned. Otherwise,
+ * it is converted to an Error which is enhanced and returned.
+ */
+goog.debug.enhanceErrorWithContext = function(err, opt_context) {
+ var error = goog.debug.enhanceError(err);
+ if (opt_context) {
+ for (var key in opt_context) {
+ goog.debug.errorcontext.addErrorContext(error, key, opt_context[key]);
+ }
+ }
+ return error;
+};
+
+
+/**
+ * Gets the current stack trace. Simple and iterative - doesn't worry about
+ * catching circular references or getting the args.
+ * @param {number=} opt_depth Optional maximum depth to trace back to.
+ * @return {string} A string with the function names of all functions in the
+ * stack, separated by \n.
+ * @suppress {es5Strict}
+ */
+goog.debug.getStacktraceSimple = function(opt_depth) {
+ if (!goog.debug.FORCE_SLOPPY_STACKS) {
+ var stack = goog.debug.getNativeStackTrace_(goog.debug.getStacktraceSimple);
+ if (stack) {
+ return stack;
+ }
+ // NOTE: browsers that have strict mode support also have native "stack"
+ // properties. Fall-through for legacy browser support.
+ }
+
+ 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('');
+};
+
+
+/**
+ * Max length of stack to try and output
+ * @type {number}
+ */
+goog.debug.MAX_STACK_DEPTH = 50;
+
+
+/**
+ * @param {Function} fn The function to start getting the trace from.
+ * @return {?string}
+ * @private
+ */
+goog.debug.getNativeStackTrace_ = function(fn) {
+ var tempErr = new Error();
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(tempErr, fn);
+ return String(tempErr.stack);
+ } else {
+ // IE10, only adds stack traces when an exception is thrown.
+ try {
+ throw tempErr;
+ } catch (e) {
+ tempErr = e;
+ }
+ var stack = tempErr.stack;
+ if (stack) {
+ return String(stack);
+ }
+ }
+ return null;
+};
+
+
+/**
+ * Gets the current stack trace, either starting from the caller or starting
+ * from a specified function that's currently on the call stack.
+ * @param {?Function=} fn If provided, when collecting the stack trace all
+ * frames above the topmost call to this function, including that call,
+ * will be left out of the stack trace.
+ * @return {string} Stack trace.
+ * @suppress {es5Strict}
+ */
+goog.debug.getStacktrace = function(fn) {
+ var stack;
+ if (!goog.debug.FORCE_SLOPPY_STACKS) {
+ // Try to get the stack trace from the environment if it is available.
+ var contextFn = fn || goog.debug.getStacktrace;
+ stack = goog.debug.getNativeStackTrace_(contextFn);
+ }
+ if (!stack) {
+ // NOTE: browsers that have strict mode support also have native "stack"
+ // properties. This function will throw in strict mode.
+ stack = goog.debug.getStacktraceHelper_(fn || arguments.callee.caller, []);
+ }
+ return stack;
+};
+
+
+/**
+ * Private helper for getStacktrace().
+ * @param {?Function} fn If provided, when collecting the stack trace all
+ * frames above the topmost call to this function, including that call,
+ * will be left out of the stack trace.
+ * @param {Array<!Function>} visited List of functions visited so far.
+ * @return {string} Stack trace starting from function fn.
+ * @suppress {es5Strict}
+ * @private
+ */
+goog.debug.getStacktraceHelper_ = function(fn, visited) {
+ var sb = [];
+
+ // Circular reference, certain functions like bind seem to cause a recursive
+ // loop so we need to catch circular references
+ if (goog.array.contains(visited, fn)) {
+ sb.push('[...circular reference...]');
+
+ // Traverse the call stack until function not found or max depth is reached
+ } else if (fn && visited.length < goog.debug.MAX_STACK_DEPTH) {
+ sb.push(goog.debug.getFunctionName(fn) + '(');
+ var args = fn.arguments;
+ // Args may be null for some special functions such as host objects or eval.
+ for (var i = 0; args && 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('');
+};
+
+
+/**
+ * Set a custom function name resolver.
+ * @param {function(Function): string} resolver Resolves functions to their
+ * names.
+ */
+goog.debug.setFunctionResolver = function(resolver) {
+ goog.debug.fnNameResolver_ = resolver;
+};
+
+
+/**
+ * Gets a function name
+ * @param {Function} fn Function to get name of.
+ * @return {string} Function's name.
+ */
+goog.debug.getFunctionName = function(fn) {
+ if (goog.debug.fnNameCache_[fn]) {
+ return goog.debug.fnNameCache_[fn];
+ }
+ if (goog.debug.fnNameResolver_) {
+ var name = goog.debug.fnNameResolver_(fn);
+ if (name) {
+ goog.debug.fnNameCache_[fn] = name;
+ return name;
+ }
+ }
+
+ // Heuristically determine function name based on code.
+ 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];
+};
+
+
+/**
+ * Makes whitespace visible by replacing it with printable characters.
+ * This is useful in finding diffrences between the expected and the actual
+ * output strings of a testcase.
+ * @param {string} string whose whitespace needs to be made visible.
+ * @return {string} string whose whitespace is made visible.
+ */
+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]');
+};
+
+
+/**
+ * Returns the type of a value. If a constructor is passed, and a suitable
+ * string cannot be found, 'unknown type name' will be returned.
+ *
+ * <p>Forked rather than moved from {@link goog.asserts.getType_}
+ * to avoid adding a dependency to goog.asserts.
+ * @param {*} value A constructor, object, or primitive.
+ * @return {string} The best display name for the value, or 'unknown type name'.
+ */
+goog.debug.runtimeType = function(value) {
+ if (value instanceof Function) {
+ return value.displayName || value.name || 'unknown type name';
+ } else if (value instanceof Object) {
+ return value.constructor.displayName || value.constructor.name ||
+ Object.prototype.toString.call(value);
+ } else {
+ return value === null ? 'null' : typeof value;
+ }
+};
+
+
+/**
+ * Hash map for storing function names that have already been looked up.
+ * @type {Object}
+ * @private
+ */
+goog.debug.fnNameCache_ = {};
+
+
+/**
+ * Resolves functions to their names. Resolved function names will be cached.
+ * @type {function(Function):string}
+ * @private
+ */
+goog.debug.fnNameResolver_;
+
+
+/**
+ * Private internal function to support goog.debug.freeze.
+ * @param {T} arg
+ * @return {T}
+ * @template T
+ * @private
+ */
+goog.debug.freezeInternal_ = goog.DEBUG && Object.freeze || function(arg) {
+ return arg;
+};
+
+
+/**
+ * Freezes the given object, but only in debug mode (and in browsers that
+ * support it). Note that this is a shallow freeze, so for deeply nested
+ * objects it must be called at every level to ensure deep immutability.
+ * @param {T} arg
+ * @return {T}
+ * @template T
+ */
+goog.debug.freeze = function(arg) {
+ // NOTE: this compiles to nothing, but hides the possible side effect of
+ // freezeInternal_ from the compiler so that the entire call can be
+ // removed if the result is not used.
+ return {
+ valueOf: function() {
+ return goog.debug.freezeInternal_(arg);
+ }
+ }.valueOf();
+};
diff --git a/chromium/third_party/ink/closure/debug/entrypointregistry.js b/chromium/third_party/ink/closure/debug/entrypointregistry.js
new file mode 100644
index 00000000000..336e1468bdd
--- /dev/null
+++ b/chromium/third_party/ink/closure/debug/entrypointregistry.js
@@ -0,0 +1,159 @@
+// Copyright 2010 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview A global registry for entry points into a program,
+ * so that they can be instrumented. Each module should register their
+ * entry points with this registry. Designed to be compiled out
+ * if no instrumentation is requested.
+ *
+ * Entry points may be registered before or after a call to
+ * goog.debug.entryPointRegistry.monitorAll. If an entry point is registered
+ * later, the existing monitor will instrument the new entry point.
+ *
+ * @author nicksantos@google.com (Nick Santos)
+ */
+
+goog.provide('goog.debug.EntryPointMonitor');
+goog.provide('goog.debug.entryPointRegistry');
+
+goog.require('goog.asserts');
+
+
+
+/**
+ * @interface
+ */
+goog.debug.EntryPointMonitor = function() {};
+
+
+/**
+ * Instruments a function.
+ *
+ * @param {!Function} fn A function to instrument.
+ * @return {!Function} The instrumented function.
+ */
+goog.debug.EntryPointMonitor.prototype.wrap;
+
+
+/**
+ * Try to remove an instrumentation wrapper created by this monitor.
+ * If the function passed to unwrap is not a wrapper created by this
+ * monitor, then we will do nothing.
+ *
+ * Notice that some wrappers may not be unwrappable. For example, if other
+ * monitors have applied their own wrappers, then it will be impossible to
+ * unwrap them because their wrappers will have captured our wrapper.
+ *
+ * So it is important that entry points are unwrapped in the reverse
+ * order that they were wrapped.
+ *
+ * @param {!Function} fn A function to unwrap.
+ * @return {!Function} The unwrapped function, or {@code fn} if it was not
+ * a wrapped function created by this monitor.
+ */
+goog.debug.EntryPointMonitor.prototype.unwrap;
+
+
+/**
+ * An array of entry point callbacks.
+ * @type {!Array<function(!Function)>}
+ * @private
+ */
+goog.debug.entryPointRegistry.refList_ = [];
+
+
+/**
+ * Monitors that should wrap all the entry points.
+ * @type {!Array<!goog.debug.EntryPointMonitor>}
+ * @private
+ */
+goog.debug.entryPointRegistry.monitors_ = [];
+
+
+/**
+ * Whether goog.debug.entryPointRegistry.monitorAll has ever been called.
+ * Checking this allows the compiler to optimize out the registrations.
+ * @type {boolean}
+ * @private
+ */
+goog.debug.entryPointRegistry.monitorsMayExist_ = false;
+
+
+/**
+ * Register an entry point with this module.
+ *
+ * The entry point will be instrumented when a monitor is passed to
+ * goog.debug.entryPointRegistry.monitorAll. If this has already occurred, the
+ * entry point is instrumented immediately.
+ *
+ * @param {function(!Function)} callback A callback function which is called
+ * with a transforming function to instrument the entry point. The callback
+ * is responsible for wrapping the relevant entry point with the
+ * transforming function.
+ */
+goog.debug.entryPointRegistry.register = function(callback) {
+ // Don't use push(), so that this can be compiled out.
+ goog.debug.entryPointRegistry
+ .refList_[goog.debug.entryPointRegistry.refList_.length] = callback;
+ // If no one calls monitorAll, this can be compiled out.
+ if (goog.debug.entryPointRegistry.monitorsMayExist_) {
+ var monitors = goog.debug.entryPointRegistry.monitors_;
+ for (var i = 0; i < monitors.length; i++) {
+ callback(goog.bind(monitors[i].wrap, monitors[i]));
+ }
+ }
+};
+
+
+/**
+ * Configures a monitor to wrap all entry points.
+ *
+ * Entry points that have already been registered are immediately wrapped by
+ * the monitor. When an entry point is registered in the future, it will also
+ * be wrapped by the monitor when it is registered.
+ *
+ * @param {!goog.debug.EntryPointMonitor} monitor An entry point monitor.
+ */
+goog.debug.entryPointRegistry.monitorAll = function(monitor) {
+ goog.debug.entryPointRegistry.monitorsMayExist_ = true;
+ var transformer = goog.bind(monitor.wrap, monitor);
+ for (var i = 0; i < goog.debug.entryPointRegistry.refList_.length; i++) {
+ goog.debug.entryPointRegistry.refList_[i](transformer);
+ }
+ goog.debug.entryPointRegistry.monitors_.push(monitor);
+};
+
+
+/**
+ * Try to unmonitor all the entry points that have already been registered. If
+ * an entry point is registered in the future, it will not be wrapped by the
+ * monitor when it is registered. Note that this may fail if the entry points
+ * have additional wrapping.
+ *
+ * @param {!goog.debug.EntryPointMonitor} monitor The last monitor to wrap
+ * the entry points.
+ * @throws {Error} If the monitor is not the most recently configured monitor.
+ */
+goog.debug.entryPointRegistry.unmonitorAllIfPossible = function(monitor) {
+ var monitors = goog.debug.entryPointRegistry.monitors_;
+ goog.asserts.assert(
+ monitor == monitors[monitors.length - 1],
+ 'Only the most recent monitor can be unwrapped.');
+ var transformer = goog.bind(monitor.unwrap, monitor);
+ for (var i = 0; i < goog.debug.entryPointRegistry.refList_.length; i++) {
+ goog.debug.entryPointRegistry.refList_[i](transformer);
+ }
+ monitors.length--;
+};
diff --git a/chromium/third_party/ink/closure/debug/error.js b/chromium/third_party/ink/closure/debug/error.js
new file mode 100644
index 00000000000..2099b56997a
--- /dev/null
+++ b/chromium/third_party/ink/closure/debug/error.js
@@ -0,0 +1,66 @@
+// Copyright 2009 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Provides a base class for custom Error objects such that the
+ * stack is correctly maintained.
+ *
+ * You should never need to throw goog.debug.Error(msg) directly, Error(msg) is
+ * sufficient.
+ *
+ * @author pupius@google.com (Daniel Pupius)
+ */
+
+goog.provide('goog.debug.Error');
+
+
+
+/**
+ * Base class for custom error objects.
+ * @param {*=} opt_msg The message associated with the error.
+ * @constructor
+ * @extends {Error}
+ */
+goog.debug.Error = function(opt_msg) {
+
+ // Attempt to ensure there is a stack trace.
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this, goog.debug.Error);
+ } else {
+ var stack = new Error().stack;
+ if (stack) {
+ /** @override */
+ this.stack = stack;
+ }
+ }
+
+ if (opt_msg) {
+ /** @override */
+ this.message = String(opt_msg);
+ }
+
+ /**
+ * Whether to report this error to the server. Setting this to false will
+ * cause the error reporter to not report the error back to the server,
+ * which can be useful if the client knows that the error has already been
+ * logged on the server.
+ * @type {boolean}
+ */
+ this.reportErrorToServer = true;
+};
+goog.inherits(goog.debug.Error, Error);
+
+
+/** @override */
+goog.debug.Error.prototype.name = 'CustomError';
diff --git a/chromium/third_party/ink/closure/debug/errorcontext.js b/chromium/third_party/ink/closure/debug/errorcontext.js
new file mode 100644
index 00000000000..683454a3fd6
--- /dev/null
+++ b/chromium/third_party/ink/closure/debug/errorcontext.js
@@ -0,0 +1,49 @@
+// Copyright 2017 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Provides methods dealing with context on error objects.
+ */
+
+goog.provide('goog.debug.errorcontext');
+
+
+/**
+ * Adds key-value context to the error.
+ * @param {!Error} err The error to add context to.
+ * @param {string} contextKey Key for the context to be added.
+ * @param {string} contextValue Value for the context to be added.
+ */
+goog.debug.errorcontext.addErrorContext = function(
+ err, contextKey, contextValue) {
+ if (!err[goog.debug.errorcontext.CONTEXT_KEY_]) {
+ err[goog.debug.errorcontext.CONTEXT_KEY_] = {};
+ }
+ err[goog.debug.errorcontext.CONTEXT_KEY_][contextKey] = contextValue;
+};
+
+
+/**
+ * @param {!Error} err The error to get context from.
+ * @return {!Object<string, string>} The context of the provided error.
+ */
+goog.debug.errorcontext.getErrorContext = function(err) {
+ return err[goog.debug.errorcontext.CONTEXT_KEY_] || {};
+};
+
+
+// TODO(aaronsn): convert this to a Symbol once goog.debug.ErrorReporter is
+// able to use ES6.
+/** @private @const {string} */
+goog.debug.errorcontext.CONTEXT_KEY_ = '__closure__error__context__984382';
diff --git a/chromium/third_party/ink/closure/disposable/disposable.js b/chromium/third_party/ink/closure/disposable/disposable.js
new file mode 100644
index 00000000000..b3e81385745
--- /dev/null
+++ b/chromium/third_party/ink/closure/disposable/disposable.js
@@ -0,0 +1,305 @@
+// Copyright 2005 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Implements the disposable interface. The dispose method is used
+ * to clean up references and resources.
+ * @author arv@google.com (Erik Arvidsson)
+ */
+
+
+goog.provide('goog.Disposable');
+goog.provide('goog.dispose');
+goog.provide('goog.disposeAll');
+
+goog.require('goog.disposable.IDisposable');
+
+
+
+/**
+ * Class that provides the basic implementation for disposable objects. If your
+ * class holds one or more references to COM objects, DOM nodes, or other
+ * disposable objects, it should extend this class or implement the disposable
+ * interface (defined in goog.disposable.IDisposable).
+ * @constructor
+ * @implements {goog.disposable.IDisposable}
+ */
+goog.Disposable = function() {
+ /**
+ * If monitoring the goog.Disposable instances is enabled, stores the creation
+ * stack trace of the Disposable instance.
+ * @type {string|undefined}
+ */
+ this.creationStack;
+
+ if (goog.Disposable.MONITORING_MODE != goog.Disposable.MonitoringMode.OFF) {
+ if (goog.Disposable.INCLUDE_STACK_ON_CREATION) {
+ this.creationStack = new Error().stack;
+ }
+ goog.Disposable.instances_[goog.getUid(this)] = this;
+ }
+ // Support sealing
+ this.disposed_ = this.disposed_;
+ this.onDisposeCallbacks_ = this.onDisposeCallbacks_;
+};
+
+
+/**
+ * @enum {number} Different monitoring modes for Disposable.
+ */
+goog.Disposable.MonitoringMode = {
+ /**
+ * No monitoring.
+ */
+ OFF: 0,
+ /**
+ * Creating and disposing the goog.Disposable instances is monitored. All
+ * disposable objects need to call the {@code goog.Disposable} base
+ * constructor. The PERMANENT mode must be switched on before creating any
+ * goog.Disposable instances.
+ */
+ PERMANENT: 1,
+ /**
+ * INTERACTIVE mode can be switched on and off on the fly without producing
+ * errors. It also doesn't warn if the disposable objects don't call the
+ * {@code goog.Disposable} base constructor.
+ */
+ INTERACTIVE: 2
+};
+
+
+/**
+ * @define {number} The monitoring mode of the goog.Disposable
+ * instances. Default is OFF. Switching on the monitoring is only
+ * recommended for debugging because it has a significant impact on
+ * performance and memory usage. If switched off, the monitoring code
+ * compiles down to 0 bytes.
+ */
+goog.define('goog.Disposable.MONITORING_MODE', 0);
+
+
+/**
+ * @define {boolean} Whether to attach creation stack to each created disposable
+ * instance; This is only relevant for when MonitoringMode != OFF.
+ */
+goog.define('goog.Disposable.INCLUDE_STACK_ON_CREATION', true);
+
+
+/**
+ * Maps the unique ID of every undisposed {@code goog.Disposable} object to
+ * the object itself.
+ * @type {!Object<number, !goog.Disposable>}
+ * @private
+ */
+goog.Disposable.instances_ = {};
+
+
+/**
+ * @return {!Array<!goog.Disposable>} All {@code goog.Disposable} objects that
+ * haven't been disposed of.
+ */
+goog.Disposable.getUndisposedObjects = function() {
+ var ret = [];
+ for (var id in goog.Disposable.instances_) {
+ if (goog.Disposable.instances_.hasOwnProperty(id)) {
+ ret.push(goog.Disposable.instances_[Number(id)]);
+ }
+ }
+ return ret;
+};
+
+
+/**
+ * Clears the registry of undisposed objects but doesn't dispose of them.
+ */
+goog.Disposable.clearUndisposedObjects = function() {
+ goog.Disposable.instances_ = {};
+};
+
+
+/**
+ * Whether the object has been disposed of.
+ * @type {boolean}
+ * @private
+ */
+goog.Disposable.prototype.disposed_ = false;
+
+
+/**
+ * Callbacks to invoke when this object is disposed.
+ * @type {Array<!Function>}
+ * @private
+ */
+goog.Disposable.prototype.onDisposeCallbacks_;
+
+
+/**
+ * @return {boolean} Whether the object has been disposed of.
+ * @override
+ */
+goog.Disposable.prototype.isDisposed = function() {
+ return this.disposed_;
+};
+
+
+/**
+ * @return {boolean} Whether the object has been disposed of.
+ * @deprecated Use {@link #isDisposed} instead.
+ */
+goog.Disposable.prototype.getDisposed = goog.Disposable.prototype.isDisposed;
+
+
+/**
+ * Disposes of the object. If the object hasn't already been disposed of, calls
+ * {@link #disposeInternal}. Classes that extend {@code goog.Disposable} should
+ * override {@link #disposeInternal} in order to delete references to COM
+ * objects, DOM nodes, and other disposable objects. Reentrant.
+ *
+ * @return {void} Nothing.
+ * @override
+ */
+goog.Disposable.prototype.dispose = function() {
+ if (!this.disposed_) {
+ // Set disposed_ to true first, in case during the chain of disposal this
+ // gets disposed recursively.
+ this.disposed_ = true;
+ this.disposeInternal();
+ if (goog.Disposable.MONITORING_MODE != goog.Disposable.MonitoringMode.OFF) {
+ var uid = goog.getUid(this);
+ if (goog.Disposable.MONITORING_MODE ==
+ goog.Disposable.MonitoringMode.PERMANENT &&
+ !goog.Disposable.instances_.hasOwnProperty(uid)) {
+ throw new Error(
+ this + ' did not call the goog.Disposable base ' +
+ 'constructor or was disposed of after a clearUndisposedObjects ' +
+ 'call');
+ }
+ delete goog.Disposable.instances_[uid];
+ }
+ }
+};
+
+
+/**
+ * Associates a disposable object with this object so that they will be disposed
+ * together.
+ * @param {goog.disposable.IDisposable} disposable that will be disposed when
+ * this object is disposed.
+ */
+goog.Disposable.prototype.registerDisposable = function(disposable) {
+ this.addOnDisposeCallback(goog.partial(goog.dispose, disposable));
+};
+
+
+/**
+ * Invokes a callback function when this object is disposed. Callbacks are
+ * invoked in the order in which they were added. If a callback is added to
+ * an already disposed Disposable, it will be called immediately.
+ * @param {function(this:T):?} callback The callback function.
+ * @param {T=} opt_scope An optional scope to call the callback in.
+ * @template T
+ */
+goog.Disposable.prototype.addOnDisposeCallback = function(callback, opt_scope) {
+ if (this.disposed_) {
+ goog.isDef(opt_scope) ? callback.call(opt_scope) : callback();
+ return;
+ }
+ if (!this.onDisposeCallbacks_) {
+ this.onDisposeCallbacks_ = [];
+ }
+
+ this.onDisposeCallbacks_.push(
+ goog.isDef(opt_scope) ? goog.bind(callback, opt_scope) : callback);
+};
+
+
+/**
+ * Deletes or nulls out any references to COM objects, DOM nodes, or other
+ * disposable objects. Classes that extend {@code goog.Disposable} should
+ * override this method.
+ * Not reentrant. To avoid calling it twice, it must only be called from the
+ * subclass' {@code disposeInternal} method. Everywhere else the public
+ * {@code dispose} method must be used.
+ * For example:
+ * <pre>
+ * mypackage.MyClass = function() {
+ * mypackage.MyClass.base(this, 'constructor');
+ * // Constructor logic specific to MyClass.
+ * ...
+ * };
+ * goog.inherits(mypackage.MyClass, goog.Disposable);
+ *
+ * mypackage.MyClass.prototype.disposeInternal = function() {
+ * // Dispose logic specific to MyClass.
+ * ...
+ * // Call superclass's disposeInternal at the end of the subclass's, like
+ * // in C++, to avoid hard-to-catch issues.
+ * mypackage.MyClass.base(this, 'disposeInternal');
+ * };
+ * </pre>
+ * @protected
+ */
+goog.Disposable.prototype.disposeInternal = function() {
+ if (this.onDisposeCallbacks_) {
+ while (this.onDisposeCallbacks_.length) {
+ this.onDisposeCallbacks_.shift()();
+ }
+ }
+};
+
+
+/**
+ * Returns True if we can verify the object is disposed.
+ * Calls {@code isDisposed} on the argument if it supports it. If obj
+ * is not an object with an isDisposed() method, return false.
+ * @param {*} obj The object to investigate.
+ * @return {boolean} True if we can verify the object is disposed.
+ */
+goog.Disposable.isDisposed = function(obj) {
+ if (obj && typeof obj.isDisposed == 'function') {
+ return obj.isDisposed();
+ }
+ return false;
+};
+
+
+/**
+ * Calls {@code dispose} on the argument if it supports it. If obj is not an
+ * object with a dispose() method, this is a no-op.
+ * @param {*} obj The object to dispose of.
+ */
+goog.dispose = function(obj) {
+ if (obj && typeof obj.dispose == 'function') {
+ obj.dispose();
+ }
+};
+
+
+/**
+ * Calls {@code dispose} on each member of the list that supports it. (If the
+ * member is an ArrayLike, then {@code goog.disposeAll()} will be called
+ * recursively on each of its members.) If the member is not an object with a
+ * {@code dispose()} method, then it is ignored.
+ * @param {...*} var_args The list.
+ */
+goog.disposeAll = function(var_args) {
+ for (var i = 0, len = arguments.length; i < len; ++i) {
+ var disposable = arguments[i];
+ if (goog.isArrayLike(disposable)) {
+ goog.disposeAll.apply(null, disposable);
+ } else {
+ goog.dispose(disposable);
+ }
+ }
+};
diff --git a/chromium/third_party/ink/closure/disposable/idisposable.js b/chromium/third_party/ink/closure/disposable/idisposable.js
new file mode 100644
index 00000000000..b539eb6f521
--- /dev/null
+++ b/chromium/third_party/ink/closure/disposable/idisposable.js
@@ -0,0 +1,45 @@
+// Copyright 2011 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Definition of the disposable interface. A disposable object
+ * has a dispose method to to clean up references and resources.
+ * @author nnaze@google.com (Nathan Naze)
+ */
+
+
+goog.provide('goog.disposable.IDisposable');
+
+
+
+/**
+ * Interface for a disposable object. If a instance requires cleanup
+ * (references COM objects, DOM nodes, or other disposable objects), it should
+ * implement this interface (it may subclass goog.Disposable).
+ * @record
+ */
+goog.disposable.IDisposable = function() {};
+
+
+/**
+ * Disposes of the object and its resources.
+ * @return {void} Nothing.
+ */
+goog.disposable.IDisposable.prototype.dispose = goog.abstractMethod;
+
+
+/**
+ * @return {boolean} Whether the object has been disposed of.
+ */
+goog.disposable.IDisposable.prototype.isDisposed = goog.abstractMethod;
diff --git a/chromium/third_party/ink/closure/dom/asserts.js b/chromium/third_party/ink/closure/dom/asserts.js
new file mode 100644
index 00000000000..e89144038cc
--- /dev/null
+++ b/chromium/third_party/ink/closure/dom/asserts.js
@@ -0,0 +1,299 @@
+// Copyright 2017 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+goog.provide('goog.dom.asserts');
+
+goog.require('goog.asserts');
+
+/**
+ * @fileoverview Custom assertions to ensure that an element has the appropriate
+ * type.
+ *
+ * Using a goog.dom.safe wrapper on an object on the incorrect type (via an
+ * incorrect static type cast) can result in security bugs: For instance,
+ * g.d.s.setAnchorHref ensures that the URL assigned to the .href attribute
+ * satisfies the SafeUrl contract, i.e., is safe to dereference as a hyperlink.
+ * However, the value assigned to a HTMLLinkElement's .href property requires
+ * the stronger TrustedResourceUrl contract, since it can refer to a stylesheet.
+ * Thus, using g.d.s.setAnchorHref on an (incorrectly statically typed) object
+ * of type HTMLLinkElement can result in a security vulnerability.
+ * Assertions of the correct run-time type help prevent such incorrect use.
+ *
+ * In some cases, code using the DOM API is tested using mock objects (e.g., a
+ * plain object such as {'href': url} instead of an actual Location object).
+ * To allow such mocking, the assertions permit objects of types that are not
+ * relevant DOM API objects at all (for instance, not Element or Location).
+ *
+ * Note that instanceof checks don't work straightforwardly in older versions of
+ * IE, or across frames (see,
+ * http://stackoverflow.com/questions/384286/javascript-isdom-how-do-you-check-if-a-javascript-object-is-a-dom-object,
+ * http://stackoverflow.com/questions/26248599/instanceof-htmlelement-in-iframe-is-not-element-or-object).
+ *
+ * Hence, these assertions may pass vacuously in such scenarios. The resulting
+ * risk of security bugs is limited by the following factors:
+ * - A bug can only arise in scenarios involving incorrect static typing (the
+ * wrapper methods are statically typed to demand objects of the appropriate,
+ * precise type).
+ * - Typically, code is tested and exercised in multiple browsers.
+ */
+
+/**
+ * Asserts that a given object is a Location.
+ *
+ * To permit this assertion to pass in the context of tests where DOM APIs might
+ * be mocked, also accepts any other type except for subtypes of {!Element}.
+ * This is to ensure that, for instance, HTMLLinkElement is not being used in
+ * place of a Location, since this could result in security bugs due to stronger
+ * contracts required for assignments to the href property of the latter.
+ *
+ * @param {?Object} o The object whose type to assert.
+ * @return {!Location}
+ */
+goog.dom.asserts.assertIsLocation = function(o) {
+ if (goog.asserts.ENABLE_ASSERTS) {
+ var win = goog.dom.asserts.getWindow_(o);
+ if (typeof win.Location != 'undefined' &&
+ typeof win.Element != 'undefined') {
+ goog.asserts.assert(
+ o && (o instanceof win.Location || !(o instanceof win.Element)),
+ 'Argument is not a Location (or a non-Element mock); got: %s',
+ goog.dom.asserts.debugStringForType_(o));
+ }
+ }
+ return /** @type {!Location} */ (o);
+};
+
+
+/**
+ * Asserts that a given object is either the given subtype of Element
+ * or a non-Element, non-Location Mock.
+ *
+ * To permit this assertion to pass in the context of tests where DOM
+ * APIs might be mocked, also accepts any other type except for
+ * subtypes of {!Element}. This is to ensure that, for instance,
+ * HTMLScriptElement is not being used in place of a HTMLImageElement,
+ * since this could result in security bugs due to stronger contracts
+ * required for assignments to the src property of the latter.
+ *
+ * The DOM type is looked up in the window the object belongs to. In
+ * some contexts, this might not be possible (e.g. when running tests
+ * outside a browser, cross-domain lookup). In this case, the
+ * assertions are skipped.
+ *
+ * @param {?Object} o The object whose type to assert.
+ * @param {string} typename The name of the DOM type.
+ * @return {!Element} The object.
+ * @private
+ */
+// TODO(bangert): Make an analog of goog.dom.TagName to correctly handle casts?
+goog.dom.asserts.assertIsElementType_ = function(o, typename) {
+ if (goog.asserts.ENABLE_ASSERTS) {
+ var win = goog.dom.asserts.getWindow_(o);
+ if (typeof win[typename] != 'undefined' &&
+ typeof win.Location != 'undefined' &&
+ typeof win.Element != 'undefined') {
+ goog.asserts.assert(
+ o &&
+ (o instanceof win[typename] ||
+ !((o instanceof win.Location) || (o instanceof win.Element))),
+ 'Argument is not a %s (or a non-Element, non-Location mock); got: %s',
+ typename, goog.dom.asserts.debugStringForType_(o));
+ }
+ }
+ return /** @type {!Element} */ (o);
+};
+
+/**
+ * Asserts that a given object is a HTMLAnchorElement.
+ *
+ * To permit this assertion to pass in the context of tests where elements might
+ * be mocked, also accepts objects that are not of type Location nor a subtype
+ * of Element.
+ *
+ * @param {?Object} o The object whose type to assert.
+ * @return {!HTMLAnchorElement}
+ */
+goog.dom.asserts.assertIsHTMLAnchorElement = function(o) {
+ return /** @type {!HTMLAnchorElement} */ (
+ goog.dom.asserts.assertIsElementType_(o, 'HTMLAnchorElement'));
+};
+
+/**
+ * Asserts that a given object is a HTMLButtonElement.
+ *
+ * To permit this assertion to pass in the context of tests where elements might
+ * be mocked, also accepts objects that are not a subtype of Element.
+ *
+ * @param {?Object} o The object whose type to assert.
+ * @return {!HTMLButtonElement}
+ */
+goog.dom.asserts.assertIsHTMLButtonElement = function(o) {
+ return /** @type {!HTMLButtonElement} */ (
+ goog.dom.asserts.assertIsElementType_(o, 'HTMLButtonElement'));
+};
+
+/**
+ * Asserts that a given object is a HTMLLinkElement.
+ *
+ * To permit this assertion to pass in the context of tests where elements might
+ * be mocked, also accepts objects that are not a subtype of Element.
+ *
+ * @param {?Object} o The object whose type to assert.
+ * @return {!HTMLLinkElement}
+ */
+goog.dom.asserts.assertIsHTMLLinkElement = function(o) {
+ return /** @type {!HTMLLinkElement} */ (
+ goog.dom.asserts.assertIsElementType_(o, 'HTMLLinkElement'));
+};
+
+/**
+ * Asserts that a given object is a HTMLImageElement.
+ *
+ * To permit this assertion to pass in the context of tests where elements might
+ * be mocked, also accepts objects that are not a subtype of Element.
+ *
+ * @param {?Object} o The object whose type to assert.
+ * @return {!HTMLImageElement}
+ */
+goog.dom.asserts.assertIsHTMLImageElement = function(o) {
+ return /** @type {!HTMLImageElement} */ (
+ goog.dom.asserts.assertIsElementType_(o, 'HTMLImageElement'));
+};
+
+/**
+ * Asserts that a given object is a HTMLInputElement.
+ *
+ * To permit this assertion to pass in the context of tests where elements might
+ * be mocked, also accepts objects that are not a subtype of Element.
+ *
+ * @param {?Object} o The object whose type to assert.
+ * @return {!HTMLInputElement}
+ */
+goog.dom.asserts.assertIsHTMLInputElement = function(o) {
+ return /** @type {!HTMLInputElement} */ (
+ goog.dom.asserts.assertIsElementType_(o, 'HTMLInputElement'));
+};
+
+/**
+ * Asserts that a given object is a HTMLEmbedElement.
+ *
+ * To permit this assertion to pass in the context of tests where elements might
+ * be mocked, also accepts objects that are not a subtype of Element.
+ *
+ * @param {?Object} o The object whose type to assert.
+ * @return {!HTMLEmbedElement}
+ */
+goog.dom.asserts.assertIsHTMLEmbedElement = function(o) {
+ return /** @type {!HTMLEmbedElement} */ (
+ goog.dom.asserts.assertIsElementType_(o, 'HTMLEmbedElement'));
+};
+
+/**
+ * Asserts that a given object is a HTMLFormElement.
+ *
+ * To permit this assertion to pass in the context of tests where elements might
+ * be mocked, also accepts objects that are not a subtype of Element.
+ *
+ * @param {?Object} o The object whose type to assert.
+ * @return {!HTMLFormElement}
+ */
+goog.dom.asserts.assertIsHTMLFormElement = function(o) {
+ return /** @type {!HTMLFormElement} */ (
+ goog.dom.asserts.assertIsElementType_(o, 'HTMLFormElement'));
+};
+
+/**
+ * Asserts that a given object is a HTMLFrameElement.
+ *
+ * To permit this assertion to pass in the context of tests where elements might
+ * be mocked, also accepts objects that are not a subtype of Element.
+ *
+ * @param {?Object} o The object whose type to assert.
+ * @return {!HTMLFrameElement}
+ */
+goog.dom.asserts.assertIsHTMLFrameElement = function(o) {
+ return /** @type {!HTMLFrameElement} */ (
+ goog.dom.asserts.assertIsElementType_(o, 'HTMLFrameElement'));
+};
+
+/**
+ * Asserts that a given object is a HTMLIFrameElement.
+ *
+ * To permit this assertion to pass in the context of tests where elements might
+ * be mocked, also accepts objects that are not a subtype of Element.
+ *
+ * @param {?Object} o The object whose type to assert.
+ * @return {!HTMLIFrameElement}
+ */
+goog.dom.asserts.assertIsHTMLIFrameElement = function(o) {
+ return /** @type {!HTMLIFrameElement} */ (
+ goog.dom.asserts.assertIsElementType_(o, 'HTMLIFrameElement'));
+};
+
+/**
+ * Asserts that a given object is a HTMLObjectElement.
+ *
+ * To permit this assertion to pass in the context of tests where elements might
+ * be mocked, also accepts objects that are not a subtype of Element.
+ *
+ * @param {?Object} o The object whose type to assert.
+ * @return {!HTMLObjectElement}
+ */
+goog.dom.asserts.assertIsHTMLObjectElement = function(o) {
+ return /** @type {!HTMLObjectElement} */ (
+ goog.dom.asserts.assertIsElementType_(o, 'HTMLObjectElement'));
+};
+
+/**
+ * Asserts that a given object is a HTMLScriptElement.
+ *
+ * To permit this assertion to pass in the context of tests where elements might
+ * be mocked, also accepts objects that are not a subtype of Element.
+ *
+ * @param {?Object} o The object whose type to assert.
+ * @return {!HTMLScriptElement}
+ */
+goog.dom.asserts.assertIsHTMLScriptElement = function(o) {
+ return /** @type {!HTMLScriptElement} */ (
+ goog.dom.asserts.assertIsElementType_(o, 'HTMLScriptElement'));
+};
+
+/**
+ * Returns a string representation of a value's type.
+ *
+ * @param {*} value An object, or primitive.
+ * @return {string} The best display name for the value.
+ * @private
+ */
+goog.dom.asserts.debugStringForType_ = function(value) {
+ if (goog.isObject(value)) {
+ return value.constructor.displayName || value.constructor.name ||
+ Object.prototype.toString.call(value);
+ } else {
+ return value === undefined ? 'undefined' :
+ value === null ? 'null' : typeof value;
+ }
+};
+
+/**
+ * Gets window of element.
+ * @param {?Object} o
+ * @return {!Window}
+ * @private
+ */
+goog.dom.asserts.getWindow_ = function(o) {
+ var doc = o && o.ownerDocument;
+ var win = doc && /** @type {?Window} */ (doc.defaultView || doc.parentWindow);
+ return win || /** @type {!Window} */ (goog.global);
+};
diff --git a/chromium/third_party/ink/closure/dom/browserfeature.js b/chromium/third_party/ink/closure/dom/browserfeature.js
new file mode 100644
index 00000000000..2d418ea564d
--- /dev/null
+++ b/chromium/third_party/ink/closure/dom/browserfeature.js
@@ -0,0 +1,74 @@
+// Copyright 2010 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Browser capability checks for the dom package.
+ *
+ * @author zhyder@google.com (Zohair Hyder)
+ */
+
+
+goog.provide('goog.dom.BrowserFeature');
+
+goog.require('goog.userAgent');
+
+
+/**
+ * Enum of browser capabilities.
+ * @enum {boolean}
+ */
+goog.dom.BrowserFeature = {
+ /**
+ * Whether attributes 'name' and 'type' can be added to an element after it's
+ * created. False in Internet Explorer prior to version 9.
+ */
+ CAN_ADD_NAME_OR_TYPE_ATTRIBUTES:
+ !goog.userAgent.IE || goog.userAgent.isDocumentModeOrHigher(9),
+
+ /**
+ * Whether we can use element.children to access an element's Element
+ * children. Available since Gecko 1.9.1, IE 9. (IE<9 also includes comment
+ * nodes in the collection.)
+ */
+ CAN_USE_CHILDREN_ATTRIBUTE: !goog.userAgent.GECKO && !goog.userAgent.IE ||
+ goog.userAgent.IE && goog.userAgent.isDocumentModeOrHigher(9) ||
+ goog.userAgent.GECKO && goog.userAgent.isVersionOrHigher('1.9.1'),
+
+ /**
+ * Opera, Safari 3, and Internet Explorer 9 all support innerText but they
+ * include text nodes in script and style tags. Not document-mode-dependent.
+ */
+ CAN_USE_INNER_TEXT:
+ (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9')),
+
+ /**
+ * MSIE, Opera, and Safari>=4 support element.parentElement to access an
+ * element's parent if it is an Element.
+ */
+ CAN_USE_PARENT_ELEMENT_PROPERTY:
+ goog.userAgent.IE || goog.userAgent.OPERA || goog.userAgent.WEBKIT,
+
+ /**
+ * Whether NoScope elements need a scoped element written before them in
+ * innerHTML.
+ * MSDN: http://msdn.microsoft.com/en-us/library/ms533897(VS.85).aspx#1
+ */
+ INNER_HTML_NEEDS_SCOPED_ELEMENT: goog.userAgent.IE,
+
+ /**
+ * Whether we use legacy IE range API.
+ */
+ LEGACY_IE_RANGES:
+ goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9)
+};
diff --git a/chromium/third_party/ink/closure/dom/classlist.js b/chromium/third_party/ink/closure/dom/classlist.js
new file mode 100644
index 00000000000..0d7afbf583b
--- /dev/null
+++ b/chromium/third_party/ink/closure/dom/classlist.js
@@ -0,0 +1,276 @@
+// Copyright 2012 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Utilities for detecting, adding and removing classes. Prefer
+ * this over goog.dom.classes for new code since it attempts to use classList
+ * (DOMTokenList: http://dom.spec.whatwg.org/#domtokenlist) which is faster
+ * and requires less code.
+ *
+ * Note: these utilities are meant to operate on HTMLElements
+ * and may have unexpected behavior on elements with differing interfaces
+ * (such as SVGElements).
+ */
+
+
+goog.provide('goog.dom.classlist');
+
+goog.require('goog.array');
+
+
+/**
+ * Override this define at build-time if you know your target supports it.
+ * @define {boolean} Whether to use the classList property (DOMTokenList).
+ */
+goog.define('goog.dom.classlist.ALWAYS_USE_DOM_TOKEN_LIST', false);
+
+
+/**
+ * Gets an array-like object of class names on an element.
+ * @param {Element} element DOM node to get the classes of.
+ * @return {!IArrayLike<?>} Class names on {@code element}.
+ */
+goog.dom.classlist.get = function(element) {
+ if (goog.dom.classlist.ALWAYS_USE_DOM_TOKEN_LIST || element.classList) {
+ return element.classList;
+ }
+
+ var className = element.className;
+ // Some types of elements don't have a className in IE (e.g. iframes).
+ // Furthermore, in Firefox, className is not a string when the element is
+ // an SVG element.
+ return goog.isString(className) && className.match(/\S+/g) || [];
+};
+
+
+/**
+ * Sets the entire class name of an element.
+ * @param {Element} element DOM node to set class of.
+ * @param {string} className Class name(s) to apply to element.
+ */
+goog.dom.classlist.set = function(element, className) {
+ element.className = className;
+};
+
+
+/**
+ * Returns true if an element has a class. This method may throw a DOM
+ * exception for an invalid or empty class name if DOMTokenList is used.
+ * @param {Element} element DOM node to test.
+ * @param {string} className Class name to test for.
+ * @return {boolean} Whether element has the class.
+ */
+goog.dom.classlist.contains = function(element, className) {
+ if (goog.dom.classlist.ALWAYS_USE_DOM_TOKEN_LIST || element.classList) {
+ return element.classList.contains(className);
+ }
+ return goog.array.contains(goog.dom.classlist.get(element), className);
+};
+
+
+/**
+ * Adds a class to an element. Does not add multiples of class names. This
+ * method may throw a DOM exception for an invalid or empty class name if
+ * DOMTokenList is used.
+ * @param {Element} element DOM node to add class to.
+ * @param {string} className Class name to add.
+ */
+goog.dom.classlist.add = function(element, className) {
+ if (goog.dom.classlist.ALWAYS_USE_DOM_TOKEN_LIST || element.classList) {
+ element.classList.add(className);
+ return;
+ }
+
+ if (!goog.dom.classlist.contains(element, className)) {
+ // Ensure we add a space if this is not the first class name added.
+ element.className +=
+ element.className.length > 0 ? (' ' + className) : className;
+ }
+};
+
+
+/**
+ * Convenience method to add a number of class names at once.
+ * @param {Element} element The element to which to add classes.
+ * @param {IArrayLike<string>} classesToAdd An array-like object
+ * containing a collection of class names to add to the element.
+ * This method may throw a DOM exception if classesToAdd contains invalid
+ * or empty class names.
+ */
+goog.dom.classlist.addAll = function(element, classesToAdd) {
+ if (goog.dom.classlist.ALWAYS_USE_DOM_TOKEN_LIST || element.classList) {
+ goog.array.forEach(classesToAdd, function(className) {
+ goog.dom.classlist.add(element, className);
+ });
+ return;
+ }
+
+ var classMap = {};
+
+ // Get all current class names into a map.
+ goog.array.forEach(goog.dom.classlist.get(element), function(className) {
+ classMap[className] = true;
+ });
+
+ // Add new class names to the map.
+ goog.array.forEach(
+ classesToAdd, function(className) { classMap[className] = true; });
+
+ // Flatten the keys of the map into the className.
+ element.className = '';
+ for (var className in classMap) {
+ element.className +=
+ element.className.length > 0 ? (' ' + className) : className;
+ }
+};
+
+
+/**
+ * Removes a class from an element. This method may throw a DOM exception
+ * for an invalid or empty class name if DOMTokenList is used.
+ * @param {Element} element DOM node to remove class from.
+ * @param {string} className Class name to remove.
+ */
+goog.dom.classlist.remove = function(element, className) {
+ if (goog.dom.classlist.ALWAYS_USE_DOM_TOKEN_LIST || element.classList) {
+ element.classList.remove(className);
+ return;
+ }
+
+ if (goog.dom.classlist.contains(element, className)) {
+ // Filter out the class name.
+ element.className = goog.array
+ .filter(
+ goog.dom.classlist.get(element),
+ function(c) { return c != className; })
+ .join(' ');
+ }
+};
+
+
+/**
+ * Removes a set of classes from an element. Prefer this call to
+ * repeatedly calling {@code goog.dom.classlist.remove} if you want to remove
+ * a large set of class names at once.
+ * @param {Element} element The element from which to remove classes.
+ * @param {IArrayLike<string>} classesToRemove An array-like object
+ * containing a collection of class names to remove from the element.
+ * This method may throw a DOM exception if classesToRemove contains invalid
+ * or empty class names.
+ */
+goog.dom.classlist.removeAll = function(element, classesToRemove) {
+ if (goog.dom.classlist.ALWAYS_USE_DOM_TOKEN_LIST || element.classList) {
+ goog.array.forEach(classesToRemove, function(className) {
+ goog.dom.classlist.remove(element, className);
+ });
+ return;
+ }
+ // Filter out those classes in classesToRemove.
+ element.className =
+ goog.array
+ .filter(
+ goog.dom.classlist.get(element),
+ function(className) {
+ // If this class is not one we are trying to remove,
+ // add it to the array of new class names.
+ return !goog.array.contains(classesToRemove, className);
+ })
+ .join(' ');
+};
+
+
+/**
+ * Adds or removes a class depending on the enabled argument. This method
+ * may throw a DOM exception for an invalid or empty class name if DOMTokenList
+ * is used.
+ * @param {Element} element DOM node to add or remove the class on.
+ * @param {string} className Class name to add or remove.
+ * @param {boolean} enabled Whether to add or remove the class (true adds,
+ * false removes).
+ */
+goog.dom.classlist.enable = function(element, className, enabled) {
+ if (enabled) {
+ goog.dom.classlist.add(element, className);
+ } else {
+ goog.dom.classlist.remove(element, className);
+ }
+};
+
+
+/**
+ * Adds or removes a set of classes depending on the enabled argument. This
+ * method may throw a DOM exception for an invalid or empty class name if
+ * DOMTokenList is used.
+ * @param {!Element} element DOM node to add or remove the class on.
+ * @param {?IArrayLike<string>} classesToEnable An array-like object
+ * containing a collection of class names to add or remove from the element.
+ * @param {boolean} enabled Whether to add or remove the classes (true adds,
+ * false removes).
+ */
+goog.dom.classlist.enableAll = function(element, classesToEnable, enabled) {
+ var f = enabled ? goog.dom.classlist.addAll : goog.dom.classlist.removeAll;
+ f(element, classesToEnable);
+};
+
+
+/**
+ * Switches a class on an element from one to another without disturbing other
+ * classes. If the fromClass isn't removed, the toClass won't be added. This
+ * method may throw a DOM exception if the class names are empty or invalid.
+ * @param {Element} element DOM node to swap classes on.
+ * @param {string} fromClass Class to remove.
+ * @param {string} toClass Class to add.
+ * @return {boolean} Whether classes were switched.
+ */
+goog.dom.classlist.swap = function(element, fromClass, toClass) {
+ if (goog.dom.classlist.contains(element, fromClass)) {
+ goog.dom.classlist.remove(element, fromClass);
+ goog.dom.classlist.add(element, toClass);
+ return true;
+ }
+ return false;
+};
+
+
+/**
+ * Removes a class if an element has it, and adds it the element doesn't have
+ * it. Won't affect other classes on the node. This method may throw a DOM
+ * exception if the class name is empty or invalid.
+ * @param {Element} element DOM node to toggle class on.
+ * @param {string} className Class to toggle.
+ * @return {boolean} True if class was added, false if it was removed
+ * (in other words, whether element has the class after this function has
+ * been called).
+ */
+goog.dom.classlist.toggle = function(element, className) {
+ var add = !goog.dom.classlist.contains(element, className);
+ goog.dom.classlist.enable(element, className, add);
+ return add;
+};
+
+
+/**
+ * Adds and removes a class of an element. Unlike
+ * {@link goog.dom.classlist.swap}, this method adds the classToAdd regardless
+ * of whether the classToRemove was present and had been removed. This method
+ * may throw a DOM exception if the class names are empty or invalid.
+ *
+ * @param {Element} element DOM node to swap classes on.
+ * @param {string} classToRemove Class to remove.
+ * @param {string} classToAdd Class to add.
+ */
+goog.dom.classlist.addRemove = function(element, classToRemove, classToAdd) {
+ goog.dom.classlist.remove(element, classToRemove);
+ goog.dom.classlist.add(element, classToAdd);
+};
diff --git a/chromium/third_party/ink/closure/dom/dom.js b/chromium/third_party/ink/closure/dom/dom.js
new file mode 100644
index 00000000000..a330b4c4e91
--- /dev/null
+++ b/chromium/third_party/ink/closure/dom/dom.js
@@ -0,0 +1,3234 @@
+// Copyright 2006 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Utilities for manipulating the browser's Document Object Model
+ * Inspiration taken *heavily* from mochikit (http://mochikit.com/).
+ *
+ * You can use {@link goog.dom.DomHelper} to create new dom helpers that refer
+ * to a different document object. This is useful if you are working with
+ * frames or multiple windows.
+ *
+ * @author pupius@google.com (Daniel Pupius)
+ * @author arv@google.com (Erik Arvidsson)
+ */
+
+
+// TODO(arv): Rename/refactor getTextContent and getRawTextContent. The problem
+// is that getTextContent should mimic the DOM3 textContent. We should add a
+// getInnerText (or getText) which tries to return the visible text, innerText.
+
+
+goog.provide('goog.dom');
+goog.provide('goog.dom.Appendable');
+goog.provide('goog.dom.DomHelper');
+
+goog.require('goog.array');
+goog.require('goog.asserts');
+goog.require('goog.dom.BrowserFeature');
+goog.require('goog.dom.NodeType');
+goog.require('goog.dom.TagName');
+goog.require('goog.dom.safe');
+goog.require('goog.html.SafeHtml');
+goog.require('goog.html.uncheckedconversions');
+goog.require('goog.math.Coordinate');
+goog.require('goog.math.Size');
+goog.require('goog.object');
+goog.require('goog.string');
+goog.require('goog.string.Unicode');
+goog.require('goog.userAgent');
+
+
+/**
+ * @define {boolean} Whether we know at compile time that the browser is in
+ * quirks mode.
+ */
+goog.define('goog.dom.ASSUME_QUIRKS_MODE', false);
+
+
+/**
+ * @define {boolean} Whether we know at compile time that the browser is in
+ * standards compliance mode.
+ */
+goog.define('goog.dom.ASSUME_STANDARDS_MODE', false);
+
+
+/**
+ * Whether we know the compatibility mode at compile time.
+ * @type {boolean}
+ * @private
+ */
+goog.dom.COMPAT_MODE_KNOWN_ =
+ goog.dom.ASSUME_QUIRKS_MODE || goog.dom.ASSUME_STANDARDS_MODE;
+
+
+/**
+ * Gets the DomHelper object for the document where the element resides.
+ * @param {(Node|Window)=} opt_element If present, gets the DomHelper for this
+ * element.
+ * @return {!goog.dom.DomHelper} The DomHelper.
+ */
+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()));
+};
+
+
+/**
+ * Cached default DOM helper.
+ * @type {!goog.dom.DomHelper|undefined}
+ * @private
+ */
+goog.dom.defaultDomHelper_;
+
+
+/**
+ * Gets the document object being used by the dom library.
+ * @return {!Document} Document object.
+ */
+goog.dom.getDocument = function() {
+ return document;
+};
+
+
+/**
+ * Gets an element from the current document by element id.
+ *
+ * If an Element is passed in, it is returned.
+ *
+ * @param {string|Element} element Element ID or a DOM node.
+ * @return {Element} The element with the given ID, or the node passed in.
+ */
+goog.dom.getElement = function(element) {
+ return goog.dom.getElementHelper_(document, element);
+};
+
+
+/**
+ * Gets an element by id from the given document (if present).
+ * If an element is given, it is returned.
+ * @param {!Document} doc
+ * @param {string|Element} element Element ID or a DOM node.
+ * @return {Element} The resulting element.
+ * @private
+ */
+goog.dom.getElementHelper_ = function(doc, element) {
+ return goog.isString(element) ? doc.getElementById(element) : element;
+};
+
+
+/**
+ * Gets an element by id, asserting that the element is found.
+ *
+ * This is used when an element is expected to exist, and should fail with
+ * an assertion error if it does not (if assertions are enabled).
+ *
+ * @param {string} id Element ID.
+ * @return {!Element} The element with the given ID, if it exists.
+ */
+goog.dom.getRequiredElement = function(id) {
+ return goog.dom.getRequiredElementHelper_(document, id);
+};
+
+
+/**
+ * Helper function for getRequiredElementHelper functions, both static and
+ * on DomHelper. Asserts the element with the given id exists.
+ * @param {!Document} doc
+ * @param {string} id
+ * @return {!Element} The element with the given ID, if it exists.
+ * @private
+ */
+goog.dom.getRequiredElementHelper_ = function(doc, id) {
+ // To prevent users passing in Elements as is permitted in getElement().
+ goog.asserts.assertString(id);
+ var element = goog.dom.getElementHelper_(doc, id);
+ element =
+ goog.asserts.assertElement(element, 'No element found with id: ' + id);
+ return element;
+};
+
+
+/**
+ * Alias for getElement.
+ * @param {string|Element} element Element ID or a DOM node.
+ * @return {Element} The element with the given ID, or the node passed in.
+ * @deprecated Use {@link goog.dom.getElement} instead.
+ */
+goog.dom.$ = goog.dom.getElement;
+
+
+/**
+ * Gets elements by tag name.
+ * @param {!goog.dom.TagName<T>} tagName
+ * @param {(!Document|!Element)=} opt_parent Parent element or document where to
+ * look for elements. Defaults to document.
+ * @return {!NodeList<R>} List of elements. The members of the list are
+ * {!Element} if tagName is not a member of goog.dom.TagName or more
+ * specific types if it is (e.g. {!HTMLAnchorElement} for
+ * goog.dom.TagName.A).
+ * @template T
+ * @template R := cond(isUnknown(T), 'Element', T) =:
+ */
+goog.dom.getElementsByTagName = function(tagName, opt_parent) {
+ var parent = opt_parent || document;
+ return parent.getElementsByTagName(String(tagName));
+};
+
+
+/**
+ * Looks up elements by both tag and class name, using browser native functions
+ * ({@code querySelectorAll}, {@code getElementsByTagName} or
+ * {@code getElementsByClassName}) where possible. This function
+ * is a useful, if limited, way of collecting a list of DOM elements
+ * with certain characteristics. {@code goog.dom.query} offers a
+ * more powerful and general solution which allows matching on CSS3
+ * selector expressions, but at increased cost in code size. If all you
+ * need is particular tags belonging to a single class, this function
+ * is fast and sleek.
+ *
+ * Note that tag names are case sensitive in the SVG namespace, and this
+ * function converts opt_tag to uppercase for comparisons. For queries in the
+ * SVG namespace you should use querySelector or querySelectorAll instead.
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=963870
+ * https://bugs.webkit.org/show_bug.cgi?id=83438
+ *
+ * @see {goog.dom.query}
+ *
+ * @param {(string|?goog.dom.TagName<T>)=} opt_tag Element tag name.
+ * @param {?string=} opt_class Optional class name.
+ * @param {(Document|Element)=} opt_el Optional element to look in.
+ * @return {!IArrayLike<R>} Array-like list of elements (only a length property
+ * and numerical indices are guaranteed to exist). The members of the array
+ * are {!Element} if opt_tag is not a member of goog.dom.TagName or more
+ * specific types if it is (e.g. {!HTMLAnchorElement} for
+ * goog.dom.TagName.A).
+ * @template T
+ * @template R := cond(isUnknown(T), 'Element', T) =:
+ */
+goog.dom.getElementsByTagNameAndClass = function(opt_tag, opt_class, opt_el) {
+ return goog.dom.getElementsByTagNameAndClass_(
+ document, opt_tag, opt_class, opt_el);
+};
+
+
+/**
+ * Gets the first element matching the tag and the class.
+ *
+ * @param {(string|?goog.dom.TagName<T>)=} opt_tag Element tag name.
+ * @param {?string=} opt_class Optional class name.
+ * @param {(Document|Element)=} opt_el Optional element to look in.
+ * @return {?R} Reference to a DOM node. The return type is {?Element} if
+ * tagName is a string or a more specific type if it is a member of
+ * goog.dom.TagName (e.g. {?HTMLAnchorElement} for goog.dom.TagName.A).
+ * @template T
+ * @template R := cond(isUnknown(T), 'Element', T) =:
+ */
+goog.dom.getElementByTagNameAndClass = function(opt_tag, opt_class, opt_el) {
+ return goog.dom.getElementByTagNameAndClass_(
+ document, opt_tag, opt_class, opt_el);
+};
+
+
+/**
+ * Returns a static, array-like list of the elements with the provided
+ * className.
+ * @see {goog.dom.query}
+ * @param {string} className the name of the class to look for.
+ * @param {(Document|Element)=} opt_el Optional element to look in.
+ * @return {!IArrayLike<!Element>} The items found with the class name provided.
+ */
+goog.dom.getElementsByClass = function(className, opt_el) {
+ var parent = opt_el || document;
+ if (goog.dom.canUseQuerySelector_(parent)) {
+ return parent.querySelectorAll('.' + className);
+ }
+ return goog.dom.getElementsByTagNameAndClass_(
+ document, '*', className, opt_el);
+};
+
+
+/**
+ * Returns the first element with the provided className.
+ * @see {goog.dom.query}
+ * @param {string} className the name of the class to look for.
+ * @param {Element|Document=} opt_el Optional element to look in.
+ * @return {Element} The first item with the class name provided.
+ */
+goog.dom.getElementByClass = function(className, opt_el) {
+ var parent = opt_el || document;
+ var retVal = null;
+ if (parent.getElementsByClassName) {
+ retVal = parent.getElementsByClassName(className)[0];
+ } else {
+ retVal =
+ goog.dom.getElementByTagNameAndClass_(document, '*', className, opt_el);
+ }
+ return retVal || null;
+};
+
+
+/**
+ * Ensures an element with the given className exists, and then returns the
+ * first element with the provided className.
+ * @see {goog.dom.query}
+ * @param {string} className the name of the class to look for.
+ * @param {!Element|!Document=} opt_root Optional element or document to look
+ * in.
+ * @return {!Element} The first item with the class name provided.
+ * @throws {goog.asserts.AssertionError} Thrown if no element is found.
+ */
+goog.dom.getRequiredElementByClass = function(className, opt_root) {
+ var retValue = goog.dom.getElementByClass(className, opt_root);
+ return goog.asserts.assert(
+ retValue, 'No element found with className: ' + className);
+};
+
+
+/**
+ * Prefer the standardized (http://www.w3.org/TR/selectors-api/), native and
+ * fast W3C Selectors API.
+ * @param {!(Element|Document)} parent The parent document object.
+ * @return {boolean} whether or not we can use parent.querySelector* APIs.
+ * @private
+ */
+goog.dom.canUseQuerySelector_ = function(parent) {
+ return !!(parent.querySelectorAll && parent.querySelector);
+};
+
+
+/**
+ * Helper for {@code getElementsByTagNameAndClass}.
+ * @param {!Document} doc The document to get the elements in.
+ * @param {(string|?goog.dom.TagName<T>)=} opt_tag Element tag name.
+ * @param {?string=} opt_class Optional class name.
+ * @param {(Document|Element)=} opt_el Optional element to look in.
+ * @return {!IArrayLike<R>} Array-like list of elements (only a length property
+ * and numerical indices are guaranteed to exist). The members of the array
+ * are {!Element} if opt_tag is not a member of goog.dom.TagName or more
+ * specific types if it is (e.g. {!HTMLAnchorElement} for
+ * goog.dom.TagName.A).
+ * @template T
+ * @template R := cond(isUnknown(T), 'Element', T) =:
+ * @private
+ */
+goog.dom.getElementsByTagNameAndClass_ = function(
+ doc, opt_tag, opt_class, opt_el) {
+ var parent = opt_el || doc;
+ var tagName =
+ (opt_tag && opt_tag != '*') ? String(opt_tag).toUpperCase() : '';
+
+ if (goog.dom.canUseQuerySelector_(parent) && (tagName || opt_class)) {
+ var query = tagName + (opt_class ? '.' + opt_class : '');
+ return parent.querySelectorAll(query);
+ }
+
+ // Use the native getElementsByClassName if available, under the assumption
+ // that even when the tag name is specified, there will be fewer elements to
+ // filter through when going by class than by tag name
+ if (opt_class && parent.getElementsByClassName) {
+ var els = parent.getElementsByClassName(opt_class);
+
+ if (tagName) {
+ var arrayLike = {};
+ var len = 0;
+
+ // Filter for specific tags if requested.
+ for (var i = 0, el; el = els[i]; i++) {
+ if (tagName == el.nodeName) {
+ arrayLike[len++] = el;
+ }
+ }
+ arrayLike.length = len;
+
+ return /** @type {!IArrayLike<!Element>} */ (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;
+ // Check if className has a split function since SVG className does not.
+ if (typeof className.split == 'function' &&
+ goog.array.contains(className.split(/\s+/), opt_class)) {
+ arrayLike[len++] = el;
+ }
+ }
+ arrayLike.length = len;
+ return /** @type {!IArrayLike<!Element>} */ (arrayLike);
+ } else {
+ return els;
+ }
+};
+
+
+/**
+ * Helper for goog.dom.getElementByTagNameAndClass.
+ *
+ * @param {!Document} doc The document to get the elements in.
+ * @param {(string|?goog.dom.TagName<T>)=} opt_tag Element tag name.
+ * @param {?string=} opt_class Optional class name.
+ * @param {(Document|Element)=} opt_el Optional element to look in.
+ * @return {?R} Reference to a DOM node. The return type is {?Element} if
+ * tagName is a string or a more specific type if it is a member of
+ * goog.dom.TagName (e.g. {?HTMLAnchorElement} for goog.dom.TagName.A).
+ * @template T
+ * @template R := cond(isUnknown(T), 'Element', T) =:
+ * @private
+ */
+goog.dom.getElementByTagNameAndClass_ = function(
+ doc, opt_tag, opt_class, opt_el) {
+ var parent = opt_el || doc;
+ var tag = (opt_tag && opt_tag != '*') ? String(opt_tag).toUpperCase() : '';
+ if (goog.dom.canUseQuerySelector_(parent) && (tag || opt_class)) {
+ return parent.querySelector(tag + (opt_class ? '.' + opt_class : ''));
+ }
+ var elements =
+ goog.dom.getElementsByTagNameAndClass_(doc, opt_tag, opt_class, opt_el);
+ return elements[0] || null;
+};
+
+
+
+/**
+ * Alias for {@code getElementsByTagNameAndClass}.
+ * @param {(string|?goog.dom.TagName<T>)=} opt_tag Element tag name.
+ * @param {?string=} opt_class Optional class name.
+ * @param {Element=} opt_el Optional element to look in.
+ * @return {!IArrayLike<R>} Array-like list of elements (only a length property
+ * and numerical indices are guaranteed to exist). The members of the array
+ * are {!Element} if opt_tag is not a member of goog.dom.TagName or more
+ * specific types if it is (e.g. {!HTMLAnchorElement} for
+ * goog.dom.TagName.A).
+ * @template T
+ * @template R := cond(isUnknown(T), 'Element', T) =:
+ * @deprecated Use {@link goog.dom.getElementsByTagNameAndClass} instead.
+ */
+goog.dom.$$ = goog.dom.getElementsByTagNameAndClass;
+
+
+/**
+ * Sets multiple properties, and sometimes attributes, on an element. Note that
+ * properties are simply object properties on the element instance, while
+ * attributes are visible in the DOM. Many properties map to attributes with the
+ * same names, some with different names, and there are also unmappable cases.
+ *
+ * This method sets properties by default (which means that custom attributes
+ * are not supported). These are the exeptions (some of which is legacy):
+ * - "style": Even though this is an attribute name, it is translated to a
+ * property, "style.cssText". Note that this property sanitizes and formats
+ * its value, unlike the attribute.
+ * - "class": This is an attribute name, it is translated to the "className"
+ * property.
+ * - "for": This is an attribute name, it is translated to the "htmlFor"
+ * property.
+ * - Entries in {@see goog.dom.DIRECT_ATTRIBUTE_MAP_} are set as attributes,
+ * this is probably due to browser quirks.
+ * - "aria-*", "data-*": Always set as attributes, they have no property
+ * counterparts.
+ *
+ * @param {Element} element DOM node to set properties on.
+ * @param {Object} properties Hash of property:value pairs.
+ * Property values can be strings or goog.string.TypedString values (such as
+ * goog.html.SafeUrl).
+ */
+goog.dom.setProperties = function(element, properties) {
+ goog.object.forEach(properties, function(val, key) {
+ if (val && val.implementsGoogStringTypedString) {
+ val = val.getTypedStringValue();
+ }
+ if (key == 'style') {
+ element.style.cssText = val;
+ } else if (key == 'class') {
+ element.className = val;
+ } else if (key == 'for') {
+ element.htmlFor = val;
+ } else if (goog.dom.DIRECT_ATTRIBUTE_MAP_.hasOwnProperty(key)) {
+ element.setAttribute(goog.dom.DIRECT_ATTRIBUTE_MAP_[key], val);
+ } else if (
+ goog.string.startsWith(key, 'aria-') ||
+ goog.string.startsWith(key, 'data-')) {
+ element.setAttribute(key, val);
+ } else {
+ element[key] = val;
+ }
+ });
+};
+
+
+/**
+ * Map of attributes that should be set using
+ * element.setAttribute(key, val) instead of element[key] = val. Used
+ * by goog.dom.setProperties.
+ *
+ * @private {!Object<string, string>}
+ * @const
+ */
+goog.dom.DIRECT_ATTRIBUTE_MAP_ = {
+ 'cellpadding': 'cellPadding',
+ 'cellspacing': 'cellSpacing',
+ 'colspan': 'colSpan',
+ 'frameborder': 'frameBorder',
+ 'height': 'height',
+ 'maxlength': 'maxLength',
+ 'nonce': 'nonce',
+ 'role': 'role',
+ 'rowspan': 'rowSpan',
+ 'type': 'type',
+ 'usemap': 'useMap',
+ 'valign': 'vAlign',
+ 'width': 'width'
+};
+
+
+/**
+ * Gets the dimensions of the viewport.
+ *
+ * Gecko Standards mode:
+ * docEl.clientWidth Width of viewport excluding scrollbar.
+ * win.innerWidth Width of viewport including scrollbar.
+ * body.clientWidth Width of body element.
+ *
+ * docEl.clientHeight Height of viewport excluding scrollbar.
+ * win.innerHeight Height of viewport including scrollbar.
+ * body.clientHeight Height of document.
+ *
+ * Gecko Backwards compatible mode:
+ * docEl.clientWidth Width of viewport excluding scrollbar.
+ * win.innerWidth Width of viewport including scrollbar.
+ * body.clientWidth Width of viewport excluding scrollbar.
+ *
+ * docEl.clientHeight Height of document.
+ * win.innerHeight Height of viewport including scrollbar.
+ * body.clientHeight Height of viewport excluding scrollbar.
+ *
+ * IE6/7 Standards mode:
+ * docEl.clientWidth Width of viewport excluding scrollbar.
+ * win.innerWidth Undefined.
+ * body.clientWidth Width of body element.
+ *
+ * docEl.clientHeight Height of viewport excluding scrollbar.
+ * win.innerHeight Undefined.
+ * body.clientHeight Height of document element.
+ *
+ * IE5 + IE6/7 Backwards compatible mode:
+ * docEl.clientWidth 0.
+ * win.innerWidth Undefined.
+ * body.clientWidth Width of viewport excluding scrollbar.
+ *
+ * docEl.clientHeight 0.
+ * win.innerHeight Undefined.
+ * body.clientHeight Height of viewport excluding scrollbar.
+ *
+ * Opera 9 Standards and backwards compatible mode:
+ * docEl.clientWidth Width of viewport excluding scrollbar.
+ * win.innerWidth Width of viewport including scrollbar.
+ * body.clientWidth Width of viewport excluding scrollbar.
+ *
+ * docEl.clientHeight Height of document.
+ * win.innerHeight Height of viewport including scrollbar.
+ * body.clientHeight Height of viewport excluding scrollbar.
+ *
+ * WebKit:
+ * Safari 2
+ * docEl.clientHeight Same as scrollHeight.
+ * docEl.clientWidth Same as innerWidth.
+ * win.innerWidth Width of viewport excluding scrollbar.
+ * win.innerHeight Height of the viewport including scrollbar.
+ * frame.innerHeight Height of the viewport exluding scrollbar.
+ *
+ * Safari 3 (tested in 522)
+ *
+ * docEl.clientWidth Width of viewport excluding scrollbar.
+ * docEl.clientHeight Height of viewport excluding scrollbar in strict mode.
+ * body.clientHeight Height of viewport excluding scrollbar in quirks mode.
+ *
+ * @param {Window=} opt_window Optional window element to test.
+ * @return {!goog.math.Size} Object with values 'width' and 'height'.
+ */
+goog.dom.getViewportSize = function(opt_window) {
+ // TODO(arv): This should not take an argument
+ return goog.dom.getViewportSize_(opt_window || window);
+};
+
+
+/**
+ * Helper for {@code getViewportSize}.
+ * @param {Window} win The window to get the view port size for.
+ * @return {!goog.math.Size} Object with values 'width' and 'height'.
+ * @private
+ */
+goog.dom.getViewportSize_ = function(win) {
+ var doc = win.document;
+ var el = goog.dom.isCss1CompatMode_(doc) ? doc.documentElement : doc.body;
+ return new goog.math.Size(el.clientWidth, el.clientHeight);
+};
+
+
+/**
+ * Calculates the height of the document.
+ *
+ * @return {number} The height of the current document.
+ */
+goog.dom.getDocumentHeight = function() {
+ return goog.dom.getDocumentHeight_(window);
+};
+
+/**
+ * Calculates the height of the document of the given window.
+ *
+ * @param {!Window} win The window whose document height to retrieve.
+ * @return {number} The height of the document of the given window.
+ */
+goog.dom.getDocumentHeightForWindow = function(win) {
+ return goog.dom.getDocumentHeight_(win);
+};
+
+/**
+ * Calculates the height of the document of the given window.
+ *
+ * Function code copied from the opensocial gadget api:
+ * gadgets.window.adjustHeight(opt_height)
+ *
+ * @private
+ * @param {!Window} win The window whose document height to retrieve.
+ * @return {number} The height of the document of the given window.
+ */
+goog.dom.getDocumentHeight_ = function(win) {
+ // NOTE(eae): This method will return the window size rather than the document
+ // size in webkit quirks mode.
+ var doc = win.document;
+ var height = 0;
+
+ if (doc) {
+ // Calculating inner content height is hard and different between
+ // browsers rendering in Strict vs. Quirks mode. We use a combination of
+ // three properties within document.body and document.documentElement:
+ // - scrollHeight
+ // - offsetHeight
+ // - clientHeight
+ // These values differ significantly between browsers and rendering modes.
+ // But there are patterns. It just takes a lot of time and persistence
+ // to figure out.
+
+ var body = doc.body;
+ var docEl = /** @type {!HTMLElement} */ (doc.documentElement);
+ if (!(docEl && body)) {
+ return 0;
+ }
+
+ // Get the height of the viewport
+ var vh = goog.dom.getViewportSize_(win).height;
+ if (goog.dom.isCss1CompatMode_(doc) && docEl.scrollHeight) {
+ // In Strict mode:
+ // The inner content height is contained in either:
+ // document.documentElement.scrollHeight
+ // document.documentElement.offsetHeight
+ // Based on studying the values output by different browsers,
+ // use the value that's NOT equal to the viewport height found above.
+ height =
+ docEl.scrollHeight != vh ? docEl.scrollHeight : docEl.offsetHeight;
+ } else {
+ // In Quirks mode:
+ // documentElement.clientHeight is equal to documentElement.offsetHeight
+ // except in IE. In most browsers, document.documentElement can be used
+ // to calculate the inner content height.
+ // However, in other browsers (e.g. IE), document.body must be used
+ // instead. How do we know which one to use?
+ // If document.documentElement.clientHeight does NOT equal
+ // document.documentElement.offsetHeight, then use document.body.
+ var sh = docEl.scrollHeight;
+ var oh = docEl.offsetHeight;
+ if (docEl.clientHeight != oh) {
+ sh = body.scrollHeight;
+ oh = body.offsetHeight;
+ }
+
+ // Detect whether the inner content height is bigger or smaller
+ // than the bounding box (viewport). If bigger, take the larger
+ // value. If smaller, take the smaller value.
+ if (sh > vh) {
+ // Content is larger
+ height = sh > oh ? sh : oh;
+ } else {
+ // Content is smaller
+ height = sh < oh ? sh : oh;
+ }
+ }
+ }
+
+ return height;
+};
+
+
+/**
+ * Gets the page scroll distance as a coordinate object.
+ *
+ * @param {Window=} opt_window Optional window element to test.
+ * @return {!goog.math.Coordinate} Object with values 'x' and 'y'.
+ * @deprecated Use {@link goog.dom.getDocumentScroll} instead.
+ */
+goog.dom.getPageScroll = function(opt_window) {
+ var win = opt_window || goog.global || window;
+ return goog.dom.getDomHelper(win.document).getDocumentScroll();
+};
+
+
+/**
+ * Gets the document scroll distance as a coordinate object.
+ *
+ * @return {!goog.math.Coordinate} Object with values 'x' and 'y'.
+ */
+goog.dom.getDocumentScroll = function() {
+ return goog.dom.getDocumentScroll_(document);
+};
+
+
+/**
+ * Helper for {@code getDocumentScroll}.
+ *
+ * @param {!Document} doc The document to get the scroll for.
+ * @return {!goog.math.Coordinate} Object with values 'x' and 'y'.
+ * @private
+ */
+goog.dom.getDocumentScroll_ = function(doc) {
+ var el = goog.dom.getDocumentScrollElement_(doc);
+ var win = goog.dom.getWindow_(doc);
+ if (goog.userAgent.IE && goog.userAgent.isVersionOrHigher('10') &&
+ win.pageYOffset != el.scrollTop) {
+ // The keyboard on IE10 touch devices shifts the page using the pageYOffset
+ // without modifying scrollTop. For this case, we want the body scroll
+ // offsets.
+ return new goog.math.Coordinate(el.scrollLeft, el.scrollTop);
+ }
+ return new goog.math.Coordinate(
+ win.pageXOffset || el.scrollLeft, win.pageYOffset || el.scrollTop);
+};
+
+
+/**
+ * Gets the document scroll element.
+ * @return {!Element} Scrolling element.
+ */
+goog.dom.getDocumentScrollElement = function() {
+ return goog.dom.getDocumentScrollElement_(document);
+};
+
+
+/**
+ * Helper for {@code getDocumentScrollElement}.
+ * @param {!Document} doc The document to get the scroll element for.
+ * @return {!Element} Scrolling element.
+ * @private
+ */
+goog.dom.getDocumentScrollElement_ = function(doc) {
+ // Old WebKit needs body.scrollLeft in both quirks mode and strict mode. We
+ // also default to the documentElement if the document does not have a body
+ // (e.g. a SVG document).
+ // Uses http://dev.w3.org/csswg/cssom-view/#dom-document-scrollingelement to
+ // avoid trying to guess about browser behavior from the UA string.
+ if (doc.scrollingElement) {
+ return doc.scrollingElement;
+ }
+ if (!goog.userAgent.WEBKIT && goog.dom.isCss1CompatMode_(doc)) {
+ return doc.documentElement;
+ }
+ return doc.body || doc.documentElement;
+};
+
+
+/**
+ * Gets the window object associated with the given document.
+ *
+ * @param {Document=} opt_doc Document object to get window for.
+ * @return {!Window} The window associated with the given document.
+ */
+goog.dom.getWindow = function(opt_doc) {
+ // TODO(arv): This should not take an argument.
+ return opt_doc ? goog.dom.getWindow_(opt_doc) : window;
+};
+
+
+/**
+ * Helper for {@code getWindow}.
+ *
+ * @param {!Document} doc Document object to get window for.
+ * @return {!Window} The window associated with the given document.
+ * @private
+ */
+goog.dom.getWindow_ = function(doc) {
+ return /** @type {!Window} */ (doc.parentWindow || doc.defaultView);
+};
+
+
+/**
+ * Returns a dom node with a set of attributes. This function accepts varargs
+ * for subsequent nodes to be added. Subsequent nodes will be added to the
+ * first node as childNodes.
+ *
+ * So:
+ * <code>createDom(goog.dom.TagName.DIV, null, createDom(goog.dom.TagName.P),
+ * createDom(goog.dom.TagName.P));</code> would return a div with two child
+ * paragraphs
+ *
+ * For passing properties, please see {@link goog.dom.setProperties} for more
+ * information.
+ *
+ * @param {string|!goog.dom.TagName<T>} tagName Tag to create.
+ * @param {?Object|?Array<string>|string=} opt_attributes If object, then a map
+ * of name-value pairs for attributes. If a string, then this is the
+ * className of the new element. If an array, the elements will be joined
+ * together as the className of the new element.
+ * @param {...(Object|string|Array|NodeList)} var_args Further DOM nodes or
+ * strings for text nodes. If one of the var_args is an array or NodeList,
+ * its elements will be added as childNodes instead.
+ * @return {R} Reference to a DOM node. The return type is {!Element} if tagName
+ * is a string or a more specific type if it is a member of
+ * goog.dom.TagName (e.g. {!HTMLAnchorElement} for goog.dom.TagName.A).
+ * @template T
+ * @template R := cond(isUnknown(T), 'Element', T) =:
+ */
+goog.dom.createDom = function(tagName, opt_attributes, var_args) {
+ return goog.dom.createDom_(document, arguments);
+};
+
+
+/**
+ * Helper for {@code createDom}.
+ * @param {!Document} doc The document to create the DOM in.
+ * @param {!Arguments} args Argument object passed from the callers. See
+ * {@code goog.dom.createDom} for details.
+ * @return {!Element} Reference to a DOM node.
+ * @private
+ */
+goog.dom.createDom_ = function(doc, args) {
+ var tagName = String(args[0]);
+ var attributes = args[1];
+
+ // Internet Explorer is dumb:
+ // name: https://msdn.microsoft.com/en-us/library/ms534184(v=vs.85).aspx
+ // type: https://msdn.microsoft.com/en-us/library/ms534700(v=vs.85).aspx
+ // Also does not allow setting of 'type' attribute on 'input' or 'button'.
+ 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), '"');
+
+ // Clone attributes map to remove 'type' without mutating the input.
+ var clone = {};
+ goog.object.extend(clone, attributes);
+
+ // JSCompiler can't see how goog.object.extend added this property,
+ // because it was essentially added by reflection.
+ // So it needs to be quoted.
+ delete clone['type'];
+
+ attributes = clone;
+ }
+ tagNameArr.push('>');
+ tagName = tagNameArr.join('');
+ }
+
+ var element = doc.createElement(tagName);
+
+ if (attributes) {
+ if (goog.isString(attributes)) {
+ element.className = attributes;
+ } else if (goog.isArray(attributes)) {
+ element.className = attributes.join(' ');
+ } else {
+ goog.dom.setProperties(element, attributes);
+ }
+ }
+
+ if (args.length > 2) {
+ goog.dom.append_(doc, element, args, 2);
+ }
+
+ return element;
+};
+
+
+/**
+ * Appends a node with text or other nodes.
+ * @param {!Document} doc The document to create new nodes in.
+ * @param {!Node} parent The node to append nodes to.
+ * @param {!Arguments} args The values to add. See {@code goog.dom.append}.
+ * @param {number} startIndex The index of the array to start from.
+ * @private
+ */
+goog.dom.append_ = function(doc, parent, args, startIndex) {
+ function childHandler(child) {
+ // TODO(pupius): More coercion, ala MochiKit?
+ if (child) {
+ parent.appendChild(
+ goog.isString(child) ? doc.createTextNode(child) : child);
+ }
+ }
+
+ for (var i = startIndex; i < args.length; i++) {
+ var arg = args[i];
+ // TODO(attila): Fix isArrayLike to return false for a text node.
+ if (goog.isArrayLike(arg) && !goog.dom.isNodeLike(arg)) {
+ // If the argument is a node list, not a real array, use a clone,
+ // because forEach can't be used to mutate a NodeList.
+ goog.array.forEach(
+ goog.dom.isNodeList(arg) ? goog.array.toArray(arg) : arg,
+ childHandler);
+ } else {
+ childHandler(arg);
+ }
+ }
+};
+
+
+/**
+ * Alias for {@code createDom}.
+ * @param {string|!goog.dom.TagName<T>} tagName Tag to create.
+ * @param {?Object|?Array<string>|string=} opt_attributes If object, then a map
+ * of name-value pairs for attributes. If a string, then this is the
+ * className of the new element. If an array, the elements will be joined
+ * together as the className of the new element.
+ * @param {...(Object|string|Array|NodeList)} var_args Further DOM nodes or
+ * strings for text nodes. If one of the var_args is an array, its
+ * children will be added as childNodes instead.
+ * @return {R} Reference to a DOM node. The return type is {!Element} if tagName
+ * is a string or a more specific type if it is a member of
+ * goog.dom.TagName (e.g. {!HTMLAnchorElement} for goog.dom.TagName.A).
+ * @template T
+ * @template R := cond(isUnknown(T), 'Element', T) =:
+ * @deprecated Use {@link goog.dom.createDom} instead.
+ */
+goog.dom.$dom = goog.dom.createDom;
+
+
+/**
+ * Creates a new element.
+ * @param {string|!goog.dom.TagName<T>} name Tag to create.
+ * @return {R} The new element. The return type is {!Element} if name is
+ * a string or a more specific type if it is a member of goog.dom.TagName
+ * (e.g. {!HTMLAnchorElement} for goog.dom.TagName.A).
+ * @template T
+ * @template R := cond(isUnknown(T), 'Element', T) =:
+ */
+goog.dom.createElement = function(name) {
+ return goog.dom.createElement_(document, name);
+};
+
+
+/**
+ * Creates a new element.
+ * @param {!Document} doc The document to create the element in.
+ * @param {string|!goog.dom.TagName<T>} name Tag to create.
+ * @return {R} The new element. The return type is {!Element} if name is
+ * a string or a more specific type if it is a member of goog.dom.TagName
+ * (e.g. {!HTMLAnchorElement} for goog.dom.TagName.A).
+ * @template T
+ * @template R := cond(isUnknown(T), 'Element', T) =:
+ * @private
+ */
+goog.dom.createElement_ = function(doc, name) {
+ return doc.createElement(String(name));
+};
+
+
+/**
+ * Creates a new text node.
+ * @param {number|string} content Content.
+ * @return {!Text} The new text node.
+ */
+goog.dom.createTextNode = function(content) {
+ return document.createTextNode(String(content));
+};
+
+
+/**
+ * Create a table.
+ * @param {number} rows The number of rows in the table. Must be >= 1.
+ * @param {number} columns The number of columns in the table. Must be >= 1.
+ * @param {boolean=} opt_fillWithNbsp If true, fills table entries with
+ * {@code goog.string.Unicode.NBSP} characters.
+ * @return {!Element} The created table.
+ */
+goog.dom.createTable = function(rows, columns, opt_fillWithNbsp) {
+ // TODO(mlourenco): Return HTMLTableElement, also in prototype function.
+ // Callers need to be updated to e.g. not assign numbers to table.cellSpacing.
+ return goog.dom.createTable_(document, rows, columns, !!opt_fillWithNbsp);
+};
+
+
+/**
+ * Create a table.
+ * @param {!Document} doc Document object to use to create the table.
+ * @param {number} rows The number of rows in the table. Must be >= 1.
+ * @param {number} columns The number of columns in the table. Must be >= 1.
+ * @param {boolean} fillWithNbsp If true, fills table entries with
+ * {@code goog.string.Unicode.NBSP} characters.
+ * @return {!HTMLTableElement} The created table.
+ * @private
+ */
+goog.dom.createTable_ = function(doc, rows, columns, fillWithNbsp) {
+ var table = goog.dom.createElement_(doc, goog.dom.TagName.TABLE);
+ var tbody =
+ table.appendChild(goog.dom.createElement_(doc, goog.dom.TagName.TBODY));
+ for (var i = 0; i < rows; i++) {
+ var tr = goog.dom.createElement_(doc, goog.dom.TagName.TR);
+ for (var j = 0; j < columns; j++) {
+ var td = goog.dom.createElement_(doc, goog.dom.TagName.TD);
+ // IE <= 9 will create a text node if we set text content to the empty
+ // string, so we avoid doing it unless necessary. This ensures that the
+ // same DOM tree is returned on all browsers.
+ if (fillWithNbsp) {
+ goog.dom.setTextContent(td, goog.string.Unicode.NBSP);
+ }
+ tr.appendChild(td);
+ }
+ tbody.appendChild(tr);
+ }
+ return table;
+};
+
+
+
+/**
+ * Creates a new Node from constant strings of HTML markup.
+ * @param {...!goog.string.Const} var_args The HTML strings to concatenate then
+ * convert into a node.
+ * @return {!Node}
+ */
+goog.dom.constHtmlToNode = function(var_args) {
+ var stringArray = goog.array.map(arguments, goog.string.Const.unwrap);
+ var safeHtml =
+ goog.html.uncheckedconversions
+ .safeHtmlFromStringKnownToSatisfyTypeContract(
+ goog.string.Const.from(
+ 'Constant HTML string, that gets turned into a ' +
+ 'Node later, so it will be automatically balanced.'),
+ stringArray.join(''));
+ return goog.dom.safeHtmlToNode(safeHtml);
+};
+
+
+/**
+ * Converts HTML markup into a node. This is a safe version of
+ * {@code goog.dom.htmlToDocumentFragment} which is now deleted.
+ * @param {!goog.html.SafeHtml} html The HTML markup to convert.
+ * @return {!Node} The resulting node.
+ */
+goog.dom.safeHtmlToNode = function(html) {
+ return goog.dom.safeHtmlToNode_(document, html);
+};
+
+
+/**
+ * Helper for {@code safeHtmlToNode}.
+ * @param {!Document} doc The document.
+ * @param {!goog.html.SafeHtml} html The HTML markup to convert.
+ * @return {!Node} The resulting node.
+ * @private
+ */
+goog.dom.safeHtmlToNode_ = function(doc, html) {
+ var tempDiv = goog.dom.createElement_(doc, goog.dom.TagName.DIV);
+ if (goog.dom.BrowserFeature.INNER_HTML_NEEDS_SCOPED_ELEMENT) {
+ goog.dom.safe.setInnerHtml(
+ tempDiv, goog.html.SafeHtml.concat(goog.html.SafeHtml.BR, html));
+ tempDiv.removeChild(tempDiv.firstChild);
+ } else {
+ goog.dom.safe.setInnerHtml(tempDiv, html);
+ }
+ return goog.dom.childrenToNode_(doc, tempDiv);
+};
+
+
+/**
+ * Helper for {@code safeHtmlToNode_}.
+ * @param {!Document} doc The document.
+ * @param {!Node} tempDiv The input node.
+ * @return {!Node} The resulting node.
+ * @private
+ */
+goog.dom.childrenToNode_ = function(doc, tempDiv) {
+ if (tempDiv.childNodes.length == 1) {
+ return tempDiv.removeChild(tempDiv.firstChild);
+ } else {
+ var fragment = doc.createDocumentFragment();
+ while (tempDiv.firstChild) {
+ fragment.appendChild(tempDiv.firstChild);
+ }
+ return fragment;
+ }
+};
+
+
+/**
+ * Returns true if the browser is in "CSS1-compatible" (standards-compliant)
+ * mode, false otherwise.
+ * @return {boolean} True if in CSS1-compatible mode.
+ */
+goog.dom.isCss1CompatMode = function() {
+ return goog.dom.isCss1CompatMode_(document);
+};
+
+
+/**
+ * Returns true if the browser is in "CSS1-compatible" (standards-compliant)
+ * mode, false otherwise.
+ * @param {!Document} doc The document to check.
+ * @return {boolean} True if in CSS1-compatible mode.
+ * @private
+ */
+goog.dom.isCss1CompatMode_ = function(doc) {
+ if (goog.dom.COMPAT_MODE_KNOWN_) {
+ return goog.dom.ASSUME_STANDARDS_MODE;
+ }
+
+ return doc.compatMode == 'CSS1Compat';
+};
+
+
+/**
+ * Determines if the given node can contain children, intended to be used for
+ * HTML generation.
+ *
+ * IE natively supports node.canHaveChildren but has inconsistent behavior.
+ * Prior to IE8 the base tag allows children and in IE9 all nodes return true
+ * for canHaveChildren.
+ *
+ * In practice all non-IE browsers allow you to add children to any node, but
+ * the behavior is inconsistent:
+ *
+ * <pre>
+ * var a = goog.dom.createElement(goog.dom.TagName.BR);
+ * a.appendChild(document.createTextNode('foo'));
+ * a.appendChild(document.createTextNode('bar'));
+ * console.log(a.childNodes.length); // 2
+ * console.log(a.innerHTML); // Chrome: "", IE9: "foobar", FF3.5: "foobar"
+ * </pre>
+ *
+ * For more information, see:
+ * http://dev.w3.org/html5/markup/syntax.html#syntax-elements
+ *
+ * TODO(pupius): Rename shouldAllowChildren() ?
+ *
+ * @param {Node} node The node to check.
+ * @return {boolean} Whether the node can contain children.
+ */
+goog.dom.canHaveChildren = function(node) {
+ if (node.nodeType != goog.dom.NodeType.ELEMENT) {
+ return false;
+ }
+ switch (/** @type {!Element} */ (node).tagName) {
+ case String(goog.dom.TagName.APPLET):
+ case String(goog.dom.TagName.AREA):
+ case String(goog.dom.TagName.BASE):
+ case String(goog.dom.TagName.BR):
+ case String(goog.dom.TagName.COL):
+ case String(goog.dom.TagName.COMMAND):
+ case String(goog.dom.TagName.EMBED):
+ case String(goog.dom.TagName.FRAME):
+ case String(goog.dom.TagName.HR):
+ case String(goog.dom.TagName.IMG):
+ case String(goog.dom.TagName.INPUT):
+ case String(goog.dom.TagName.IFRAME):
+ case String(goog.dom.TagName.ISINDEX):
+ case String(goog.dom.TagName.KEYGEN):
+ case String(goog.dom.TagName.LINK):
+ case String(goog.dom.TagName.NOFRAMES):
+ case String(goog.dom.TagName.NOSCRIPT):
+ case String(goog.dom.TagName.META):
+ case String(goog.dom.TagName.OBJECT):
+ case String(goog.dom.TagName.PARAM):
+ case String(goog.dom.TagName.SCRIPT):
+ case String(goog.dom.TagName.SOURCE):
+ case String(goog.dom.TagName.STYLE):
+ case String(goog.dom.TagName.TRACK):
+ case String(goog.dom.TagName.WBR):
+ return false;
+ }
+ return true;
+};
+
+
+/**
+ * Appends a child to a node.
+ * @param {Node} parent Parent.
+ * @param {Node} child Child.
+ */
+goog.dom.appendChild = function(parent, child) {
+ parent.appendChild(child);
+};
+
+
+/**
+ * Appends a node with text or other nodes.
+ * @param {!Node} parent The node to append nodes to.
+ * @param {...goog.dom.Appendable} var_args The things to append to the node.
+ * If this is a Node it is appended as is.
+ * If this is a string then a text node is appended.
+ * If this is an array like object then fields 0 to length - 1 are appended.
+ */
+goog.dom.append = function(parent, var_args) {
+ goog.dom.append_(goog.dom.getOwnerDocument(parent), parent, arguments, 1);
+};
+
+
+/**
+ * Removes all the child nodes on a DOM node.
+ * @param {Node} node Node to remove children from.
+ */
+goog.dom.removeChildren = function(node) {
+ // Note: Iterations over live collections can be slow, this is the fastest
+ // we could find. The double parenthesis are used to prevent JsCompiler and
+ // strict warnings.
+ var child;
+ while ((child = node.firstChild)) {
+ node.removeChild(child);
+ }
+};
+
+
+/**
+ * Inserts a new node before an existing reference node (i.e. as the previous
+ * sibling). If the reference node has no parent, then does nothing.
+ * @param {Node} newNode Node to insert.
+ * @param {Node} refNode Reference node to insert before.
+ */
+goog.dom.insertSiblingBefore = function(newNode, refNode) {
+ if (refNode.parentNode) {
+ refNode.parentNode.insertBefore(newNode, refNode);
+ }
+};
+
+
+/**
+ * Inserts a new node after an existing reference node (i.e. as the next
+ * sibling). If the reference node has no parent, then does nothing.
+ * @param {Node} newNode Node to insert.
+ * @param {Node} refNode Reference node to insert after.
+ */
+goog.dom.insertSiblingAfter = function(newNode, refNode) {
+ if (refNode.parentNode) {
+ refNode.parentNode.insertBefore(newNode, refNode.nextSibling);
+ }
+};
+
+
+/**
+ * Insert a child at a given index. If index is larger than the number of child
+ * nodes that the parent currently has, the node is inserted as the last child
+ * node.
+ * @param {Element} parent The element into which to insert the child.
+ * @param {Node} child The element to insert.
+ * @param {number} index The index at which to insert the new child node. Must
+ * not be negative.
+ */
+goog.dom.insertChildAt = function(parent, child, index) {
+ // Note that if the second argument is null, insertBefore
+ // will append the child at the end of the list of children.
+ parent.insertBefore(child, parent.childNodes[index] || null);
+};
+
+
+/**
+ * Removes a node from its parent.
+ * @param {Node} node The node to remove.
+ * @return {Node} The node removed if removed; else, null.
+ */
+goog.dom.removeNode = function(node) {
+ return node && node.parentNode ? node.parentNode.removeChild(node) : null;
+};
+
+
+/**
+ * Replaces a node in the DOM tree. Will do nothing if {@code oldNode} has no
+ * parent.
+ * @param {Node} newNode Node to insert.
+ * @param {Node} oldNode Node to replace.
+ */
+goog.dom.replaceNode = function(newNode, oldNode) {
+ var parent = oldNode.parentNode;
+ if (parent) {
+ parent.replaceChild(newNode, oldNode);
+ }
+};
+
+
+/**
+ * Flattens an element. That is, removes it and replace it with its children.
+ * Does nothing if the element is not in the document.
+ * @param {Element} element The element to flatten.
+ * @return {Element|undefined} The original element, detached from the document
+ * tree, sans children; or undefined, if the element was not in the document
+ * to begin with.
+ */
+goog.dom.flattenElement = function(element) {
+ var child, parent = element.parentNode;
+ if (parent && parent.nodeType != goog.dom.NodeType.DOCUMENT_FRAGMENT) {
+ // Use IE DOM method (supported by Opera too) if available
+ if (element.removeNode) {
+ return /** @type {Element} */ (element.removeNode(false));
+ } else {
+ // Move all children of the original node up one level.
+ while ((child = element.firstChild)) {
+ parent.insertBefore(child, element);
+ }
+
+ // Detach the original element.
+ return /** @type {Element} */ (goog.dom.removeNode(element));
+ }
+ }
+};
+
+
+/**
+ * Returns an array containing just the element children of the given element.
+ * @param {Element} element The element whose element children we want.
+ * @return {!(Array<!Element>|NodeList<!Element>)} An array or array-like list
+ * of just the element children of the given element.
+ */
+goog.dom.getChildren = function(element) {
+ // We check if the children attribute is supported for child elements
+ // since IE8 misuses the attribute by also including comments.
+ if (goog.dom.BrowserFeature.CAN_USE_CHILDREN_ATTRIBUTE &&
+ element.children != undefined) {
+ return element.children;
+ }
+ // Fall back to manually filtering the element's child nodes.
+ return goog.array.filter(element.childNodes, function(node) {
+ return node.nodeType == goog.dom.NodeType.ELEMENT;
+ });
+};
+
+
+/**
+ * Returns the first child node that is an element.
+ * @param {Node} node The node to get the first child element of.
+ * @return {Element} The first child node of {@code node} that is an element.
+ */
+goog.dom.getFirstElementChild = function(node) {
+ if (goog.isDef(node.firstElementChild)) {
+ return /** @type {!Element} */ (node).firstElementChild;
+ }
+ return goog.dom.getNextElementNode_(node.firstChild, true);
+};
+
+
+/**
+ * Returns the last child node that is an element.
+ * @param {Node} node The node to get the last child element of.
+ * @return {Element} The last child node of {@code node} that is an element.
+ */
+goog.dom.getLastElementChild = function(node) {
+ if (goog.isDef(node.lastElementChild)) {
+ return /** @type {!Element} */ (node).lastElementChild;
+ }
+ return goog.dom.getNextElementNode_(node.lastChild, false);
+};
+
+
+/**
+ * Returns the first next sibling that is an element.
+ * @param {Node} node The node to get the next sibling element of.
+ * @return {Element} The next sibling of {@code node} that is an element.
+ */
+goog.dom.getNextElementSibling = function(node) {
+ if (goog.isDef(node.nextElementSibling)) {
+ return /** @type {!Element} */ (node).nextElementSibling;
+ }
+ return goog.dom.getNextElementNode_(node.nextSibling, true);
+};
+
+
+/**
+ * Returns the first previous sibling that is an element.
+ * @param {Node} node The node to get the previous sibling element of.
+ * @return {Element} The first previous sibling of {@code node} that is
+ * an element.
+ */
+goog.dom.getPreviousElementSibling = function(node) {
+ if (goog.isDef(node.previousElementSibling)) {
+ return /** @type {!Element} */ (node).previousElementSibling;
+ }
+ return goog.dom.getNextElementNode_(node.previousSibling, false);
+};
+
+
+/**
+ * Returns the first node that is an element in the specified direction,
+ * starting with {@code node}.
+ * @param {Node} node The node to get the next element from.
+ * @param {boolean} forward Whether to look forwards or backwards.
+ * @return {Element} The first element.
+ * @private
+ */
+goog.dom.getNextElementNode_ = function(node, forward) {
+ while (node && node.nodeType != goog.dom.NodeType.ELEMENT) {
+ node = forward ? node.nextSibling : node.previousSibling;
+ }
+
+ return /** @type {Element} */ (node);
+};
+
+
+/**
+ * Returns the next node in source order from the given node.
+ * @param {Node} node The node.
+ * @return {Node} The next node in the DOM tree, or null if this was the last
+ * 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;
+};
+
+
+/**
+ * Returns the previous node in source order from the given node.
+ * @param {Node} node The node.
+ * @return {Node} The previous node in the DOM tree, or null if this was the
+ * first node.
+ */
+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;
+};
+
+
+/**
+ * Whether the object looks like a DOM node.
+ * @param {?} obj The object being tested for node likeness.
+ * @return {boolean} Whether the object looks like a DOM node.
+ */
+goog.dom.isNodeLike = function(obj) {
+ return goog.isObject(obj) && obj.nodeType > 0;
+};
+
+
+/**
+ * Whether the object looks like an Element.
+ * @param {?} obj The object being tested for Element likeness.
+ * @return {boolean} Whether the object looks like an Element.
+ */
+goog.dom.isElement = function(obj) {
+ return goog.isObject(obj) && obj.nodeType == goog.dom.NodeType.ELEMENT;
+};
+
+
+/**
+ * Returns true if the specified value is a Window object. This includes the
+ * global window for HTML pages, and iframe windows.
+ * @param {?} obj Variable to test.
+ * @return {boolean} Whether the variable is a window.
+ */
+goog.dom.isWindow = function(obj) {
+ return goog.isObject(obj) && obj['window'] == obj;
+};
+
+
+/**
+ * Returns an element's parent, if it's an Element.
+ * @param {Element} element The DOM element.
+ * @return {Element} The parent, or null if not an Element.
+ */
+goog.dom.getParentElement = function(element) {
+ var parent;
+ if (goog.dom.BrowserFeature.CAN_USE_PARENT_ELEMENT_PROPERTY) {
+ var isIe9 = goog.userAgent.IE && goog.userAgent.isVersionOrHigher('9') &&
+ !goog.userAgent.isVersionOrHigher('10');
+ // SVG elements in IE9 can't use the parentElement property.
+ // goog.global['SVGElement'] is not defined in IE9 quirks mode.
+ if (!(isIe9 && goog.global['SVGElement'] &&
+ element instanceof goog.global['SVGElement'])) {
+ parent = element.parentElement;
+ if (parent) {
+ return parent;
+ }
+ }
+ }
+ parent = element.parentNode;
+ return goog.dom.isElement(parent) ? /** @type {!Element} */ (parent) : null;
+};
+
+
+/**
+ * Whether a node contains another node.
+ * @param {?Node|undefined} parent The node that should contain the other node.
+ * @param {?Node|undefined} descendant The node to test presence of.
+ * @return {boolean} Whether the parent node contains the descendent node.
+ */
+goog.dom.contains = function(parent, descendant) {
+ if (!parent || !descendant) {
+ return false;
+ }
+ // We use browser specific methods for this if available since it is faster
+ // that way.
+
+ // IE DOM
+ if (parent.contains && descendant.nodeType == goog.dom.NodeType.ELEMENT) {
+ return parent == descendant || parent.contains(descendant);
+ }
+
+ // W3C DOM Level 3
+ if (typeof parent.compareDocumentPosition != 'undefined') {
+ return parent == descendant ||
+ Boolean(parent.compareDocumentPosition(descendant) & 16);
+ }
+
+ // W3C DOM Level 1
+ while (descendant && parent != descendant) {
+ descendant = descendant.parentNode;
+ }
+ return descendant == parent;
+};
+
+
+/**
+ * Compares the document order of two nodes, returning 0 if they are the same
+ * node, a negative number if node1 is before node2, and a positive number if
+ * node2 is before node1. Note that we compare the order the tags appear in the
+ * document so in the tree <b><i>text</i></b> the B node is considered to be
+ * before the I node.
+ *
+ * @param {Node} node1 The first node to compare.
+ * @param {Node} node2 The second node to compare.
+ * @return {number} 0 if the nodes are the same node, a negative number if node1
+ * is before node2, and a positive number if node2 is before node1.
+ */
+goog.dom.compareNodeOrder = function(node1, node2) {
+ // Fall out quickly for equality.
+ if (node1 == node2) {
+ return 0;
+ }
+
+ // Use compareDocumentPosition where available
+ if (node1.compareDocumentPosition) {
+ // 4 is the bitmask for FOLLOWS.
+ return node1.compareDocumentPosition(node2) & 2 ? 1 : -1;
+ }
+
+ // Special case for document nodes on IE 7 and 8.
+ if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9)) {
+ if (node1.nodeType == goog.dom.NodeType.DOCUMENT) {
+ return -1;
+ }
+ if (node2.nodeType == goog.dom.NodeType.DOCUMENT) {
+ return 1;
+ }
+ }
+
+ // Process in IE using sourceIndex - we check to see if the first node has
+ // a source index or if its parent has one.
+ 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);
+ }
+ }
+
+ // For Safari, we compare ranges.
+ 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);
+};
+
+
+/**
+ * Utility function to compare the position of two nodes, when
+ * {@code textNode}'s parent is an ancestor of {@code node}. If this entry
+ * condition is not met, this function will attempt to reference a null object.
+ * @param {!Node} textNode The textNode to compare.
+ * @param {Node} node The node to compare.
+ * @return {number} -1 if node is before textNode, +1 otherwise.
+ * @private
+ */
+goog.dom.compareParentsDescendantNodeIe_ = function(textNode, node) {
+ var parent = textNode.parentNode;
+ if (parent == node) {
+ // If textNode is a child of node, then node comes first.
+ return -1;
+ }
+ var sibling = node;
+ while (sibling.parentNode != parent) {
+ sibling = sibling.parentNode;
+ }
+ return goog.dom.compareSiblingOrder_(sibling, textNode);
+};
+
+
+/**
+ * Utility function to compare the position of two nodes known to be non-equal
+ * siblings.
+ * @param {Node} node1 The first node to compare.
+ * @param {!Node} node2 The second node to compare.
+ * @return {number} -1 if node1 is before node2, +1 otherwise.
+ * @private
+ */
+goog.dom.compareSiblingOrder_ = function(node1, node2) {
+ var s = node2;
+ while ((s = s.previousSibling)) {
+ if (s == node1) {
+ // We just found node1 before node2.
+ return -1;
+ }
+ }
+
+ // Since we didn't find it, node1 must be after node2.
+ return 1;
+};
+
+
+/**
+ * Find the deepest common ancestor of the given nodes.
+ * @param {...Node} var_args The nodes to find a common ancestor of.
+ * @return {Node} The common ancestor of the nodes, or null if there is none.
+ * null will only be returned if two or more of the nodes are from different
+ * documents.
+ */
+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++) {
+ // Compute the list of ancestors.
+ var ancestors = [];
+ var node = arguments[i];
+ while (node) {
+ ancestors.unshift(node);
+ node = node.parentNode;
+ }
+
+ // Save the list for comparison.
+ 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;
+};
+
+
+/**
+ * Returns the owner document for a node.
+ * @param {Node|Window} node The node to get the document for.
+ * @return {!Document} The document owning the node.
+ */
+goog.dom.getOwnerDocument = function(node) {
+ // TODO(nnaze): Update param signature to be non-nullable.
+ goog.asserts.assert(node, 'Node cannot be null or undefined.');
+ return /** @type {!Document} */ (
+ node.nodeType == goog.dom.NodeType.DOCUMENT ? node : node.ownerDocument ||
+ node.document);
+};
+
+
+/**
+ * Cross-browser function for getting the document element of a frame or iframe.
+ * @param {Element} frame Frame element.
+ * @return {!Document} The frame content document.
+ */
+goog.dom.getFrameContentDocument = function(frame) {
+ return frame.contentDocument ||
+ /** @type {!HTMLFrameElement} */ (frame).contentWindow.document;
+};
+
+
+/**
+ * Cross-browser function for getting the window of a frame or iframe.
+ * @param {Element} frame Frame element.
+ * @return {Window} The window associated with the given frame, or null if none
+ * exists.
+ */
+goog.dom.getFrameContentWindow = function(frame) {
+ try {
+ return frame.contentWindow ||
+ (frame.contentDocument ? goog.dom.getWindow(frame.contentDocument) :
+ null);
+ } catch (e) {
+ // NOTE(jfedor): In IE8, checking the contentWindow or contentDocument
+ // properties will throw a "Unspecified Error" exception if the iframe is
+ // not inserted in the DOM. If we get this we can be sure that no window
+ // exists, so return null.
+ }
+ return null;
+};
+
+
+/**
+ * Sets the text content of a node, with cross-browser support.
+ * @param {Node} node The node to change the text content of.
+ * @param {string|number} text The value that should replace the node's content.
+ */
+goog.dom.setTextContent = function(node, text) {
+ goog.asserts.assert(
+ node != null,
+ 'goog.dom.setTextContent expects a non-null value for node');
+
+ if ('textContent' in node) {
+ node.textContent = text;
+ } else if (node.nodeType == goog.dom.NodeType.TEXT) {
+ /** @type {!Text} */ (node).data = String(text);
+ } else if (
+ node.firstChild && node.firstChild.nodeType == goog.dom.NodeType.TEXT) {
+ // If the first child is a text node we just change its data and remove the
+ // rest of the children.
+ while (node.lastChild != node.firstChild) {
+ node.removeChild(node.lastChild);
+ }
+ /** @type {!Text} */ (node.firstChild).data = String(text);
+ } else {
+ goog.dom.removeChildren(node);
+ var doc = goog.dom.getOwnerDocument(node);
+ node.appendChild(doc.createTextNode(String(text)));
+ }
+};
+
+
+/**
+ * Gets the outerHTML of a node, which islike innerHTML, except that it
+ * actually contains the HTML of the node itself.
+ * @param {Element} element The element to get the HTML of.
+ * @return {string} The outerHTML of the given element.
+ */
+goog.dom.getOuterHtml = function(element) {
+ goog.asserts.assert(
+ element !== null,
+ 'goog.dom.getOuterHtml expects a non-null value for element');
+ // IE, Opera and WebKit all have outerHTML.
+ if ('outerHTML' in element) {
+ return element.outerHTML;
+ } else {
+ var doc = goog.dom.getOwnerDocument(element);
+ var div = goog.dom.createElement_(doc, goog.dom.TagName.DIV);
+ div.appendChild(element.cloneNode(true));
+ return div.innerHTML;
+ }
+};
+
+
+/**
+ * Finds the first descendant node that matches the filter function, using
+ * a depth first search. This function offers the most general purpose way
+ * of finding a matching element. You may also wish to consider
+ * {@code goog.dom.query} which can express many matching criteria using
+ * CSS selector expressions. These expressions often result in a more
+ * compact representation of the desired result.
+ * @see goog.dom.query
+ *
+ * @param {Node} root The root of the tree to search.
+ * @param {function(Node) : boolean} p The filter function.
+ * @return {Node|undefined} The found node or undefined if none is found.
+ */
+goog.dom.findNode = function(root, p) {
+ var rv = [];
+ var found = goog.dom.findNodes_(root, p, rv, true);
+ return found ? rv[0] : undefined;
+};
+
+
+/**
+ * Finds all the descendant nodes that match the filter function, using a
+ * a depth first search. This function offers the most general-purpose way
+ * of finding a set of matching elements. You may also wish to consider
+ * {@code goog.dom.query} which can express many matching criteria using
+ * CSS selector expressions. These expressions often result in a more
+ * compact representation of the desired result.
+
+ * @param {Node} root The root of the tree to search.
+ * @param {function(Node) : boolean} p The filter function.
+ * @return {!Array<!Node>} The found nodes or an empty array if none are found.
+ */
+goog.dom.findNodes = function(root, p) {
+ var rv = [];
+ goog.dom.findNodes_(root, p, rv, false);
+ return rv;
+};
+
+
+/**
+ * Finds the first or all the descendant nodes that match the filter function,
+ * using a depth first search.
+ * @param {Node} root The root of the tree to search.
+ * @param {function(Node) : boolean} p The filter function.
+ * @param {!Array<!Node>} rv The found nodes are added to this array.
+ * @param {boolean} findOne If true we exit after the first found node.
+ * @return {boolean} Whether the search is complete or not. True in case findOne
+ * is true and the node is found. False otherwise.
+ * @private
+ */
+goog.dom.findNodes_ = function(root, p, rv, findOne) {
+ if (root != null) {
+ var child = root.firstChild;
+ while (child) {
+ if (p(child)) {
+ rv.push(child);
+ if (findOne) {
+ return true;
+ }
+ }
+ if (goog.dom.findNodes_(child, p, rv, findOne)) {
+ return true;
+ }
+ child = child.nextSibling;
+ }
+ }
+ return false;
+};
+
+
+/**
+ * Map of tags whose content to ignore when calculating text length.
+ * @private {!Object<string, number>}
+ * @const
+ */
+goog.dom.TAGS_TO_IGNORE_ = {
+ 'SCRIPT': 1,
+ 'STYLE': 1,
+ 'HEAD': 1,
+ 'IFRAME': 1,
+ 'OBJECT': 1
+};
+
+
+/**
+ * Map of tags which have predefined values with regard to whitespace.
+ * @private {!Object<string, string>}
+ * @const
+ */
+goog.dom.PREDEFINED_TAG_VALUES_ = {
+ 'IMG': ' ',
+ 'BR': '\n'
+};
+
+
+/**
+ * Returns true if the element has a tab index that allows it to receive
+ * keyboard focus (tabIndex >= 0), false otherwise. Note that some elements
+ * natively support keyboard focus, even if they have no tab index.
+ * @param {!Element} element Element to check.
+ * @return {boolean} Whether the element has a tab index that allows keyboard
+ * focus.
+ */
+goog.dom.isFocusableTabIndex = function(element) {
+ return goog.dom.hasSpecifiedTabIndex_(element) &&
+ goog.dom.isTabIndexFocusable_(element);
+};
+
+
+/**
+ * Enables or disables keyboard focus support on the element via its tab index.
+ * Only elements for which {@link goog.dom.isFocusableTabIndex} returns true
+ * (or elements that natively support keyboard focus, like form elements) can
+ * receive keyboard focus. See http://go/tabindex for more info.
+ * @param {Element} element Element whose tab index is to be changed.
+ * @param {boolean} enable Whether to set or remove a tab index on the element
+ * that supports keyboard focus.
+ */
+goog.dom.setFocusableTabIndex = function(element, enable) {
+ if (enable) {
+ element.tabIndex = 0;
+ } else {
+ // Set tabIndex to -1 first, then remove it. This is a workaround for
+ // Safari (confirmed in version 4 on Windows). When removing the attribute
+ // without setting it to -1 first, the element remains keyboard focusable
+ // despite not having a tabIndex attribute anymore.
+ element.tabIndex = -1;
+ element.removeAttribute('tabIndex'); // Must be camelCase!
+ }
+};
+
+
+/**
+ * Returns true if the element can be focused, i.e. it has a tab index that
+ * allows it to receive keyboard focus (tabIndex >= 0), or it is an element
+ * that natively supports keyboard focus.
+ * @param {!Element} element Element to check.
+ * @return {boolean} Whether the element allows keyboard focus.
+ */
+goog.dom.isFocusable = function(element) {
+ var focusable;
+ // Some elements can have unspecified tab index and still receive focus.
+ if (goog.dom.nativelySupportsFocus_(element)) {
+ // Make sure the element is not disabled ...
+ focusable = !element.disabled &&
+ // ... and if a tab index is specified, it allows focus.
+ (!goog.dom.hasSpecifiedTabIndex_(element) ||
+ goog.dom.isTabIndexFocusable_(element));
+ } else {
+ focusable = goog.dom.isFocusableTabIndex(element);
+ }
+
+ // IE requires elements to be visible in order to focus them.
+ return focusable && goog.userAgent.IE ?
+ goog.dom.hasNonZeroBoundingRect_(/** @type {!HTMLElement} */ (element)) :
+ focusable;
+};
+
+
+/**
+ * Returns true if the element has a specified tab index.
+ * @param {!Element} element Element to check.
+ * @return {boolean} Whether the element has a specified tab index.
+ * @private
+ */
+goog.dom.hasSpecifiedTabIndex_ = function(element) {
+ // IE8 and below don't support hasAttribute(), instead check whether the
+ // 'tabindex' attributeNode is specified. Otherwise check hasAttribute().
+ if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9')) {
+ var attrNode = element.getAttributeNode('tabindex'); // Must be lowercase!
+ return goog.isDefAndNotNull(attrNode) && attrNode.specified;
+ } else {
+ return element.hasAttribute('tabindex');
+ }
+};
+
+
+/**
+ * Returns true if the element's tab index allows the element to be focused.
+ * @param {!Element} element Element to check.
+ * @return {boolean} Whether the element's tab index allows focus.
+ * @private
+ */
+goog.dom.isTabIndexFocusable_ = function(element) {
+ var index = /** @type {!HTMLElement} */ (element).tabIndex;
+ // NOTE: IE9 puts tabIndex in 16-bit int, e.g. -2 is 65534.
+ return goog.isNumber(index) && index >= 0 && index < 32768;
+};
+
+
+/**
+ * Returns true if the element is focusable even when tabIndex is not set.
+ * @param {!Element} element Element to check.
+ * @return {boolean} Whether the element natively supports focus.
+ * @private
+ */
+goog.dom.nativelySupportsFocus_ = function(element) {
+ return element.tagName == goog.dom.TagName.A ||
+ element.tagName == goog.dom.TagName.INPUT ||
+ element.tagName == goog.dom.TagName.TEXTAREA ||
+ element.tagName == goog.dom.TagName.SELECT ||
+ element.tagName == goog.dom.TagName.BUTTON;
+};
+
+
+/**
+ * Returns true if the element has a bounding rectangle that would be visible
+ * (i.e. its width and height are greater than zero).
+ * @param {!HTMLElement} element Element to check.
+ * @return {boolean} Whether the element has a non-zero bounding rectangle.
+ * @private
+ */
+goog.dom.hasNonZeroBoundingRect_ = function(element) {
+ var rect;
+ if (!goog.isFunction(element['getBoundingClientRect']) ||
+ // In IE, getBoundingClientRect throws on detached nodes.
+ (goog.userAgent.IE && element.parentElement == null)) {
+ rect = {'height': element.offsetHeight, 'width': element.offsetWidth};
+ } else {
+ rect = element.getBoundingClientRect();
+ }
+ return goog.isDefAndNotNull(rect) && rect.height > 0 && rect.width > 0;
+};
+
+
+/**
+ * Returns the text content of the current node, without markup and invisible
+ * symbols. New lines are stripped and whitespace is collapsed,
+ * such that each character would be visible.
+ *
+ * In browsers that support it, innerText is used. Other browsers attempt to
+ * simulate it via node traversal. Line breaks are canonicalized in IE.
+ *
+ * @param {Node} node The node from which we are getting content.
+ * @return {string} The text content.
+ */
+goog.dom.getTextContent = function(node) {
+ var textContent;
+ // Note(arv): IE9, Opera, and Safari 3 support innerText but they include
+ // text nodes in script tags. So we revert to use a user agent test here.
+ if (goog.dom.BrowserFeature.CAN_USE_INNER_TEXT && node !== null &&
+ ('innerText' in node)) {
+ textContent = goog.string.canonicalizeNewlines(node.innerText);
+ // Unfortunately .innerText() returns text with &shy; symbols
+ // We need to filter it out and then remove duplicate whitespaces
+ } else {
+ var buf = [];
+ goog.dom.getTextContent_(node, buf, true);
+ textContent = buf.join('');
+ }
+
+ // Strip &shy; entities. goog.format.insertWordBreaks inserts them in Opera.
+ textContent = textContent.replace(/ \xAD /g, ' ').replace(/\xAD/g, '');
+ // Strip &#8203; entities. goog.format.insertWordBreaks inserts them in IE8.
+ textContent = textContent.replace(/\u200B/g, '');
+
+ // Skip this replacement on old browsers with working innerText, which
+ // automatically turns &nbsp; into ' ' and / +/ into ' ' when reading
+ // innerText.
+ if (!goog.dom.BrowserFeature.CAN_USE_INNER_TEXT) {
+ textContent = textContent.replace(/ +/g, ' ');
+ }
+ if (textContent != ' ') {
+ textContent = textContent.replace(/^\s*/, '');
+ }
+
+ return textContent;
+};
+
+
+/**
+ * Returns the text content of the current node, without markup.
+ *
+ * Unlike {@code getTextContent} this method does not collapse whitespaces
+ * or normalize lines breaks.
+ *
+ * @param {Node} node The node from which we are getting content.
+ * @return {string} The raw text content.
+ */
+goog.dom.getRawTextContent = function(node) {
+ var buf = [];
+ goog.dom.getTextContent_(node, buf, false);
+
+ return buf.join('');
+};
+
+
+/**
+ * Recursive support function for text content retrieval.
+ *
+ * @param {Node} node The node from which we are getting content.
+ * @param {Array<string>} buf string buffer.
+ * @param {boolean} normalizeWhitespace Whether to normalize whitespace.
+ * @private
+ */
+goog.dom.getTextContent_ = function(node, buf, normalizeWhitespace) {
+ if (node.nodeName in goog.dom.TAGS_TO_IGNORE_) {
+ // ignore certain tags
+ } 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;
+ }
+ }
+};
+
+
+/**
+ * Returns the text length of the text contained in a node, without markup. This
+ * is equivalent to the selection length if the node was selected, or the number
+ * of cursor movements to traverse the node. Images & BRs take one space. New
+ * lines are ignored.
+ *
+ * @param {Node} node The node whose text content length is being calculated.
+ * @return {number} The length of {@code node}'s text content.
+ */
+goog.dom.getNodeTextLength = function(node) {
+ return goog.dom.getTextContent(node).length;
+};
+
+
+/**
+ * Returns the text offset of a node relative to one of its ancestors. The text
+ * length is the same as the length calculated by goog.dom.getNodeTextLength.
+ *
+ * @param {Node} node The node whose offset is being calculated.
+ * @param {Node=} opt_offsetParent The node relative to which the offset will
+ * be calculated. Defaults to the node's owner document's body.
+ * @return {number} The text offset.
+ */
+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;
+ }
+ // Trim left to deal with FF cases when there might be line breaks and empty
+ // nodes at the front of the text
+ return goog.string.trimLeft(buf.join('')).replace(/ +/g, ' ').length;
+};
+
+
+/**
+ * Returns the node at a given offset in a parent node. If an object is
+ * provided for the optional third parameter, the node and the remainder of the
+ * offset will stored as properties of this object.
+ * @param {Node} parent The parent node.
+ * @param {number} offset The offset into the parent node.
+ * @param {Object=} opt_result Object to be used to store the return value. The
+ * return value will be stored in the form {node: Node, remainder: number}
+ * if this object is provided.
+ * @return {Node} The node at the given offset.
+ */
+goog.dom.getNodeAtOffset = function(parent, offset, opt_result) {
+ var stack = [parent], pos = 0, cur = null;
+ while (stack.length > 0 && pos < offset) {
+ cur = stack.pop();
+ if (cur.nodeName in goog.dom.TAGS_TO_IGNORE_) {
+ // ignore certain tags
+ } 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;
+};
+
+
+/**
+ * Returns true if the object is a {@code NodeList}. To qualify as a NodeList,
+ * the object must have a numeric length property and an item function (which
+ * has type 'string' on IE for some reason).
+ * @param {Object} val Object to test.
+ * @return {boolean} Whether the object is a NodeList.
+ */
+goog.dom.isNodeList = function(val) {
+ // TODO(attila): Now the isNodeList is part of goog.dom we can use
+ // goog.userAgent to make this simpler.
+ // A NodeList must have a length property of type 'number' on all platforms.
+ if (val && typeof val.length == 'number') {
+ // A NodeList is an object everywhere except Safari, where it's a function.
+ if (goog.isObject(val)) {
+ // A NodeList must have an item function (on non-IE platforms) or an item
+ // property of type 'string' (on IE).
+ return typeof val.item == 'function' || typeof val.item == 'string';
+ } else if (goog.isFunction(val)) {
+ // On Safari, a NodeList is a function with an item property that is also
+ // a function.
+ return typeof val.item == 'function';
+ }
+ }
+
+ // Not a NodeList.
+ return false;
+};
+
+
+/**
+ * Walks up the DOM hierarchy returning the first ancestor that has the passed
+ * tag name and/or class name. If the passed element matches the specified
+ * criteria, the element itself is returned.
+ * @param {Node} element The DOM node to start with.
+ * @param {?(goog.dom.TagName<T>|string)=} opt_tag The tag name to match (or
+ * null/undefined to match only based on class name).
+ * @param {?string=} opt_class The class name to match (or null/undefined to
+ * match only based on tag name).
+ * @param {number=} opt_maxSearchSteps Maximum number of levels to search up the
+ * dom.
+ * @return {?R} The first ancestor that matches the passed criteria, or
+ * null if no match is found. The return type is {?Element} if opt_tag is
+ * not a member of goog.dom.TagName or a more specific type if it is (e.g.
+ * {?HTMLAnchorElement} for goog.dom.TagName.A).
+ * @template T
+ * @template R := cond(isUnknown(T), 'Element', T) =:
+ */
+goog.dom.getAncestorByTagNameAndClass = function(
+ element, opt_tag, opt_class, opt_maxSearchSteps) {
+ if (!opt_tag && !opt_class) {
+ return null;
+ }
+ var tagName = opt_tag ? String(opt_tag).toUpperCase() : null;
+ return /** @type {Element} */ (goog.dom.getAncestor(element, function(node) {
+ return (!tagName || node.nodeName == tagName) &&
+ (!opt_class ||
+ goog.isString(node.className) &&
+ goog.array.contains(node.className.split(/\s+/), opt_class));
+ }, true, opt_maxSearchSteps));
+};
+
+
+/**
+ * Walks up the DOM hierarchy returning the first ancestor that has the passed
+ * class name. If the passed element matches the specified criteria, the
+ * element itself is returned.
+ * @param {Node} element The DOM node to start with.
+ * @param {string} className The class name to match.
+ * @param {number=} opt_maxSearchSteps Maximum number of levels to search up the
+ * dom.
+ * @return {Element} The first ancestor that matches the passed criteria, or
+ * null if none match.
+ */
+goog.dom.getAncestorByClass = function(element, className, opt_maxSearchSteps) {
+ return goog.dom.getAncestorByTagNameAndClass(
+ element, null, className, opt_maxSearchSteps);
+};
+
+
+/**
+ * Walks up the DOM hierarchy returning the first ancestor that passes the
+ * matcher function.
+ * @param {Node} element The DOM node to start with.
+ * @param {function(Node) : boolean} matcher A function that returns true if the
+ * passed node matches the desired criteria.
+ * @param {boolean=} opt_includeNode If true, the node itself is included in
+ * the search (the first call to the matcher will pass startElement as
+ * the node to test).
+ * @param {number=} opt_maxSearchSteps Maximum number of levels to search up the
+ * dom.
+ * @return {Node} DOM node that matched the matcher, or null if there was
+ * no match.
+ */
+goog.dom.getAncestor = function(
+ element, matcher, opt_includeNode, opt_maxSearchSteps) {
+ if (element && !opt_includeNode) {
+ element = element.parentNode;
+ }
+ var steps = 0;
+ while (element &&
+ (opt_maxSearchSteps == null || steps <= opt_maxSearchSteps)) {
+ goog.asserts.assert(element.name != 'parentNode');
+ if (matcher(element)) {
+ return element;
+ }
+ element = element.parentNode;
+ steps++;
+ }
+ // Reached the root of the DOM without a match
+ return null;
+};
+
+
+/**
+ * Determines the active element in the given document.
+ * @param {Document} doc The document to look in.
+ * @return {Element} The active element.
+ */
+goog.dom.getActiveElement = function(doc) {
+ try {
+ return doc && doc.activeElement;
+ } catch (e) {
+ // NOTE(nicksantos): Sometimes, evaluating document.activeElement in IE
+ // throws an exception. I'm not 100% sure why, but I suspect it chokes
+ // on document.activeElement if the activeElement has been recently
+ // removed from the DOM by a JS operation.
+ //
+ // We assume that an exception here simply means
+ // "there is no active element."
+ }
+
+ return null;
+};
+
+
+/**
+ * Gives the current devicePixelRatio.
+ *
+ * By default, this is the value of window.devicePixelRatio (which should be
+ * preferred if present).
+ *
+ * If window.devicePixelRatio is not present, the ratio is calculated with
+ * window.matchMedia, if present. Otherwise, gives 1.0.
+ *
+ * Some browsers (including Chrome) consider the browser zoom level in the pixel
+ * ratio, so the value may change across multiple calls.
+ *
+ * @return {number} The number of actual pixels per virtual pixel.
+ */
+goog.dom.getPixelRatio = function() {
+ var win = goog.dom.getWindow();
+ if (goog.isDef(win.devicePixelRatio)) {
+ return win.devicePixelRatio;
+ } else if (win.matchMedia) {
+ // Should be for IE10 and FF6-17 (this basically clamps to lower)
+ // Note that the order of these statements is important
+ return goog.dom.matchesPixelRatio_(3) || goog.dom.matchesPixelRatio_(2) ||
+ goog.dom.matchesPixelRatio_(1.5) || goog.dom.matchesPixelRatio_(1) ||
+ .75;
+ }
+ return 1;
+};
+
+
+/**
+ * Calculates a mediaQuery to check if the current device supports the
+ * given actual to virtual pixel ratio.
+ * @param {number} pixelRatio The ratio of actual pixels to virtual pixels.
+ * @return {number} pixelRatio if applicable, otherwise 0.
+ * @private
+ */
+goog.dom.matchesPixelRatio_ = function(pixelRatio) {
+ var win = goog.dom.getWindow();
+ /**
+ * Due to the 1:96 fixed ratio of CSS in to CSS px, 1dppx is equivalent to
+ * 96dpi.
+ * @const {number}
+ */
+ var dpiPerDppx = 96;
+ var query =
+ // FF16-17
+ '(min-resolution: ' + pixelRatio + 'dppx),' +
+ // FF6-15
+ '(min--moz-device-pixel-ratio: ' + pixelRatio + '),' +
+ // IE10 (this works for the two browsers above too but I don't want to
+ // trust the 1:96 fixed ratio magic)
+ '(min-resolution: ' + (pixelRatio * dpiPerDppx) + 'dpi)';
+ return win.matchMedia(query).matches ? pixelRatio : 0;
+};
+
+
+/**
+ * Gets '2d' context of a canvas. Shortcut for canvas.getContext('2d') with a
+ * type information.
+ * @param {!HTMLCanvasElement} canvas
+ * @return {!CanvasRenderingContext2D}
+ */
+goog.dom.getCanvasContext2D = function(canvas) {
+ return /** @type {!CanvasRenderingContext2D} */ (canvas.getContext('2d'));
+};
+
+
+
+/**
+ * Create an instance of a DOM helper with a new document object.
+ * @param {Document=} opt_document Document object to associate with this
+ * DOM helper.
+ * @constructor
+ */
+goog.dom.DomHelper = function(opt_document) {
+ /**
+ * Reference to the document object to use
+ * @type {!Document}
+ * @private
+ */
+ this.document_ = opt_document || goog.global.document || document;
+};
+
+
+/**
+ * Gets the dom helper object for the document where the element resides.
+ * @param {Node=} opt_node If present, gets the DomHelper for this node.
+ * @return {!goog.dom.DomHelper} The DomHelper.
+ */
+goog.dom.DomHelper.prototype.getDomHelper = goog.dom.getDomHelper;
+
+
+/**
+ * Sets the document object.
+ * @param {!Document} document Document object.
+ */
+goog.dom.DomHelper.prototype.setDocument = function(document) {
+ this.document_ = document;
+};
+
+
+/**
+ * Gets the document object being used by the dom library.
+ * @return {!Document} Document object.
+ */
+goog.dom.DomHelper.prototype.getDocument = function() {
+ return this.document_;
+};
+
+
+/**
+ * Alias for {@code getElementById}. If a DOM node is passed in then we just
+ * return that.
+ * @param {string|Element} element Element ID or a DOM node.
+ * @return {Element} The element with the given ID, or the node passed in.
+ */
+goog.dom.DomHelper.prototype.getElement = function(element) {
+ return goog.dom.getElementHelper_(this.document_, element);
+};
+
+
+/**
+ * Gets an element by id, asserting that the element is found.
+ *
+ * This is used when an element is expected to exist, and should fail with
+ * an assertion error if it does not (if assertions are enabled).
+ *
+ * @param {string} id Element ID.
+ * @return {!Element} The element with the given ID, if it exists.
+ */
+goog.dom.DomHelper.prototype.getRequiredElement = function(id) {
+ return goog.dom.getRequiredElementHelper_(this.document_, id);
+};
+
+
+/**
+ * Alias for {@code getElement}.
+ * @param {string|Element} element Element ID or a DOM node.
+ * @return {Element} The element with the given ID, or the node passed in.
+ * @deprecated Use {@link goog.dom.DomHelper.prototype.getElement} instead.
+ */
+goog.dom.DomHelper.prototype.$ = goog.dom.DomHelper.prototype.getElement;
+
+
+/**
+ * Gets elements by tag name.
+ * @param {!goog.dom.TagName<T>} tagName
+ * @param {(!Document|!Element)=} opt_parent Parent element or document where to
+ * look for elements. Defaults to document of this DomHelper.
+ * @return {!NodeList<R>} List of elements. The members of the list are
+ * {!Element} if tagName is not a member of goog.dom.TagName or more
+ * specific types if it is (e.g. {!HTMLAnchorElement} for
+ * goog.dom.TagName.A).
+ * @template T
+ * @template R := cond(isUnknown(T), 'Element', T) =:
+ */
+goog.dom.DomHelper.prototype.getElementsByTagName =
+ function(tagName, opt_parent) {
+ var parent = opt_parent || this.document_;
+ return parent.getElementsByTagName(String(tagName));
+};
+
+
+/**
+ * Looks up elements by both tag and class name, using browser native functions
+ * ({@code querySelectorAll}, {@code getElementsByTagName} or
+ * {@code getElementsByClassName}) where possible. The returned array is a live
+ * NodeList or a static list depending on the code path taken.
+ *
+ * @see goog.dom.query
+ *
+ * @param {(string|?goog.dom.TagName<T>)=} opt_tag Element tag name or * for all
+ * tags.
+ * @param {?string=} opt_class Optional class name.
+ * @param {(Document|Element)=} opt_el Optional element to look in.
+ * @return {!IArrayLike<R>} Array-like list of elements (only a length property
+ * and numerical indices are guaranteed to exist). The members of the array
+ * are {!Element} if opt_tag is not a member of goog.dom.TagName or more
+ * specific types if it is (e.g. {!HTMLAnchorElement} for
+ * goog.dom.TagName.A).
+ * @template T
+ * @template R := cond(isUnknown(T), 'Element', T) =:
+ */
+goog.dom.DomHelper.prototype.getElementsByTagNameAndClass = function(
+ opt_tag, opt_class, opt_el) {
+ return goog.dom.getElementsByTagNameAndClass_(
+ this.document_, opt_tag, opt_class, opt_el);
+};
+
+
+/**
+ * Gets the first element matching the tag and the class.
+ *
+ * @param {(string|?goog.dom.TagName<T>)=} opt_tag Element tag name.
+ * @param {?string=} opt_class Optional class name.
+ * @param {(Document|Element)=} opt_el Optional element to look in.
+ * @return {?R} Reference to a DOM node. The return type is {?Element} if
+ * tagName is a string or a more specific type if it is a member of
+ * goog.dom.TagName (e.g. {?HTMLAnchorElement} for goog.dom.TagName.A).
+ * @template T
+ * @template R := cond(isUnknown(T), 'Element', T) =:
+ */
+goog.dom.DomHelper.prototype.getElementByTagNameAndClass = function(
+ opt_tag, opt_class, opt_el) {
+ return goog.dom.getElementByTagNameAndClass_(
+ this.document_, opt_tag, opt_class, opt_el);
+};
+
+
+/**
+ * Returns an array of all the elements with the provided className.
+ * @see {goog.dom.query}
+ * @param {string} className the name of the class to look for.
+ * @param {Element|Document=} opt_el Optional element to look in.
+ * @return {!IArrayLike<!Element>} The items found with the class name provided.
+ */
+goog.dom.DomHelper.prototype.getElementsByClass = function(className, opt_el) {
+ var doc = opt_el || this.document_;
+ return goog.dom.getElementsByClass(className, doc);
+};
+
+
+/**
+ * Returns the first element we find matching the provided class name.
+ * @see {goog.dom.query}
+ * @param {string} className the name of the class to look for.
+ * @param {(Element|Document)=} opt_el Optional element to look in.
+ * @return {Element} The first item found with the class name provided.
+ */
+goog.dom.DomHelper.prototype.getElementByClass = function(className, opt_el) {
+ var doc = opt_el || this.document_;
+ return goog.dom.getElementByClass(className, doc);
+};
+
+
+/**
+ * Ensures an element with the given className exists, and then returns the
+ * first element with the provided className.
+ * @see {goog.dom.query}
+ * @param {string} className the name of the class to look for.
+ * @param {(!Element|!Document)=} opt_root Optional element or document to look
+ * in.
+ * @return {!Element} The first item found with the class name provided.
+ * @throws {goog.asserts.AssertionError} Thrown if no element is found.
+ */
+goog.dom.DomHelper.prototype.getRequiredElementByClass = function(
+ className, opt_root) {
+ var root = opt_root || this.document_;
+ return goog.dom.getRequiredElementByClass(className, root);
+};
+
+
+/**
+ * Alias for {@code getElementsByTagNameAndClass}.
+ * @deprecated Use DomHelper getElementsByTagNameAndClass.
+ * @see goog.dom.query
+ *
+ * @param {(string|?goog.dom.TagName<T>)=} opt_tag Element tag name.
+ * @param {?string=} opt_class Optional class name.
+ * @param {Element=} opt_el Optional element to look in.
+ * @return {!IArrayLike<R>} Array-like list of elements (only a length property
+ * and numerical indices are guaranteed to exist). The members of the array
+ * are {!Element} if opt_tag is a string or more specific types if it is
+ * a member of goog.dom.TagName (e.g. {!HTMLAnchorElement} for
+ * goog.dom.TagName.A).
+ * @template T
+ * @template R := cond(isUnknown(T), 'Element', T) =:
+ */
+goog.dom.DomHelper.prototype.$$ =
+ goog.dom.DomHelper.prototype.getElementsByTagNameAndClass;
+
+
+/**
+ * Sets a number of properties on a node.
+ * @param {Element} element DOM node to set properties on.
+ * @param {Object} properties Hash of property:value pairs.
+ */
+goog.dom.DomHelper.prototype.setProperties = goog.dom.setProperties;
+
+
+/**
+ * Gets the dimensions of the viewport.
+ * @param {Window=} opt_window Optional window element to test. Defaults to
+ * the window of the Dom Helper.
+ * @return {!goog.math.Size} Object with values 'width' and 'height'.
+ */
+goog.dom.DomHelper.prototype.getViewportSize = function(opt_window) {
+ // TODO(arv): This should not take an argument. That breaks the rule of a
+ // a DomHelper representing a single frame/window/document.
+ return goog.dom.getViewportSize(opt_window || this.getWindow());
+};
+
+
+/**
+ * Calculates the height of the document.
+ *
+ * @return {number} The height of the document.
+ */
+goog.dom.DomHelper.prototype.getDocumentHeight = function() {
+ return goog.dom.getDocumentHeight_(this.getWindow());
+};
+
+
+/**
+ * Typedef for use with goog.dom.createDom and goog.dom.append.
+ * @typedef {Object|string|Array|NodeList}
+ */
+goog.dom.Appendable;
+
+
+/**
+ * Returns a dom node with a set of attributes. This function accepts varargs
+ * for subsequent nodes to be added. Subsequent nodes will be added to the
+ * first node as childNodes.
+ *
+ * So:
+ * <code>createDom(goog.dom.TagName.DIV, null, createDom(goog.dom.TagName.P),
+ * createDom(goog.dom.TagName.P));</code> would return a div with two child
+ * paragraphs
+ *
+ * An easy way to move all child nodes of an existing element to a new parent
+ * element is:
+ * <code>createDom(goog.dom.TagName.DIV, null, oldElement.childNodes);</code>
+ * which will remove all child nodes from the old element and add them as
+ * child nodes of the new DIV.
+ *
+ * @param {string|!goog.dom.TagName<T>} tagName Tag to create.
+ * @param {?Object|?Array<string>|string=} opt_attributes If object, then a map
+ * of name-value pairs for attributes. If a string, then this is the
+ * className of the new element. If an array, the elements will be joined
+ * together as the className of the new element.
+ * @param {...goog.dom.Appendable} var_args Further DOM nodes or
+ * strings for text nodes. If one of the var_args is an array or
+ * NodeList, its elements will be added as childNodes instead.
+ * @return {R} Reference to a DOM node. The return type is {!Element} if tagName
+ * is a string or a more specific type if it is a member of
+ * goog.dom.TagName (e.g. {!HTMLAnchorElement} for goog.dom.TagName.A).
+ * @template T
+ * @template R := cond(isUnknown(T), 'Element', T) =:
+ */
+goog.dom.DomHelper.prototype.createDom = function(
+ tagName, opt_attributes, var_args) {
+ return goog.dom.createDom_(this.document_, arguments);
+};
+
+
+/**
+ * Alias for {@code createDom}.
+ * @param {string|!goog.dom.TagName<T>} tagName Tag to create.
+ * @param {?Object|?Array<string>|string=} opt_attributes If object, then a map
+ * of name-value pairs for attributes. If a string, then this is the
+ * className of the new element. If an array, the elements will be joined
+ * together as the className of the new element.
+ * @param {...goog.dom.Appendable} var_args Further DOM nodes or strings for
+ * text nodes. If one of the var_args is an array, its children will be
+ * added as childNodes instead.
+ * @return {R} Reference to a DOM node. The return type is {!Element} if tagName
+ * is a string or a more specific type if it is a member of
+ * goog.dom.TagName (e.g. {!HTMLAnchorElement} for goog.dom.TagName.A).
+ * @template T
+ * @template R := cond(isUnknown(T), 'Element', T) =:
+ * @deprecated Use {@link goog.dom.DomHelper.prototype.createDom} instead.
+ */
+goog.dom.DomHelper.prototype.$dom = goog.dom.DomHelper.prototype.createDom;
+
+
+/**
+ * Creates a new element.
+ * @param {string|!goog.dom.TagName<T>} name Tag to create.
+ * @return {R} The new element. The return type is {!Element} if name is
+ * a string or a more specific type if it is a member of goog.dom.TagName
+ * (e.g. {!HTMLAnchorElement} for goog.dom.TagName.A).
+ * @template T
+ * @template R := cond(isUnknown(T), 'Element', T) =:
+ */
+goog.dom.DomHelper.prototype.createElement = function(name) {
+ return goog.dom.createElement_(this.document_, name);
+};
+
+
+/**
+ * Creates a new text node.
+ * @param {number|string} content Content.
+ * @return {!Text} The new text node.
+ */
+goog.dom.DomHelper.prototype.createTextNode = function(content) {
+ return this.document_.createTextNode(String(content));
+};
+
+
+/**
+ * Create a table.
+ * @param {number} rows The number of rows in the table. Must be >= 1.
+ * @param {number} columns The number of columns in the table. Must be >= 1.
+ * @param {boolean=} opt_fillWithNbsp If true, fills table entries with
+ * {@code goog.string.Unicode.NBSP} characters.
+ * @return {!HTMLElement} The created table.
+ */
+goog.dom.DomHelper.prototype.createTable = function(
+ rows, columns, opt_fillWithNbsp) {
+ return goog.dom.createTable_(
+ this.document_, rows, columns, !!opt_fillWithNbsp);
+};
+
+
+/**
+ * Converts an HTML into a node or a document fragment. A single Node is used if
+ * {@code html} only generates a single node. If {@code html} generates multiple
+ * nodes then these are put inside a {@code DocumentFragment}. This is a safe
+ * version of {@code goog.dom.DomHelper#htmlToDocumentFragment} which is now
+ * deleted.
+ * @param {!goog.html.SafeHtml} html The HTML markup to convert.
+ * @return {!Node} The resulting node.
+ */
+goog.dom.DomHelper.prototype.safeHtmlToNode = function(html) {
+ return goog.dom.safeHtmlToNode_(this.document_, html);
+};
+
+
+/**
+ * Returns true if the browser is in "CSS1-compatible" (standards-compliant)
+ * mode, false otherwise.
+ * @return {boolean} True if in CSS1-compatible mode.
+ */
+goog.dom.DomHelper.prototype.isCss1CompatMode = function() {
+ return goog.dom.isCss1CompatMode_(this.document_);
+};
+
+
+/**
+ * Gets the window object associated with the document.
+ * @return {!Window} The window associated with the given document.
+ */
+goog.dom.DomHelper.prototype.getWindow = function() {
+ return goog.dom.getWindow_(this.document_);
+};
+
+
+/**
+ * Gets the document scroll element.
+ * @return {!Element} Scrolling element.
+ */
+goog.dom.DomHelper.prototype.getDocumentScrollElement = function() {
+ return goog.dom.getDocumentScrollElement_(this.document_);
+};
+
+
+/**
+ * Gets the document scroll distance as a coordinate object.
+ * @return {!goog.math.Coordinate} Object with properties 'x' and 'y'.
+ */
+goog.dom.DomHelper.prototype.getDocumentScroll = function() {
+ return goog.dom.getDocumentScroll_(this.document_);
+};
+
+
+/**
+ * Determines the active element in the given document.
+ * @param {Document=} opt_doc The document to look in.
+ * @return {Element} The active element.
+ */
+goog.dom.DomHelper.prototype.getActiveElement = function(opt_doc) {
+ return goog.dom.getActiveElement(opt_doc || this.document_);
+};
+
+
+/**
+ * Appends a child to a node.
+ * @param {Node} parent Parent.
+ * @param {Node} child Child.
+ */
+goog.dom.DomHelper.prototype.appendChild = goog.dom.appendChild;
+
+
+/**
+ * Appends a node with text or other nodes.
+ * @param {!Node} parent The node to append nodes to.
+ * @param {...goog.dom.Appendable} var_args The things to append to the node.
+ * If this is a Node it is appended as is.
+ * If this is a string then a text node is appended.
+ * If this is an array like object then fields 0 to length - 1 are appended.
+ */
+goog.dom.DomHelper.prototype.append = goog.dom.append;
+
+
+/**
+ * Determines if the given node can contain children, intended to be used for
+ * HTML generation.
+ *
+ * @param {Node} node The node to check.
+ * @return {boolean} Whether the node can contain children.
+ */
+goog.dom.DomHelper.prototype.canHaveChildren = goog.dom.canHaveChildren;
+
+
+/**
+ * Removes all the child nodes on a DOM node.
+ * @param {Node} node Node to remove children from.
+ */
+goog.dom.DomHelper.prototype.removeChildren = goog.dom.removeChildren;
+
+
+/**
+ * Inserts a new node before an existing reference node (i.e., as the previous
+ * sibling). If the reference node has no parent, then does nothing.
+ * @param {Node} newNode Node to insert.
+ * @param {Node} refNode Reference node to insert before.
+ */
+goog.dom.DomHelper.prototype.insertSiblingBefore = goog.dom.insertSiblingBefore;
+
+
+/**
+ * Inserts a new node after an existing reference node (i.e., as the next
+ * sibling). If the reference node has no parent, then does nothing.
+ * @param {Node} newNode Node to insert.
+ * @param {Node} refNode Reference node to insert after.
+ */
+goog.dom.DomHelper.prototype.insertSiblingAfter = goog.dom.insertSiblingAfter;
+
+
+/**
+ * Insert a child at a given index. If index is larger than the number of child
+ * nodes that the parent currently has, the node is inserted as the last child
+ * node.
+ * @param {Element} parent The element into which to insert the child.
+ * @param {Node} child The element to insert.
+ * @param {number} index The index at which to insert the new child node. Must
+ * not be negative.
+ */
+goog.dom.DomHelper.prototype.insertChildAt = goog.dom.insertChildAt;
+
+
+/**
+ * Removes a node from its parent.
+ * @param {Node} node The node to remove.
+ * @return {Node} The node removed if removed; else, null.
+ */
+goog.dom.DomHelper.prototype.removeNode = goog.dom.removeNode;
+
+
+/**
+ * Replaces a node in the DOM tree. Will do nothing if {@code oldNode} has no
+ * parent.
+ * @param {Node} newNode Node to insert.
+ * @param {Node} oldNode Node to replace.
+ */
+goog.dom.DomHelper.prototype.replaceNode = goog.dom.replaceNode;
+
+
+/**
+ * Flattens an element. That is, removes it and replace it with its children.
+ * @param {Element} element The element to flatten.
+ * @return {Element|undefined} The original element, detached from the document
+ * tree, sans children, or undefined if the element was already not in the
+ * document.
+ */
+goog.dom.DomHelper.prototype.flattenElement = goog.dom.flattenElement;
+
+
+/**
+ * Returns an array containing just the element children of the given element.
+ * @param {Element} element The element whose element children we want.
+ * @return {!(Array<!Element>|NodeList<!Element>)} An array or array-like list
+ * of just the element children of the given element.
+ */
+goog.dom.DomHelper.prototype.getChildren = goog.dom.getChildren;
+
+
+/**
+ * Returns the first child node that is an element.
+ * @param {Node} node The node to get the first child element of.
+ * @return {Element} The first child node of {@code node} that is an element.
+ */
+goog.dom.DomHelper.prototype.getFirstElementChild =
+ goog.dom.getFirstElementChild;
+
+
+/**
+ * Returns the last child node that is an element.
+ * @param {Node} node The node to get the last child element of.
+ * @return {Element} The last child node of {@code node} that is an element.
+ */
+goog.dom.DomHelper.prototype.getLastElementChild = goog.dom.getLastElementChild;
+
+
+/**
+ * Returns the first next sibling that is an element.
+ * @param {Node} node The node to get the next sibling element of.
+ * @return {Element} The next sibling of {@code node} that is an element.
+ */
+goog.dom.DomHelper.prototype.getNextElementSibling =
+ goog.dom.getNextElementSibling;
+
+
+/**
+ * Returns the first previous sibling that is an element.
+ * @param {Node} node The node to get the previous sibling element of.
+ * @return {Element} The first previous sibling of {@code node} that is
+ * an element.
+ */
+goog.dom.DomHelper.prototype.getPreviousElementSibling =
+ goog.dom.getPreviousElementSibling;
+
+
+/**
+ * Returns the next node in source order from the given node.
+ * @param {Node} node The node.
+ * @return {Node} The next node in the DOM tree, or null if this was the last
+ * node.
+ */
+goog.dom.DomHelper.prototype.getNextNode = goog.dom.getNextNode;
+
+
+/**
+ * Returns the previous node in source order from the given node.
+ * @param {Node} node The node.
+ * @return {Node} The previous node in the DOM tree, or null if this was the
+ * first node.
+ */
+goog.dom.DomHelper.prototype.getPreviousNode = goog.dom.getPreviousNode;
+
+
+/**
+ * Whether the object looks like a DOM node.
+ * @param {?} obj The object being tested for node likeness.
+ * @return {boolean} Whether the object looks like a DOM node.
+ */
+goog.dom.DomHelper.prototype.isNodeLike = goog.dom.isNodeLike;
+
+
+/**
+ * Whether the object looks like an Element.
+ * @param {?} obj The object being tested for Element likeness.
+ * @return {boolean} Whether the object looks like an Element.
+ */
+goog.dom.DomHelper.prototype.isElement = goog.dom.isElement;
+
+
+/**
+ * Returns true if the specified value is a Window object. This includes the
+ * global window for HTML pages, and iframe windows.
+ * @param {?} obj Variable to test.
+ * @return {boolean} Whether the variable is a window.
+ */
+goog.dom.DomHelper.prototype.isWindow = goog.dom.isWindow;
+
+
+/**
+ * Returns an element's parent, if it's an Element.
+ * @param {Element} element The DOM element.
+ * @return {Element} The parent, or null if not an Element.
+ */
+goog.dom.DomHelper.prototype.getParentElement = goog.dom.getParentElement;
+
+
+/**
+ * Whether a node contains another node.
+ * @param {Node} parent The node that should contain the other node.
+ * @param {Node} descendant The node to test presence of.
+ * @return {boolean} Whether the parent node contains the descendent node.
+ */
+goog.dom.DomHelper.prototype.contains = goog.dom.contains;
+
+
+/**
+ * Compares the document order of two nodes, returning 0 if they are the same
+ * node, a negative number if node1 is before node2, and a positive number if
+ * node2 is before node1. Note that we compare the order the tags appear in the
+ * document so in the tree <b><i>text</i></b> the B node is considered to be
+ * before the I node.
+ *
+ * @param {Node} node1 The first node to compare.
+ * @param {Node} node2 The second node to compare.
+ * @return {number} 0 if the nodes are the same node, a negative number if node1
+ * is before node2, and a positive number if node2 is before node1.
+ */
+goog.dom.DomHelper.prototype.compareNodeOrder = goog.dom.compareNodeOrder;
+
+
+/**
+ * Find the deepest common ancestor of the given nodes.
+ * @param {...Node} var_args The nodes to find a common ancestor of.
+ * @return {Node} The common ancestor of the nodes, or null if there is none.
+ * null will only be returned if two or more of the nodes are from different
+ * documents.
+ */
+goog.dom.DomHelper.prototype.findCommonAncestor = goog.dom.findCommonAncestor;
+
+
+/**
+ * Returns the owner document for a node.
+ * @param {Node} node The node to get the document for.
+ * @return {!Document} The document owning the node.
+ */
+goog.dom.DomHelper.prototype.getOwnerDocument = goog.dom.getOwnerDocument;
+
+
+/**
+ * Cross browser function for getting the document element of an iframe.
+ * @param {Element} iframe Iframe element.
+ * @return {!Document} The frame content document.
+ */
+goog.dom.DomHelper.prototype.getFrameContentDocument =
+ goog.dom.getFrameContentDocument;
+
+
+/**
+ * Cross browser function for getting the window of a frame or iframe.
+ * @param {Element} frame Frame element.
+ * @return {Window} The window associated with the given frame.
+ */
+goog.dom.DomHelper.prototype.getFrameContentWindow =
+ goog.dom.getFrameContentWindow;
+
+
+/**
+ * Sets the text content of a node, with cross-browser support.
+ * @param {Node} node The node to change the text content of.
+ * @param {string|number} text The value that should replace the node's content.
+ */
+goog.dom.DomHelper.prototype.setTextContent = goog.dom.setTextContent;
+
+
+/**
+ * Gets the outerHTML of a node, which islike innerHTML, except that it
+ * actually contains the HTML of the node itself.
+ * @param {Element} element The element to get the HTML of.
+ * @return {string} The outerHTML of the given element.
+ */
+goog.dom.DomHelper.prototype.getOuterHtml = goog.dom.getOuterHtml;
+
+
+/**
+ * Finds the first descendant node that matches the filter function. This does
+ * a depth first search.
+ * @param {Node} root The root of the tree to search.
+ * @param {function(Node) : boolean} p The filter function.
+ * @return {Node|undefined} The found node or undefined if none is found.
+ */
+goog.dom.DomHelper.prototype.findNode = goog.dom.findNode;
+
+
+/**
+ * Finds all the descendant nodes that matches the filter function. This does a
+ * depth first search.
+ * @param {Node} root The root of the tree to search.
+ * @param {function(Node) : boolean} p The filter function.
+ * @return {Array<Node>} The found nodes or an empty array if none are found.
+ */
+goog.dom.DomHelper.prototype.findNodes = goog.dom.findNodes;
+
+
+/**
+ * Returns true if the element has a tab index that allows it to receive
+ * keyboard focus (tabIndex >= 0), false otherwise. Note that some elements
+ * natively support keyboard focus, even if they have no tab index.
+ * @param {!Element} element Element to check.
+ * @return {boolean} Whether the element has a tab index that allows keyboard
+ * focus.
+ */
+goog.dom.DomHelper.prototype.isFocusableTabIndex = goog.dom.isFocusableTabIndex;
+
+
+/**
+ * Enables or disables keyboard focus support on the element via its tab index.
+ * Only elements for which {@link goog.dom.isFocusableTabIndex} returns true
+ * (or elements that natively support keyboard focus, like form elements) can
+ * receive keyboard focus. See http://go/tabindex for more info.
+ * @param {Element} element Element whose tab index is to be changed.
+ * @param {boolean} enable Whether to set or remove a tab index on the element
+ * that supports keyboard focus.
+ */
+goog.dom.DomHelper.prototype.setFocusableTabIndex =
+ goog.dom.setFocusableTabIndex;
+
+
+/**
+ * Returns true if the element can be focused, i.e. it has a tab index that
+ * allows it to receive keyboard focus (tabIndex >= 0), or it is an element
+ * that natively supports keyboard focus.
+ * @param {!Element} element Element to check.
+ * @return {boolean} Whether the element allows keyboard focus.
+ */
+goog.dom.DomHelper.prototype.isFocusable = goog.dom.isFocusable;
+
+
+/**
+ * Returns the text contents of the current node, without markup. New lines are
+ * stripped and whitespace is collapsed, such that each character would be
+ * visible.
+ *
+ * In browsers that support it, innerText is used. Other browsers attempt to
+ * simulate it via node traversal. Line breaks are canonicalized in IE.
+ *
+ * @param {Node} node The node from which we are getting content.
+ * @return {string} The text content.
+ */
+goog.dom.DomHelper.prototype.getTextContent = goog.dom.getTextContent;
+
+
+/**
+ * Returns the text length of the text contained in a node, without markup. This
+ * is equivalent to the selection length if the node was selected, or the number
+ * of cursor movements to traverse the node. Images & BRs take one space. New
+ * lines are ignored.
+ *
+ * @param {Node} node The node whose text content length is being calculated.
+ * @return {number} The length of {@code node}'s text content.
+ */
+goog.dom.DomHelper.prototype.getNodeTextLength = goog.dom.getNodeTextLength;
+
+
+/**
+ * Returns the text offset of a node relative to one of its ancestors. The text
+ * length is the same as the length calculated by
+ * {@code goog.dom.getNodeTextLength}.
+ *
+ * @param {Node} node The node whose offset is being calculated.
+ * @param {Node=} opt_offsetParent Defaults to the node's owner document's body.
+ * @return {number} The text offset.
+ */
+goog.dom.DomHelper.prototype.getNodeTextOffset = goog.dom.getNodeTextOffset;
+
+
+/**
+ * Returns the node at a given offset in a parent node. If an object is
+ * provided for the optional third parameter, the node and the remainder of the
+ * offset will stored as properties of this object.
+ * @param {Node} parent The parent node.
+ * @param {number} offset The offset into the parent node.
+ * @param {Object=} opt_result Object to be used to store the return value. The
+ * return value will be stored in the form {node: Node, remainder: number}
+ * if this object is provided.
+ * @return {Node} The node at the given offset.
+ */
+goog.dom.DomHelper.prototype.getNodeAtOffset = goog.dom.getNodeAtOffset;
+
+
+/**
+ * Returns true if the object is a {@code NodeList}. To qualify as a NodeList,
+ * the object must have a numeric length property and an item function (which
+ * has type 'string' on IE for some reason).
+ * @param {Object} val Object to test.
+ * @return {boolean} Whether the object is a NodeList.
+ */
+goog.dom.DomHelper.prototype.isNodeList = goog.dom.isNodeList;
+
+
+/**
+ * Walks up the DOM hierarchy returning the first ancestor that has the passed
+ * tag name and/or class name. If the passed element matches the specified
+ * criteria, the element itself is returned.
+ * @param {Node} element The DOM node to start with.
+ * @param {?(goog.dom.TagName<T>|string)=} opt_tag The tag name to match (or
+ * null/undefined to match only based on class name).
+ * @param {?string=} opt_class The class name to match (or null/undefined to
+ * match only based on tag name).
+ * @param {number=} opt_maxSearchSteps Maximum number of levels to search up the
+ * dom.
+ * @return {?R} The first ancestor that matches the passed criteria, or
+ * null if no match is found. The return type is {?Element} if opt_tag is
+ * not a member of goog.dom.TagName or a more specific type if it is (e.g.
+ * {?HTMLAnchorElement} for goog.dom.TagName.A).
+ * @template T
+ * @template R := cond(isUnknown(T), 'Element', T) =:
+ */
+goog.dom.DomHelper.prototype.getAncestorByTagNameAndClass =
+ goog.dom.getAncestorByTagNameAndClass;
+
+
+/**
+ * Walks up the DOM hierarchy returning the first ancestor that has the passed
+ * class name. If the passed element matches the specified criteria, the
+ * element itself is returned.
+ * @param {Node} element The DOM node to start with.
+ * @param {string} class The class name to match.
+ * @param {number=} opt_maxSearchSteps Maximum number of levels to search up the
+ * dom.
+ * @return {Element} The first ancestor that matches the passed criteria, or
+ * null if none match.
+ */
+goog.dom.DomHelper.prototype.getAncestorByClass = goog.dom.getAncestorByClass;
+
+
+/**
+ * Walks up the DOM hierarchy returning the first ancestor that passes the
+ * matcher function.
+ * @param {Node} element The DOM node to start with.
+ * @param {function(Node) : boolean} matcher A function that returns true if the
+ * passed node matches the desired criteria.
+ * @param {boolean=} opt_includeNode If true, the node itself is included in
+ * the search (the first call to the matcher will pass startElement as
+ * the node to test).
+ * @param {number=} opt_maxSearchSteps Maximum number of levels to search up the
+ * dom.
+ * @return {Node} DOM node that matched the matcher, or null if there was
+ * no match.
+ */
+goog.dom.DomHelper.prototype.getAncestor = goog.dom.getAncestor;
+
+
+/**
+ * Gets '2d' context of a canvas. Shortcut for canvas.getContext('2d') with a
+ * type information.
+ * @param {!HTMLCanvasElement} canvas
+ * @return {!CanvasRenderingContext2D}
+ */
+goog.dom.DomHelper.prototype.getCanvasContext2D = goog.dom.getCanvasContext2D;
diff --git a/chromium/third_party/ink/closure/dom/htmlelement.js b/chromium/third_party/ink/closure/dom/htmlelement.js
new file mode 100644
index 00000000000..c48f7535926
--- /dev/null
+++ b/chromium/third_party/ink/closure/dom/htmlelement.js
@@ -0,0 +1,29 @@
+// Copyright 2017 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+goog.provide('goog.dom.HtmlElement');
+
+
+
+/**
+ * This subclass of HTMLElement is used when only a HTMLElement is possible and
+ * not any of its subclasses. Normally, a type can refer to an instance of
+ * itself or an instance of any subtype. More concretely, if HTMLElement is used
+ * then the compiler must assume that it might still be e.g. HTMLScriptElement.
+ * With this, the type check knows that it couldn't be any special element.
+ *
+ * @constructor
+ * @extends {HTMLElement}
+ */
+goog.dom.HtmlElement = function() {};
diff --git a/chromium/third_party/ink/closure/dom/nodetype.js b/chromium/third_party/ink/closure/dom/nodetype.js
new file mode 100644
index 00000000000..cccb4706eca
--- /dev/null
+++ b/chromium/third_party/ink/closure/dom/nodetype.js
@@ -0,0 +1,48 @@
+// Copyright 2006 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Definition of goog.dom.NodeType.
+ */
+
+goog.provide('goog.dom.NodeType');
+
+
+/**
+ * Constants for the nodeType attribute in the Node interface.
+ *
+ * These constants match those specified in the Node interface. These are
+ * usually present on the Node object in recent browsers, but not in older
+ * browsers (specifically, early IEs) and thus are given here.
+ *
+ * In some browsers (early IEs), these are not defined on the Node object,
+ * so they are provided here.
+ *
+ * See http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-1950641247
+ * @enum {number}
+ */
+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
+};
diff --git a/chromium/third_party/ink/closure/dom/safe.js b/chromium/third_party/ink/closure/dom/safe.js
new file mode 100644
index 00000000000..a869e1b6689
--- /dev/null
+++ b/chromium/third_party/ink/closure/dom/safe.js
@@ -0,0 +1,458 @@
+// Copyright 2013 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Type-safe wrappers for unsafe DOM APIs.
+ *
+ * This file provides type-safe wrappers for DOM APIs that can result in
+ * cross-site scripting (XSS) vulnerabilities, if the API is supplied with
+ * untrusted (attacker-controlled) input. Instead of plain strings, the type
+ * safe wrappers consume values of types from the goog.html package whose
+ * contract promises that values are safe to use in the corresponding context.
+ *
+ * Hence, a program that exclusively uses the wrappers in this file (i.e., whose
+ * only reference to security-sensitive raw DOM APIs are in this file) is
+ * guaranteed to be free of XSS due to incorrect use of such DOM APIs (modulo
+ * correctness of code that produces values of the respective goog.html types,
+ * and absent code that violates type safety).
+ *
+ * For example, assigning to an element's .innerHTML property a string that is
+ * derived (even partially) from untrusted input typically results in an XSS
+ * vulnerability. The type-safe wrapper goog.dom.safe.setInnerHtml consumes a
+ * value of type goog.html.SafeHtml, whose contract states that using its values
+ * in a HTML context will not result in XSS. Hence a program that is free of
+ * direct assignments to any element's innerHTML property (with the exception of
+ * the assignment to .innerHTML in this file) is guaranteed to be free of XSS
+ * due to assignment of untrusted strings to the innerHTML property.
+ */
+
+goog.provide('goog.dom.safe');
+goog.provide('goog.dom.safe.InsertAdjacentHtmlPosition');
+
+goog.require('goog.asserts');
+goog.require('goog.dom.asserts');
+goog.require('goog.html.SafeHtml');
+goog.require('goog.html.SafeScript');
+goog.require('goog.html.SafeStyle');
+goog.require('goog.html.SafeUrl');
+goog.require('goog.html.TrustedResourceUrl');
+goog.require('goog.string');
+goog.require('goog.string.Const');
+
+
+/** @enum {string} */
+goog.dom.safe.InsertAdjacentHtmlPosition = {
+ AFTERBEGIN: 'afterbegin',
+ AFTEREND: 'afterend',
+ BEFOREBEGIN: 'beforebegin',
+ BEFOREEND: 'beforeend'
+};
+
+
+/**
+ * Inserts known-safe HTML into a Node, at the specified position.
+ * @param {!Node} node The node on which to call insertAdjacentHTML.
+ * @param {!goog.dom.safe.InsertAdjacentHtmlPosition} position Position where
+ * to insert the HTML.
+ * @param {!goog.html.SafeHtml} html The known-safe HTML to insert.
+ */
+goog.dom.safe.insertAdjacentHtml = function(node, position, html) {
+ node.insertAdjacentHTML(position, goog.html.SafeHtml.unwrap(html));
+};
+
+
+/**
+ * Tags not allowed in goog.dom.safe.setInnerHtml.
+ * @private @const {!Object<string, boolean>}
+ */
+goog.dom.safe.SET_INNER_HTML_DISALLOWED_TAGS_ = {
+ 'MATH': true,
+ 'SCRIPT': true,
+ 'STYLE': true,
+ 'SVG': true,
+ 'TEMPLATE': true
+};
+
+
+/**
+ * Assigns known-safe HTML to an element's innerHTML property.
+ * @param {!Element} elem The element whose innerHTML is to be assigned to.
+ * @param {!goog.html.SafeHtml} html The known-safe HTML to assign.
+ * @throws {Error} If called with one of these tags: math, script, style, svg,
+ * template.
+ */
+goog.dom.safe.setInnerHtml = function(elem, html) {
+ if (goog.asserts.ENABLE_ASSERTS) {
+ var tagName = elem.tagName.toUpperCase();
+ if (goog.dom.safe.SET_INNER_HTML_DISALLOWED_TAGS_[tagName]) {
+ throw new Error(
+ 'goog.dom.safe.setInnerHtml cannot be used to set content of ' +
+ elem.tagName + '.');
+ }
+ }
+ elem.innerHTML = goog.html.SafeHtml.unwrap(html);
+};
+
+
+/**
+ * Assigns known-safe HTML to an element's outerHTML property.
+ * @param {!Element} elem The element whose outerHTML is to be assigned to.
+ * @param {!goog.html.SafeHtml} html The known-safe HTML to assign.
+ */
+goog.dom.safe.setOuterHtml = function(elem, html) {
+ elem.outerHTML = goog.html.SafeHtml.unwrap(html);
+};
+
+
+/**
+ * Sets the given element's style property to the contents of the provided
+ * SafeStyle object.
+ * @param {!Element} elem
+ * @param {!goog.html.SafeStyle} style
+ */
+goog.dom.safe.setStyle = function(elem, style) {
+ elem.style.cssText = goog.html.SafeStyle.unwrap(style);
+};
+
+
+/**
+ * Writes known-safe HTML to a document.
+ * @param {!Document} doc The document to be written to.
+ * @param {!goog.html.SafeHtml} html The known-safe HTML to assign.
+ */
+goog.dom.safe.documentWrite = function(doc, html) {
+ doc.write(goog.html.SafeHtml.unwrap(html));
+};
+
+
+/**
+ * Safely assigns a URL to an anchor element's href property.
+ *
+ * If url is of type goog.html.SafeUrl, its value is unwrapped and assigned to
+ * anchor's href property. If url is of type string however, it is first
+ * sanitized using goog.html.SafeUrl.sanitize.
+ *
+ * Example usage:
+ * goog.dom.safe.setAnchorHref(anchorEl, url);
+ * which is a safe alternative to
+ * anchorEl.href = url;
+ * The latter can result in XSS vulnerabilities if url is a
+ * user-/attacker-controlled value.
+ *
+ * @param {!HTMLAnchorElement} anchor The anchor element whose href property
+ * is to be assigned to.
+ * @param {string|!goog.html.SafeUrl} url The URL to assign.
+ * @see goog.html.SafeUrl#sanitize
+ */
+goog.dom.safe.setAnchorHref = function(anchor, url) {
+ goog.dom.asserts.assertIsHTMLAnchorElement(anchor);
+ /** @type {!goog.html.SafeUrl} */
+ var safeUrl;
+ if (url instanceof goog.html.SafeUrl) {
+ safeUrl = url;
+ } else {
+ safeUrl = goog.html.SafeUrl.sanitizeAssertUnchanged(url);
+ }
+ anchor.href = goog.html.SafeUrl.unwrap(safeUrl);
+};
+
+
+/**
+ * Safely assigns a URL to an image element's src property.
+ *
+ * If url is of type goog.html.SafeUrl, its value is unwrapped and assigned to
+ * image's src property. If url is of type string however, it is first
+ * sanitized using goog.html.SafeUrl.sanitize.
+ *
+ * @param {!HTMLImageElement} imageElement The image element whose src property
+ * is to be assigned to.
+ * @param {string|!goog.html.SafeUrl} url The URL to assign.
+ * @see goog.html.SafeUrl#sanitize
+ */
+goog.dom.safe.setImageSrc = function(imageElement, url) {
+ goog.dom.asserts.assertIsHTMLImageElement(imageElement);
+ /** @type {!goog.html.SafeUrl} */
+ var safeUrl;
+ if (url instanceof goog.html.SafeUrl) {
+ safeUrl = url;
+ } else {
+ safeUrl = goog.html.SafeUrl.sanitizeAssertUnchanged(url);
+ }
+ imageElement.src = goog.html.SafeUrl.unwrap(safeUrl);
+};
+
+
+/**
+ * Safely assigns a URL to an embed element's src property.
+ *
+ * Example usage:
+ * goog.dom.safe.setEmbedSrc(embedEl, url);
+ * which is a safe alternative to
+ * embedEl.src = url;
+ * The latter can result in loading untrusted code unless it is ensured that
+ * the URL refers to a trustworthy resource.
+ *
+ * @param {!HTMLEmbedElement} embed The embed element whose src property
+ * is to be assigned to.
+ * @param {!goog.html.TrustedResourceUrl} url The URL to assign.
+ */
+goog.dom.safe.setEmbedSrc = function(embed, url) {
+ goog.dom.asserts.assertIsHTMLEmbedElement(embed);
+ embed.src = goog.html.TrustedResourceUrl.unwrap(url);
+};
+
+
+/**
+ * Safely assigns a URL to a frame element's src property.
+ *
+ * Example usage:
+ * goog.dom.safe.setFrameSrc(frameEl, url);
+ * which is a safe alternative to
+ * frameEl.src = url;
+ * The latter can result in loading untrusted code unless it is ensured that
+ * the URL refers to a trustworthy resource.
+ *
+ * @param {!HTMLFrameElement} frame The frame element whose src property
+ * is to be assigned to.
+ * @param {!goog.html.TrustedResourceUrl} url The URL to assign.
+ */
+goog.dom.safe.setFrameSrc = function(frame, url) {
+ goog.dom.asserts.assertIsHTMLFrameElement(frame);
+ frame.src = goog.html.TrustedResourceUrl.unwrap(url);
+};
+
+
+/**
+ * Safely assigns a URL to an iframe element's src property.
+ *
+ * Example usage:
+ * goog.dom.safe.setIframeSrc(iframeEl, url);
+ * which is a safe alternative to
+ * iframeEl.src = url;
+ * The latter can result in loading untrusted code unless it is ensured that
+ * the URL refers to a trustworthy resource.
+ *
+ * @param {!HTMLIFrameElement} iframe The iframe element whose src property
+ * is to be assigned to.
+ * @param {!goog.html.TrustedResourceUrl} url The URL to assign.
+ */
+goog.dom.safe.setIframeSrc = function(iframe, url) {
+ goog.dom.asserts.assertIsHTMLIFrameElement(iframe);
+ iframe.src = goog.html.TrustedResourceUrl.unwrap(url);
+};
+
+
+/**
+ * Safely assigns HTML to an iframe element's srcdoc property.
+ *
+ * Example usage:
+ * goog.dom.safe.setIframeSrcdoc(iframeEl, safeHtml);
+ * which is a safe alternative to
+ * iframeEl.srcdoc = html;
+ * The latter can result in loading untrusted code.
+ *
+ * @param {!HTMLIFrameElement} iframe The iframe element whose srcdoc property
+ * is to be assigned to.
+ * @param {!goog.html.SafeHtml} html The HTML to assign.
+ */
+goog.dom.safe.setIframeSrcdoc = function(iframe, html) {
+ goog.dom.asserts.assertIsHTMLIFrameElement(iframe);
+ iframe.srcdoc = goog.html.SafeHtml.unwrap(html);
+};
+
+
+/**
+ * Safely sets a link element's href and rel properties. Whether or not
+ * the URL assigned to href has to be a goog.html.TrustedResourceUrl
+ * depends on the value of the rel property. If rel contains "stylesheet"
+ * then a TrustedResourceUrl is required.
+ *
+ * Example usage:
+ * goog.dom.safe.setLinkHrefAndRel(linkEl, url, 'stylesheet');
+ * which is a safe alternative to
+ * linkEl.rel = 'stylesheet';
+ * linkEl.href = url;
+ * The latter can result in loading untrusted code unless it is ensured that
+ * the URL refers to a trustworthy resource.
+ *
+ * @param {!HTMLLinkElement} link The link element whose href property
+ * is to be assigned to.
+ * @param {string|!goog.html.SafeUrl|!goog.html.TrustedResourceUrl} url The URL
+ * to assign to the href property. Must be a TrustedResourceUrl if the
+ * value assigned to rel contains "stylesheet". A string value is
+ * sanitized with goog.html.SafeUrl.sanitize.
+ * @param {string} rel The value to assign to the rel property.
+ * @throws {Error} if rel contains "stylesheet" and url is not a
+ * TrustedResourceUrl
+ * @see goog.html.SafeUrl#sanitize
+ */
+goog.dom.safe.setLinkHrefAndRel = function(link, url, rel) {
+ goog.dom.asserts.assertIsHTMLLinkElement(link);
+ link.rel = rel;
+ if (goog.string.caseInsensitiveContains(rel, 'stylesheet')) {
+ goog.asserts.assert(
+ url instanceof goog.html.TrustedResourceUrl,
+ 'URL must be TrustedResourceUrl because "rel" contains "stylesheet"');
+ link.href = goog.html.TrustedResourceUrl.unwrap(url);
+ } else if (url instanceof goog.html.TrustedResourceUrl) {
+ link.href = goog.html.TrustedResourceUrl.unwrap(url);
+ } else if (url instanceof goog.html.SafeUrl) {
+ link.href = goog.html.SafeUrl.unwrap(url);
+ } else { // string
+ // SafeUrl.sanitize must return legitimate SafeUrl when passed a string.
+ link.href =
+ goog.html.SafeUrl.sanitizeAssertUnchanged(url).getTypedStringValue();
+ }
+};
+
+
+/**
+ * Safely assigns a URL to an object element's data property.
+ *
+ * Example usage:
+ * goog.dom.safe.setObjectData(objectEl, url);
+ * which is a safe alternative to
+ * objectEl.data = url;
+ * The latter can result in loading untrusted code unless setit is ensured that
+ * the URL refers to a trustworthy resource.
+ *
+ * @param {!HTMLObjectElement} object The object element whose data property
+ * is to be assigned to.
+ * @param {!goog.html.TrustedResourceUrl} url The URL to assign.
+ */
+goog.dom.safe.setObjectData = function(object, url) {
+ goog.dom.asserts.assertIsHTMLObjectElement(object);
+ object.data = goog.html.TrustedResourceUrl.unwrap(url);
+};
+
+
+/**
+ * Safely assigns a URL to a script element's src property.
+ *
+ * Example usage:
+ * goog.dom.safe.setScriptSrc(scriptEl, url);
+ * which is a safe alternative to
+ * scriptEl.src = url;
+ * The latter can result in loading untrusted code unless it is ensured that
+ * the URL refers to a trustworthy resource.
+ *
+ * @param {!HTMLScriptElement} script The script element whose src property
+ * is to be assigned to.
+ * @param {!goog.html.TrustedResourceUrl} url The URL to assign.
+ */
+goog.dom.safe.setScriptSrc = function(script, url) {
+ goog.dom.asserts.assertIsHTMLScriptElement(script);
+ script.src = goog.html.TrustedResourceUrl.unwrap(url);
+};
+
+
+/**
+ * Safely assigns a value to a script element's content.
+ *
+ * Example usage:
+ * goog.dom.safe.setScriptContent(scriptEl, content);
+ * which is a safe alternative to
+ * scriptEl.text = content;
+ * The latter can result in executing untrusted code unless it is ensured that
+ * the code is loaded from a trustworthy resource.
+ *
+ * @param {!HTMLScriptElement} script The script element whose content is being
+ * set.
+ * @param {!goog.html.SafeScript} content The content to assign.
+ */
+goog.dom.safe.setScriptContent = function(script, content) {
+ goog.dom.asserts.assertIsHTMLScriptElement(script);
+ script.text = goog.html.SafeScript.unwrap(content);
+};
+
+
+/**
+ * Safely assigns a URL to a Location object's href property.
+ *
+ * If url is of type goog.html.SafeUrl, its value is unwrapped and assigned to
+ * loc's href property. If url is of type string however, it is first sanitized
+ * using goog.html.SafeUrl.sanitize.
+ *
+ * Example usage:
+ * goog.dom.safe.setLocationHref(document.location, redirectUrl);
+ * which is a safe alternative to
+ * document.location.href = redirectUrl;
+ * The latter can result in XSS vulnerabilities if redirectUrl is a
+ * user-/attacker-controlled value.
+ *
+ * @param {!Location} loc The Location object whose href property is to be
+ * assigned to.
+ * @param {string|!goog.html.SafeUrl} url The URL to assign.
+ * @see goog.html.SafeUrl#sanitize
+ */
+goog.dom.safe.setLocationHref = function(loc, url) {
+ goog.dom.asserts.assertIsLocation(loc);
+ /** @type {!goog.html.SafeUrl} */
+ var safeUrl;
+ if (url instanceof goog.html.SafeUrl) {
+ safeUrl = url;
+ } else {
+ safeUrl = goog.html.SafeUrl.sanitizeAssertUnchanged(url);
+ }
+ loc.href = goog.html.SafeUrl.unwrap(safeUrl);
+};
+
+
+/**
+ * Safely opens a URL in a new window (via window.open).
+ *
+ * If url is of type goog.html.SafeUrl, its value is unwrapped and passed in to
+ * window.open. If url is of type string however, it is first sanitized
+ * using goog.html.SafeUrl.sanitize.
+ *
+ * Note that this function does not prevent leakages via the referer that is
+ * sent by window.open. It is advised to only use this to open 1st party URLs.
+ *
+ * Example usage:
+ * goog.dom.safe.openInWindow(url);
+ * which is a safe alternative to
+ * window.open(url);
+ * The latter can result in XSS vulnerabilities if redirectUrl is a
+ * user-/attacker-controlled value.
+ *
+ * @param {string|!goog.html.SafeUrl} url The URL to open.
+ * @param {Window=} opt_openerWin Window of which to call the .open() method.
+ * Defaults to the global window.
+ * @param {!goog.string.Const=} opt_name Name of the window to open in. Can be
+ * _top, etc as allowed by window.open().
+ * @param {string=} opt_specs Comma-separated list of specifications, same as
+ * in window.open().
+ * @param {boolean=} opt_replace Whether to replace the current entry in browser
+ * history, same as in window.open().
+ * @return {Window} Window the url was opened in.
+ */
+goog.dom.safe.openInWindow = function(
+ url, opt_openerWin, opt_name, opt_specs, opt_replace) {
+ /** @type {!goog.html.SafeUrl} */
+ var safeUrl;
+ if (url instanceof goog.html.SafeUrl) {
+ safeUrl = url;
+ } else {
+ safeUrl = goog.html.SafeUrl.sanitizeAssertUnchanged(url);
+ }
+ var win = opt_openerWin || window;
+ return win.open(
+ goog.html.SafeUrl.unwrap(safeUrl),
+ // If opt_name is undefined, simply passing that in to open() causes IE to
+ // reuse the current window instead of opening a new one. Thus we pass ''
+ // in instead, which according to spec opens a new window. See
+ // https://html.spec.whatwg.org/multipage/browsers.html#dom-open .
+ opt_name ? goog.string.Const.unwrap(opt_name) : '', opt_specs,
+ opt_replace);
+};
diff --git a/chromium/third_party/ink/closure/dom/tagname.js b/chromium/third_party/ink/closure/dom/tagname.js
new file mode 100644
index 00000000000..b3808ad172a
--- /dev/null
+++ b/chromium/third_party/ink/closure/dom/tagname.js
@@ -0,0 +1,562 @@
+// Copyright 2007 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Defines the goog.dom.TagName class. Its constants enumerate
+ * all HTML tag names specified in either the the W3C HTML 4.01 index of
+ * elements or the HTML5 draft specification.
+ *
+ * References:
+ * http://www.w3.org/TR/html401/index/elements.html
+ * http://dev.w3.org/html5/spec/section-index.html
+ */
+goog.provide('goog.dom.TagName');
+
+goog.require('goog.dom.HtmlElement');
+
+
+/**
+ * A tag name with the type of the element stored in the generic.
+ * @param {string} tagName
+ * @constructor
+ * @template T
+ */
+goog.dom.TagName = function(tagName) {
+ /** @private {string} */
+ this.tagName_ = tagName;
+};
+
+
+/**
+ * Returns the tag name.
+ * @return {string}
+ * @override
+ */
+goog.dom.TagName.prototype.toString = function() {
+ return this.tagName_;
+};
+
+
+// Closure Compiler unconditionally converts the following constants to their
+// string value (goog.dom.TagName.A -> 'A'). These are the consequences:
+// 1. Don't add any members or static members to goog.dom.TagName as they
+// couldn't be accessed after this optimization.
+// 2. Keep the constant name and its string value the same:
+// goog.dom.TagName.X = new goog.dom.TagName('Y');
+// is converted to 'X', not 'Y'.
+
+
+/** @type {!goog.dom.TagName<!HTMLAnchorElement>} */
+goog.dom.TagName.A = new goog.dom.TagName('A');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.ABBR = new goog.dom.TagName('ABBR');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.ACRONYM = new goog.dom.TagName('ACRONYM');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.ADDRESS = new goog.dom.TagName('ADDRESS');
+
+
+/** @type {!goog.dom.TagName<!HTMLAppletElement>} */
+goog.dom.TagName.APPLET = new goog.dom.TagName('APPLET');
+
+
+/** @type {!goog.dom.TagName<!HTMLAreaElement>} */
+goog.dom.TagName.AREA = new goog.dom.TagName('AREA');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.ARTICLE = new goog.dom.TagName('ARTICLE');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.ASIDE = new goog.dom.TagName('ASIDE');
+
+
+/** @type {!goog.dom.TagName<!HTMLAudioElement>} */
+goog.dom.TagName.AUDIO = new goog.dom.TagName('AUDIO');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.B = new goog.dom.TagName('B');
+
+
+/** @type {!goog.dom.TagName<!HTMLBaseElement>} */
+goog.dom.TagName.BASE = new goog.dom.TagName('BASE');
+
+
+/** @type {!goog.dom.TagName<!HTMLBaseFontElement>} */
+goog.dom.TagName.BASEFONT = new goog.dom.TagName('BASEFONT');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.BDI = new goog.dom.TagName('BDI');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.BDO = new goog.dom.TagName('BDO');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.BIG = new goog.dom.TagName('BIG');
+
+
+/** @type {!goog.dom.TagName<!HTMLQuoteElement>} */
+goog.dom.TagName.BLOCKQUOTE = new goog.dom.TagName('BLOCKQUOTE');
+
+
+/** @type {!goog.dom.TagName<!HTMLBodyElement>} */
+goog.dom.TagName.BODY = new goog.dom.TagName('BODY');
+
+
+/** @type {!goog.dom.TagName<!HTMLBRElement>} */
+goog.dom.TagName.BR = new goog.dom.TagName('BR');
+
+
+/** @type {!goog.dom.TagName<!HTMLButtonElement>} */
+goog.dom.TagName.BUTTON = new goog.dom.TagName('BUTTON');
+
+
+/** @type {!goog.dom.TagName<!HTMLCanvasElement>} */
+goog.dom.TagName.CANVAS = new goog.dom.TagName('CANVAS');
+
+
+/** @type {!goog.dom.TagName<!HTMLTableCaptionElement>} */
+goog.dom.TagName.CAPTION = new goog.dom.TagName('CAPTION');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.CENTER = new goog.dom.TagName('CENTER');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.CITE = new goog.dom.TagName('CITE');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.CODE = new goog.dom.TagName('CODE');
+
+
+/** @type {!goog.dom.TagName<!HTMLTableColElement>} */
+goog.dom.TagName.COL = new goog.dom.TagName('COL');
+
+
+/** @type {!goog.dom.TagName<!HTMLTableColElement>} */
+goog.dom.TagName.COLGROUP = new goog.dom.TagName('COLGROUP');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.COMMAND = new goog.dom.TagName('COMMAND');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.DATA = new goog.dom.TagName('DATA');
+
+
+/** @type {!goog.dom.TagName<!HTMLDataListElement>} */
+goog.dom.TagName.DATALIST = new goog.dom.TagName('DATALIST');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.DD = new goog.dom.TagName('DD');
+
+
+/** @type {!goog.dom.TagName<!HTMLModElement>} */
+goog.dom.TagName.DEL = new goog.dom.TagName('DEL');
+
+
+/** @type {!goog.dom.TagName<!HTMLDetailsElement>} */
+goog.dom.TagName.DETAILS = new goog.dom.TagName('DETAILS');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.DFN = new goog.dom.TagName('DFN');
+
+
+/** @type {!goog.dom.TagName<!HTMLDialogElement>} */
+goog.dom.TagName.DIALOG = new goog.dom.TagName('DIALOG');
+
+
+/** @type {!goog.dom.TagName<!HTMLDirectoryElement>} */
+goog.dom.TagName.DIR = new goog.dom.TagName('DIR');
+
+
+/** @type {!goog.dom.TagName<!HTMLDivElement>} */
+goog.dom.TagName.DIV = new goog.dom.TagName('DIV');
+
+
+/** @type {!goog.dom.TagName<!HTMLDListElement>} */
+goog.dom.TagName.DL = new goog.dom.TagName('DL');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.DT = new goog.dom.TagName('DT');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.EM = new goog.dom.TagName('EM');
+
+
+/** @type {!goog.dom.TagName<!HTMLEmbedElement>} */
+goog.dom.TagName.EMBED = new goog.dom.TagName('EMBED');
+
+
+/** @type {!goog.dom.TagName<!HTMLFieldSetElement>} */
+goog.dom.TagName.FIELDSET = new goog.dom.TagName('FIELDSET');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.FIGCAPTION = new goog.dom.TagName('FIGCAPTION');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.FIGURE = new goog.dom.TagName('FIGURE');
+
+
+/** @type {!goog.dom.TagName<!HTMLFontElement>} */
+goog.dom.TagName.FONT = new goog.dom.TagName('FONT');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.FOOTER = new goog.dom.TagName('FOOTER');
+
+
+/** @type {!goog.dom.TagName<!HTMLFormElement>} */
+goog.dom.TagName.FORM = new goog.dom.TagName('FORM');
+
+
+/** @type {!goog.dom.TagName<!HTMLFrameElement>} */
+goog.dom.TagName.FRAME = new goog.dom.TagName('FRAME');
+
+
+/** @type {!goog.dom.TagName<!HTMLFrameSetElement>} */
+goog.dom.TagName.FRAMESET = new goog.dom.TagName('FRAMESET');
+
+
+/** @type {!goog.dom.TagName<!HTMLHeadingElement>} */
+goog.dom.TagName.H1 = new goog.dom.TagName('H1');
+
+
+/** @type {!goog.dom.TagName<!HTMLHeadingElement>} */
+goog.dom.TagName.H2 = new goog.dom.TagName('H2');
+
+
+/** @type {!goog.dom.TagName<!HTMLHeadingElement>} */
+goog.dom.TagName.H3 = new goog.dom.TagName('H3');
+
+
+/** @type {!goog.dom.TagName<!HTMLHeadingElement>} */
+goog.dom.TagName.H4 = new goog.dom.TagName('H4');
+
+
+/** @type {!goog.dom.TagName<!HTMLHeadingElement>} */
+goog.dom.TagName.H5 = new goog.dom.TagName('H5');
+
+
+/** @type {!goog.dom.TagName<!HTMLHeadingElement>} */
+goog.dom.TagName.H6 = new goog.dom.TagName('H6');
+
+
+/** @type {!goog.dom.TagName<!HTMLHeadElement>} */
+goog.dom.TagName.HEAD = new goog.dom.TagName('HEAD');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.HEADER = new goog.dom.TagName('HEADER');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.HGROUP = new goog.dom.TagName('HGROUP');
+
+
+/** @type {!goog.dom.TagName<!HTMLHRElement>} */
+goog.dom.TagName.HR = new goog.dom.TagName('HR');
+
+
+/** @type {!goog.dom.TagName<!HTMLHtmlElement>} */
+goog.dom.TagName.HTML = new goog.dom.TagName('HTML');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.I = new goog.dom.TagName('I');
+
+
+/** @type {!goog.dom.TagName<!HTMLIFrameElement>} */
+goog.dom.TagName.IFRAME = new goog.dom.TagName('IFRAME');
+
+
+/** @type {!goog.dom.TagName<!HTMLImageElement>} */
+goog.dom.TagName.IMG = new goog.dom.TagName('IMG');
+
+
+/** @type {!goog.dom.TagName<!HTMLInputElement>} */
+goog.dom.TagName.INPUT = new goog.dom.TagName('INPUT');
+
+
+/** @type {!goog.dom.TagName<!HTMLModElement>} */
+goog.dom.TagName.INS = new goog.dom.TagName('INS');
+
+
+/** @type {!goog.dom.TagName<!HTMLIsIndexElement>} */
+goog.dom.TagName.ISINDEX = new goog.dom.TagName('ISINDEX');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.KBD = new goog.dom.TagName('KBD');
+
+
+// HTMLKeygenElement is deprecated.
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.KEYGEN = new goog.dom.TagName('KEYGEN');
+
+
+/** @type {!goog.dom.TagName<!HTMLLabelElement>} */
+goog.dom.TagName.LABEL = new goog.dom.TagName('LABEL');
+
+
+/** @type {!goog.dom.TagName<!HTMLLegendElement>} */
+goog.dom.TagName.LEGEND = new goog.dom.TagName('LEGEND');
+
+
+/** @type {!goog.dom.TagName<!HTMLLIElement>} */
+goog.dom.TagName.LI = new goog.dom.TagName('LI');
+
+
+/** @type {!goog.dom.TagName<!HTMLLinkElement>} */
+goog.dom.TagName.LINK = new goog.dom.TagName('LINK');
+
+
+/** @type {!goog.dom.TagName<!HTMLMapElement>} */
+goog.dom.TagName.MAP = new goog.dom.TagName('MAP');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.MARK = new goog.dom.TagName('MARK');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.MATH = new goog.dom.TagName('MATH');
+
+
+/** @type {!goog.dom.TagName<!HTMLMenuElement>} */
+goog.dom.TagName.MENU = new goog.dom.TagName('MENU');
+
+
+/** @type {!goog.dom.TagName<!HTMLMetaElement>} */
+goog.dom.TagName.META = new goog.dom.TagName('META');
+
+
+/** @type {!goog.dom.TagName<!HTMLMeterElement>} */
+goog.dom.TagName.METER = new goog.dom.TagName('METER');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.NAV = new goog.dom.TagName('NAV');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.NOFRAMES = new goog.dom.TagName('NOFRAMES');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.NOSCRIPT = new goog.dom.TagName('NOSCRIPT');
+
+
+/** @type {!goog.dom.TagName<!HTMLObjectElement>} */
+goog.dom.TagName.OBJECT = new goog.dom.TagName('OBJECT');
+
+
+/** @type {!goog.dom.TagName<!HTMLOListElement>} */
+goog.dom.TagName.OL = new goog.dom.TagName('OL');
+
+
+/** @type {!goog.dom.TagName<!HTMLOptGroupElement>} */
+goog.dom.TagName.OPTGROUP = new goog.dom.TagName('OPTGROUP');
+
+
+/** @type {!goog.dom.TagName<!HTMLOptionElement>} */
+goog.dom.TagName.OPTION = new goog.dom.TagName('OPTION');
+
+
+/** @type {!goog.dom.TagName<!HTMLOutputElement>} */
+goog.dom.TagName.OUTPUT = new goog.dom.TagName('OUTPUT');
+
+
+/** @type {!goog.dom.TagName<!HTMLParagraphElement>} */
+goog.dom.TagName.P = new goog.dom.TagName('P');
+
+
+/** @type {!goog.dom.TagName<!HTMLParamElement>} */
+goog.dom.TagName.PARAM = new goog.dom.TagName('PARAM');
+
+
+/** @type {!goog.dom.TagName<!HTMLPreElement>} */
+goog.dom.TagName.PRE = new goog.dom.TagName('PRE');
+
+
+/** @type {!goog.dom.TagName<!HTMLProgressElement>} */
+goog.dom.TagName.PROGRESS = new goog.dom.TagName('PROGRESS');
+
+
+/** @type {!goog.dom.TagName<!HTMLQuoteElement>} */
+goog.dom.TagName.Q = new goog.dom.TagName('Q');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.RP = new goog.dom.TagName('RP');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.RT = new goog.dom.TagName('RT');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.RUBY = new goog.dom.TagName('RUBY');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.S = new goog.dom.TagName('S');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.SAMP = new goog.dom.TagName('SAMP');
+
+
+/** @type {!goog.dom.TagName<!HTMLScriptElement>} */
+goog.dom.TagName.SCRIPT = new goog.dom.TagName('SCRIPT');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.SECTION = new goog.dom.TagName('SECTION');
+
+
+/** @type {!goog.dom.TagName<!HTMLSelectElement>} */
+goog.dom.TagName.SELECT = new goog.dom.TagName('SELECT');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.SMALL = new goog.dom.TagName('SMALL');
+
+
+/** @type {!goog.dom.TagName<!HTMLSourceElement>} */
+goog.dom.TagName.SOURCE = new goog.dom.TagName('SOURCE');
+
+
+/** @type {!goog.dom.TagName<!HTMLSpanElement>} */
+goog.dom.TagName.SPAN = new goog.dom.TagName('SPAN');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.STRIKE = new goog.dom.TagName('STRIKE');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.STRONG = new goog.dom.TagName('STRONG');
+
+
+/** @type {!goog.dom.TagName<!HTMLStyleElement>} */
+goog.dom.TagName.STYLE = new goog.dom.TagName('STYLE');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.SUB = new goog.dom.TagName('SUB');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.SUMMARY = new goog.dom.TagName('SUMMARY');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.SUP = new goog.dom.TagName('SUP');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.SVG = new goog.dom.TagName('SVG');
+
+
+/** @type {!goog.dom.TagName<!HTMLTableElement>} */
+goog.dom.TagName.TABLE = new goog.dom.TagName('TABLE');
+
+
+/** @type {!goog.dom.TagName<!HTMLTableSectionElement>} */
+goog.dom.TagName.TBODY = new goog.dom.TagName('TBODY');
+
+
+/** @type {!goog.dom.TagName<!HTMLTableCellElement>} */
+goog.dom.TagName.TD = new goog.dom.TagName('TD');
+
+
+/** @type {!goog.dom.TagName<!HTMLTemplateElement>} */
+goog.dom.TagName.TEMPLATE = new goog.dom.TagName('TEMPLATE');
+
+
+/** @type {!goog.dom.TagName<!HTMLTextAreaElement>} */
+goog.dom.TagName.TEXTAREA = new goog.dom.TagName('TEXTAREA');
+
+
+/** @type {!goog.dom.TagName<!HTMLTableSectionElement>} */
+goog.dom.TagName.TFOOT = new goog.dom.TagName('TFOOT');
+
+
+/** @type {!goog.dom.TagName<!HTMLTableCellElement>} */
+goog.dom.TagName.TH = new goog.dom.TagName('TH');
+
+
+/** @type {!goog.dom.TagName<!HTMLTableSectionElement>} */
+goog.dom.TagName.THEAD = new goog.dom.TagName('THEAD');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.TIME = new goog.dom.TagName('TIME');
+
+
+/** @type {!goog.dom.TagName<!HTMLTitleElement>} */
+goog.dom.TagName.TITLE = new goog.dom.TagName('TITLE');
+
+
+/** @type {!goog.dom.TagName<!HTMLTableRowElement>} */
+goog.dom.TagName.TR = new goog.dom.TagName('TR');
+
+
+/** @type {!goog.dom.TagName<!HTMLTrackElement>} */
+goog.dom.TagName.TRACK = new goog.dom.TagName('TRACK');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.TT = new goog.dom.TagName('TT');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.U = new goog.dom.TagName('U');
+
+
+/** @type {!goog.dom.TagName<!HTMLUListElement>} */
+goog.dom.TagName.UL = new goog.dom.TagName('UL');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.VAR = new goog.dom.TagName('VAR');
+
+
+/** @type {!goog.dom.TagName<!HTMLVideoElement>} */
+goog.dom.TagName.VIDEO = new goog.dom.TagName('VIDEO');
+
+
+/** @type {!goog.dom.TagName<!goog.dom.HtmlElement>} */
+goog.dom.TagName.WBR = new goog.dom.TagName('WBR');
diff --git a/chromium/third_party/ink/closure/dom/tags.js b/chromium/third_party/ink/closure/dom/tags.js
new file mode 100644
index 00000000000..7c129386366
--- /dev/null
+++ b/chromium/third_party/ink/closure/dom/tags.js
@@ -0,0 +1,41 @@
+// Copyright 2014 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Utilities for HTML element tag names.
+ */
+goog.provide('goog.dom.tags');
+
+goog.require('goog.object');
+
+
+/**
+ * The void elements specified by
+ * http://www.w3.org/TR/html-markup/syntax.html#void-elements.
+ * @const @private {!Object<string, boolean>}
+ */
+goog.dom.tags.VOID_TAGS_ = goog.object.createSet(
+ 'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input',
+ 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr');
+
+
+/**
+ * Checks whether the tag is void (with no contents allowed and no legal end
+ * tag), for example 'br'.
+ * @param {string} tagName The tag name in lower case.
+ * @return {boolean}
+ */
+goog.dom.tags.isVoidTag = function(tagName) {
+ return goog.dom.tags.VOID_TAGS_[tagName] === true;
+};
diff --git a/chromium/third_party/ink/closure/dom/vendor.js b/chromium/third_party/ink/closure/dom/vendor.js
new file mode 100644
index 00000000000..28b173d3485
--- /dev/null
+++ b/chromium/third_party/ink/closure/dom/vendor.js
@@ -0,0 +1,97 @@
+// Copyright 2012 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Vendor prefix getters.
+ */
+
+goog.provide('goog.dom.vendor');
+
+goog.require('goog.string');
+goog.require('goog.userAgent');
+
+
+/**
+ * Returns the JS vendor prefix used in CSS properties. Different vendors
+ * use different methods of changing the case of the property names.
+ *
+ * @return {?string} The JS vendor prefix or null if there is none.
+ */
+goog.dom.vendor.getVendorJsPrefix = function() {
+ if (goog.userAgent.WEBKIT) {
+ return 'Webkit';
+ } else if (goog.userAgent.GECKO) {
+ return 'Moz';
+ } else if (goog.userAgent.IE) {
+ return 'ms';
+ } else if (goog.userAgent.OPERA) {
+ return 'O';
+ }
+
+ return null;
+};
+
+
+/**
+ * Returns the vendor prefix used in CSS properties.
+ *
+ * @return {?string} The vendor prefix or null if there is none.
+ */
+goog.dom.vendor.getVendorPrefix = function() {
+ if (goog.userAgent.WEBKIT) {
+ return '-webkit';
+ } else if (goog.userAgent.GECKO) {
+ return '-moz';
+ } else if (goog.userAgent.IE) {
+ return '-ms';
+ } else if (goog.userAgent.OPERA) {
+ return '-o';
+ }
+
+ return null;
+};
+
+
+/**
+ * @param {string} propertyName A property name.
+ * @param {!Object=} opt_object If provided, we verify if the property exists in
+ * the object.
+ * @return {?string} A vendor prefixed property name, or null if it does not
+ * exist.
+ */
+goog.dom.vendor.getPrefixedPropertyName = function(propertyName, opt_object) {
+ // We first check for a non-prefixed property, if available.
+ if (opt_object && propertyName in opt_object) {
+ return propertyName;
+ }
+ var prefix = goog.dom.vendor.getVendorJsPrefix();
+ if (prefix) {
+ prefix = prefix.toLowerCase();
+ var prefixedPropertyName = prefix + goog.string.toTitleCase(propertyName);
+ return (!goog.isDef(opt_object) || prefixedPropertyName in opt_object) ?
+ prefixedPropertyName :
+ null;
+ }
+ return null;
+};
+
+
+/**
+ * @param {string} eventType An event type.
+ * @return {string} A lower-cased vendor prefixed event type.
+ */
+goog.dom.vendor.getPrefixedEventType = function(eventType) {
+ var prefix = goog.dom.vendor.getVendorJsPrefix() || '';
+ return (prefix + eventType).toLowerCase();
+};
diff --git a/chromium/third_party/ink/closure/events/browserevent.js b/chromium/third_party/ink/closure/events/browserevent.js
new file mode 100644
index 00000000000..3c557cac5c4
--- /dev/null
+++ b/chromium/third_party/ink/closure/events/browserevent.js
@@ -0,0 +1,470 @@
+// Copyright 2005 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview A patched, standardized event object for browser events.
+ *
+ * <pre>
+ * The patched event object contains the following members:
+ * - type {string} Event type, e.g. 'click'
+ * - target {Object} The element that actually triggered the event
+ * - currentTarget {Object} The element the listener is attached to
+ * - relatedTarget {Object} For mouseover and mouseout, the previous object
+ * - offsetX {number} X-coordinate relative to target
+ * - offsetY {number} Y-coordinate relative to target
+ * - clientX {number} X-coordinate relative to viewport
+ * - clientY {number} Y-coordinate relative to viewport
+ * - screenX {number} X-coordinate relative to the edge of the screen
+ * - screenY {number} Y-coordinate relative to the edge of the screen
+ * - button {number} Mouse button. Use isButton() to test.
+ * - keyCode {number} Key-code
+ * - ctrlKey {boolean} Was ctrl key depressed
+ * - altKey {boolean} Was alt key depressed
+ * - shiftKey {boolean} Was shift key depressed
+ * - metaKey {boolean} Was meta key depressed
+ * - pointerId {number} Pointer ID
+ * - pointerType {string} Pointer type, e.g. 'mouse', 'pen', or 'touch'
+ * - defaultPrevented {boolean} Whether the default action has been prevented
+ * - state {Object} History state object
+ *
+ * NOTE: The keyCode member contains the raw browser keyCode. For normalized
+ * key and character code use {@link goog.events.KeyHandler}.
+ * </pre>
+ *
+ * @author pupius@google.com (Daniel Pupius)
+ * @author arv@google.com (Erik Arvidsson)
+ */
+
+goog.provide('goog.events.BrowserEvent');
+goog.provide('goog.events.BrowserEvent.MouseButton');
+goog.provide('goog.events.BrowserEvent.PointerType');
+
+goog.require('goog.debug');
+goog.require('goog.events.BrowserFeature');
+goog.require('goog.events.Event');
+goog.require('goog.events.EventType');
+goog.require('goog.reflect');
+goog.require('goog.userAgent');
+
+
+
+/**
+ * Accepts a browser event object and creates a patched, cross browser event
+ * object.
+ * The content of this object will not be initialized if no event object is
+ * provided. If this is the case, init() needs to be invoked separately.
+ * @param {Event=} opt_e Browser event object.
+ * @param {EventTarget=} opt_currentTarget Current target for event.
+ * @constructor
+ * @extends {goog.events.Event}
+ */
+goog.events.BrowserEvent = function(opt_e, opt_currentTarget) {
+ goog.events.BrowserEvent.base(this, 'constructor', opt_e ? opt_e.type : '');
+
+ /**
+ * Target that fired the event.
+ * @override
+ * @type {Node}
+ */
+ this.target = null;
+
+ /**
+ * Node that had the listener attached.
+ * @override
+ * @type {Node|undefined}
+ */
+ this.currentTarget = null;
+
+ /**
+ * For mouseover and mouseout events, the related object for the event.
+ * @type {Node}
+ */
+ this.relatedTarget = null;
+
+ /**
+ * X-coordinate relative to target.
+ * @type {number}
+ */
+ this.offsetX = 0;
+
+ /**
+ * Y-coordinate relative to target.
+ * @type {number}
+ */
+ this.offsetY = 0;
+
+ /**
+ * X-coordinate relative to the window.
+ * @type {number}
+ */
+ this.clientX = 0;
+
+ /**
+ * Y-coordinate relative to the window.
+ * @type {number}
+ */
+ this.clientY = 0;
+
+ /**
+ * X-coordinate relative to the monitor.
+ * @type {number}
+ */
+ this.screenX = 0;
+
+ /**
+ * Y-coordinate relative to the monitor.
+ * @type {number}
+ */
+ this.screenY = 0;
+
+ /**
+ * Which mouse button was pressed.
+ * @type {number}
+ */
+ this.button = 0;
+
+ /**
+ * Key of key press.
+ * @type {string}
+ */
+ this.key = '';
+
+ /**
+ * Keycode of key press.
+ * @type {number}
+ */
+ this.keyCode = 0;
+
+ /**
+ * Keycode of key press.
+ * @type {number}
+ */
+ this.charCode = 0;
+
+ /**
+ * Whether control was pressed at time of event.
+ * @type {boolean}
+ */
+ this.ctrlKey = false;
+
+ /**
+ * Whether alt was pressed at time of event.
+ * @type {boolean}
+ */
+ this.altKey = false;
+
+ /**
+ * Whether shift was pressed at time of event.
+ * @type {boolean}
+ */
+ this.shiftKey = false;
+
+ /**
+ * Whether the meta key was pressed at time of event.
+ * @type {boolean}
+ */
+ this.metaKey = false;
+
+ /**
+ * History state object, only set for PopState events where it's a copy of the
+ * state object provided to pushState or replaceState.
+ * @type {Object}
+ */
+ this.state = null;
+
+ /**
+ * Whether the default platform modifier key was pressed at time of event.
+ * (This is control for all platforms except Mac, where it's Meta.)
+ * @type {boolean}
+ */
+ this.platformModifierKey = false;
+
+ /**
+ * @type {number}
+ */
+ this.pointerId = 0;
+
+ /**
+ * @type {string}
+ */
+ this.pointerType = '';
+
+ /**
+ * The browser event object.
+ * @private {Event}
+ */
+ this.event_ = null;
+
+ if (opt_e) {
+ this.init(opt_e, opt_currentTarget);
+ }
+};
+goog.inherits(goog.events.BrowserEvent, goog.events.Event);
+
+
+/**
+ * Normalized button constants for the mouse.
+ * @enum {number}
+ */
+goog.events.BrowserEvent.MouseButton = {
+ LEFT: 0,
+ MIDDLE: 1,
+ RIGHT: 2
+};
+
+
+/**
+ * Normalized pointer type constants for pointer events.
+ * @enum {string}
+ */
+goog.events.BrowserEvent.PointerType = {
+ MOUSE: 'mouse',
+ PEN: 'pen',
+ TOUCH: 'touch'
+};
+
+
+/**
+ * Static data for mapping mouse buttons.
+ * @type {!Array<number>}
+ * @deprecated Use {@code goog.events.BrowserEvent.IE_BUTTON_MAP} instead.
+ */
+goog.events.BrowserEvent.IEButtonMap = goog.debug.freeze([
+ 1, // LEFT
+ 4, // MIDDLE
+ 2 // RIGHT
+]);
+
+
+/**
+ * Static data for mapping mouse buttons.
+ * @const {!Array<number>}
+ */
+goog.events.BrowserEvent.IE_BUTTON_MAP = goog.events.BrowserEvent.IEButtonMap;
+
+
+/**
+ * Static data for mapping MSPointerEvent types to PointerEvent types.
+ * @const {!Object<number, goog.events.BrowserEvent.PointerType>}
+ */
+goog.events.BrowserEvent.IE_POINTER_TYPE_MAP = goog.debug.freeze({
+ 2: goog.events.BrowserEvent.PointerType.TOUCH,
+ 3: goog.events.BrowserEvent.PointerType.PEN,
+ 4: goog.events.BrowserEvent.PointerType.MOUSE
+});
+
+
+/**
+ * Accepts a browser event object and creates a patched, cross browser event
+ * object.
+ * @param {Event} e Browser event object.
+ * @param {EventTarget=} opt_currentTarget Current target for event.
+ */
+goog.events.BrowserEvent.prototype.init = function(e, opt_currentTarget) {
+ var type = this.type = e.type;
+
+ /**
+ * On touch devices use the first "changed touch" as the relevant touch.
+ * @type {Touch}
+ */
+ var relevantTouch = e.changedTouches ? e.changedTouches[0] : null;
+
+ // TODO(nicksantos): Change this.target to type EventTarget.
+ this.target = /** @type {Node} */ (e.target) || e.srcElement;
+
+ // TODO(nicksantos): Change this.currentTarget to type EventTarget.
+ this.currentTarget = /** @type {Node} */ (opt_currentTarget);
+
+ var relatedTarget = /** @type {Node} */ (e.relatedTarget);
+ if (relatedTarget) {
+ // There's a bug in FireFox where sometimes, relatedTarget will be a
+ // chrome element, and accessing any property of it will get a permission
+ // denied exception. See:
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=497780
+ if (goog.userAgent.GECKO) {
+ if (!goog.reflect.canAccessProperty(relatedTarget, 'nodeName')) {
+ relatedTarget = null;
+ }
+ }
+ } else if (type == goog.events.EventType.MOUSEOVER) {
+ relatedTarget = e.fromElement;
+ } else if (type == goog.events.EventType.MOUSEOUT) {
+ relatedTarget = e.toElement;
+ }
+
+ this.relatedTarget = relatedTarget;
+
+ if (!goog.isNull(relevantTouch)) {
+ this.clientX = relevantTouch.clientX !== undefined ? relevantTouch.clientX :
+ relevantTouch.pageX;
+ this.clientY = relevantTouch.clientY !== undefined ? relevantTouch.clientY :
+ relevantTouch.pageY;
+ this.screenX = relevantTouch.screenX || 0;
+ this.screenY = relevantTouch.screenY || 0;
+ } else {
+ // Webkit emits a lame warning whenever layerX/layerY is accessed.
+ // http://code.google.com/p/chromium/issues/detail?id=101733
+ this.offsetX = (goog.userAgent.WEBKIT || e.offsetX !== undefined) ?
+ e.offsetX :
+ e.layerX;
+ this.offsetY = (goog.userAgent.WEBKIT || e.offsetY !== undefined) ?
+ e.offsetY :
+ e.layerY;
+ this.clientX = e.clientX !== undefined ? e.clientX : e.pageX;
+ this.clientY = e.clientY !== undefined ? e.clientY : e.pageY;
+ this.screenX = e.screenX || 0;
+ this.screenY = e.screenY || 0;
+ }
+
+ this.button = e.button;
+
+ this.keyCode = e.keyCode || 0;
+ this.key = e.key || '';
+ this.charCode = e.charCode || (type == 'keypress' ? e.keyCode : 0);
+ this.ctrlKey = e.ctrlKey;
+ this.altKey = e.altKey;
+ this.shiftKey = e.shiftKey;
+ this.metaKey = e.metaKey;
+ this.platformModifierKey = goog.userAgent.MAC ? e.metaKey : e.ctrlKey;
+ this.pointerId = e.pointerId || 0;
+ this.pointerType = goog.events.BrowserEvent.getPointerType_(e);
+ this.state = e.state;
+ this.event_ = e;
+ if (e.defaultPrevented) {
+ this.preventDefault();
+ }
+};
+
+
+/**
+ * Tests to see which button was pressed during the event. This is really only
+ * useful in IE and Gecko browsers. And in IE, it's only useful for
+ * mousedown/mouseup events, because click only fires for the left mouse button.
+ *
+ * Safari 2 only reports the left button being clicked, and uses the value '1'
+ * instead of 0. Opera only reports a mousedown event for the middle button, and
+ * no mouse events for the right button. Opera has default behavior for left and
+ * middle click that can only be overridden via a configuration setting.
+ *
+ * There's a nice table of this mess at http://www.unixpapa.com/js/mouse.html.
+ *
+ * @param {goog.events.BrowserEvent.MouseButton} button The button
+ * to test for.
+ * @return {boolean} True if button was pressed.
+ */
+goog.events.BrowserEvent.prototype.isButton = function(button) {
+ if (!goog.events.BrowserFeature.HAS_W3C_BUTTON) {
+ if (this.type == 'click') {
+ return button == goog.events.BrowserEvent.MouseButton.LEFT;
+ } else {
+ return !!(
+ this.event_.button & goog.events.BrowserEvent.IE_BUTTON_MAP[button]);
+ }
+ } else {
+ return this.event_.button == button;
+ }
+};
+
+
+/**
+ * Whether this has an "action"-producing mouse button.
+ *
+ * By definition, this includes left-click on windows/linux, and left-click
+ * without the ctrl key on Macs.
+ *
+ * @return {boolean} The result.
+ */
+goog.events.BrowserEvent.prototype.isMouseActionButton = function() {
+ // Webkit does not ctrl+click to be a right-click, so we
+ // normalize it to behave like Gecko and Opera.
+ return this.isButton(goog.events.BrowserEvent.MouseButton.LEFT) &&
+ !(goog.userAgent.WEBKIT && goog.userAgent.MAC && this.ctrlKey);
+};
+
+
+/**
+ * @override
+ */
+goog.events.BrowserEvent.prototype.stopPropagation = function() {
+ goog.events.BrowserEvent.superClass_.stopPropagation.call(this);
+ if (this.event_.stopPropagation) {
+ this.event_.stopPropagation();
+ } else {
+ this.event_.cancelBubble = true;
+ }
+};
+
+
+/**
+ * @override
+ */
+goog.events.BrowserEvent.prototype.preventDefault = function() {
+ goog.events.BrowserEvent.superClass_.preventDefault.call(this);
+ var be = this.event_;
+ if (!be.preventDefault) {
+ be.returnValue = false;
+ if (goog.events.BrowserFeature.SET_KEY_CODE_TO_PREVENT_DEFAULT) {
+
+ try {
+ // Most keys can be prevented using returnValue. Some special keys
+ // require setting the keyCode to -1 as well:
+ //
+ // In IE7:
+ // F3, F5, F10, F11, Ctrl+P, Crtl+O, Ctrl+F (these are taken from IE6)
+ //
+ // In IE8:
+ // Ctrl+P, Crtl+O, Ctrl+F (F1-F12 cannot be stopped through the event)
+ //
+ // We therefore do this for all function keys as well as when Ctrl key
+ // is pressed.
+ var VK_F1 = 112;
+ var VK_F12 = 123;
+ if (be.ctrlKey || be.keyCode >= VK_F1 && be.keyCode <= VK_F12) {
+ be.keyCode = -1;
+ }
+ } catch (ex) {
+ // IE throws an 'access denied' exception when trying to change
+ // keyCode in some situations (e.g. srcElement is input[type=file],
+ // or srcElement is an anchor tag rewritten by parent's innerHTML).
+ // Do nothing in this case.
+ }
+ }
+ } else {
+ be.preventDefault();
+ }
+};
+
+
+/**
+ * @return {Event} The underlying browser event object.
+ */
+goog.events.BrowserEvent.prototype.getBrowserEvent = function() {
+ return this.event_;
+};
+
+
+/**
+ * Extracts the pointer type from the given event.
+ * @param {!Event} e
+ * @return {string} The pointer type, e.g. 'mouse', 'pen', or 'touch'.
+ * @private
+ */
+goog.events.BrowserEvent.getPointerType_ = function(e) {
+ if (goog.isString(e.pointerType)) {
+ return e.pointerType;
+ }
+ // IE10 uses integer codes for pointer type.
+ // https://msdn.microsoft.com/en-us/library/hh772359(v=vs.85).aspx
+ return goog.events.BrowserEvent.IE_POINTER_TYPE_MAP[e.pointerType] || '';
+};
diff --git a/chromium/third_party/ink/closure/events/browserfeature.js b/chromium/third_party/ink/closure/events/browserfeature.js
new file mode 100644
index 00000000000..10ef20d22e8
--- /dev/null
+++ b/chromium/third_party/ink/closure/events/browserfeature.js
@@ -0,0 +1,140 @@
+// Copyright 2010 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Browser capability checks for the events package.
+ *
+ * @author zhyder@google.com (Zohair Hyder)
+ */
+
+
+goog.provide('goog.events.BrowserFeature');
+
+goog.require('goog.userAgent');
+goog.scope(function() {
+
+
+
+/**
+ * Enum of browser capabilities.
+ * @enum {boolean}
+ */
+goog.events.BrowserFeature = {
+ /**
+ * Whether the button attribute of the event is W3C compliant. False in
+ * Internet Explorer prior to version 9; document-version dependent.
+ */
+ HAS_W3C_BUTTON:
+ !goog.userAgent.IE || goog.userAgent.isDocumentModeOrHigher(9),
+
+ /**
+ * Whether the browser supports full W3C event model.
+ */
+ HAS_W3C_EVENT_SUPPORT:
+ !goog.userAgent.IE || goog.userAgent.isDocumentModeOrHigher(9),
+
+ /**
+ * To prevent default in IE7-8 for certain keydown events we need set the
+ * keyCode to -1.
+ */
+ SET_KEY_CODE_TO_PREVENT_DEFAULT:
+ goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9'),
+
+ /**
+ * Whether the {@code navigator.onLine} property is supported.
+ */
+ HAS_NAVIGATOR_ONLINE_PROPERTY:
+ !goog.userAgent.WEBKIT || goog.userAgent.isVersionOrHigher('528'),
+
+ /**
+ * Whether HTML5 network online/offline events are supported.
+ */
+ HAS_HTML5_NETWORK_EVENT_SUPPORT:
+ goog.userAgent.GECKO && goog.userAgent.isVersionOrHigher('1.9b') ||
+ goog.userAgent.IE && goog.userAgent.isVersionOrHigher('8') ||
+ goog.userAgent.OPERA && goog.userAgent.isVersionOrHigher('9.5') ||
+ goog.userAgent.WEBKIT && goog.userAgent.isVersionOrHigher('528'),
+
+ /**
+ * Whether HTML5 network events fire on document.body, or otherwise the
+ * window.
+ */
+ HTML5_NETWORK_EVENTS_FIRE_ON_BODY:
+ goog.userAgent.GECKO && !goog.userAgent.isVersionOrHigher('8') ||
+ goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9'),
+
+ /**
+ * Whether touch is enabled in the browser.
+ */
+ TOUCH_ENABLED:
+ ('ontouchstart' in goog.global ||
+ !!(goog.global['document'] && document.documentElement &&
+ 'ontouchstart' in document.documentElement) ||
+ // IE10 uses non-standard touch events, so it has a different check.
+ !!(goog.global['navigator'] &&
+ (goog.global['navigator']['maxTouchPoints'] ||
+ goog.global['navigator']['msMaxTouchPoints']))),
+
+ /**
+ * Whether addEventListener supports W3C standard pointer events.
+ * http://www.w3.org/TR/pointerevents/
+ */
+ POINTER_EVENTS: ('PointerEvent' in goog.global),
+
+ /**
+ * Whether addEventListener supports MSPointer events (only used in IE10).
+ * http://msdn.microsoft.com/en-us/library/ie/hh772103(v=vs.85).aspx
+ * http://msdn.microsoft.com/library/hh673557(v=vs.85).aspx
+ */
+ MSPOINTER_EVENTS:
+ ('MSPointerEvent' in goog.global &&
+ !!(goog.global['navigator'] &&
+ goog.global['navigator']['msPointerEnabled'])),
+
+ /**
+ * Whether addEventListener supports {passive: true}.
+ * https://developers.google.com/web/updates/2016/06/passive-event-listeners
+ */
+ PASSIVE_EVENTS: purify(function() {
+ // If we're in a web worker or other custom environment, we can't tell.
+ if (!goog.global.addEventListener || !Object.defineProperty) { // IE 8
+ return false;
+ }
+
+ var passive = false;
+ var options = Object.defineProperty({}, 'passive', {
+ get: function() {
+ passive = true;
+ }
+ });
+ goog.global.addEventListener('test', goog.nullFunction, options);
+ goog.global.removeEventListener('test', goog.nullFunction, options);
+
+ return passive;
+ })
+};
+
+
+/**
+ * Tricks Closure Compiler into believing that a function is pure. The compiler
+ * assumes that any `valueOf` function is pure, without analyzing its contents.
+ *
+ * @param {function(): T} fn
+ * @return {T}
+ * @template T
+ */
+function purify(fn) {
+ return ({valueOf: fn}).valueOf();
+}
+}); // goog.scope
diff --git a/chromium/third_party/ink/closure/events/event.js b/chromium/third_party/ink/closure/events/event.js
new file mode 100644
index 00000000000..22efba96840
--- /dev/null
+++ b/chromium/third_party/ink/closure/events/event.js
@@ -0,0 +1,144 @@
+// Copyright 2005 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview A base class for event objects.
+ *
+ * @author pupius@google.com (Daniel Pupius)
+ */
+
+
+goog.provide('goog.events.Event');
+goog.provide('goog.events.EventLike');
+
+/**
+ * goog.events.Event no longer depends on goog.Disposable. Keep requiring
+ * goog.Disposable here to not break projects which assume this dependency.
+ * @suppress {extraRequire}
+ */
+goog.require('goog.Disposable');
+goog.require('goog.events.EventId');
+
+
+/**
+ * A typedef for event like objects that are dispatchable via the
+ * goog.events.dispatchEvent function. strings are treated as the type for a
+ * goog.events.Event. Objects are treated as an extension of a new
+ * goog.events.Event with the type property of the object being used as the type
+ * of the Event.
+ * @typedef {string|Object|goog.events.Event|goog.events.EventId}
+ */
+goog.events.EventLike;
+
+
+
+/**
+ * A base class for event objects, so that they can support preventDefault and
+ * stopPropagation.
+ *
+ * @suppress {underscore} Several properties on this class are technically
+ * public, but referencing these properties outside this package is strongly
+ * discouraged.
+ *
+ * @param {string|!goog.events.EventId} type Event Type.
+ * @param {Object=} opt_target Reference to the object that is the target of
+ * this event. It has to implement the {@code EventTarget} interface
+ * declared at {@link http://developer.mozilla.org/en/DOM/EventTarget}.
+ * @constructor
+ */
+goog.events.Event = function(type, opt_target) {
+ /**
+ * Event type.
+ * @type {string}
+ */
+ this.type = type instanceof goog.events.EventId ? String(type) : type;
+
+ /**
+ * TODO(tbreisacher): The type should probably be
+ * EventTarget|goog.events.EventTarget.
+ *
+ * Target of the event.
+ * @type {Object|undefined}
+ */
+ this.target = opt_target;
+
+ /**
+ * Object that had the listener attached.
+ * @type {Object|undefined}
+ */
+ this.currentTarget = this.target;
+
+ /**
+ * Whether to cancel the event in internal capture/bubble processing for IE.
+ * @type {boolean}
+ * @public
+ */
+ this.propagationStopped_ = false;
+
+ /**
+ * Whether the default action has been prevented.
+ * This is a property to match the W3C specification at
+ * {@link http://www.w3.org/TR/DOM-Level-3-Events/
+ * #events-event-type-defaultPrevented}.
+ * Must be treated as read-only outside the class.
+ * @type {boolean}
+ */
+ this.defaultPrevented = false;
+
+ /**
+ * Return value for in internal capture/bubble processing for IE.
+ * @type {boolean}
+ * @public
+ */
+ this.returnValue_ = true;
+};
+
+
+/**
+ * Stops event propagation.
+ */
+goog.events.Event.prototype.stopPropagation = function() {
+ this.propagationStopped_ = true;
+};
+
+
+/**
+ * Prevents the default action, for example a link redirecting to a url.
+ */
+goog.events.Event.prototype.preventDefault = function() {
+ this.defaultPrevented = true;
+ this.returnValue_ = false;
+};
+
+
+/**
+ * Stops the propagation of the event. It is equivalent to
+ * {@code e.stopPropagation()}, but can be used as the callback argument of
+ * {@link goog.events.listen} without declaring another function.
+ * @param {!goog.events.Event} e An event.
+ */
+goog.events.Event.stopPropagation = function(e) {
+ e.stopPropagation();
+};
+
+
+/**
+ * Prevents the default action. It is equivalent to
+ * {@code e.preventDefault()}, but can be used as the callback argument of
+ * {@link goog.events.listen} without declaring another function.
+ * @param {!goog.events.Event} e An event.
+ */
+goog.events.Event.preventDefault = function(e) {
+ e.preventDefault();
+};
diff --git a/chromium/third_party/ink/closure/events/eventhandler.js b/chromium/third_party/ink/closure/events/eventhandler.js
new file mode 100644
index 00000000000..6a887f9c6bf
--- /dev/null
+++ b/chromium/third_party/ink/closure/events/eventhandler.js
@@ -0,0 +1,479 @@
+// Copyright 2005 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Class to create objects which want to handle multiple events
+ * and have their listeners easily cleaned up via a dispose method.
+ *
+ * Example:
+ * <pre>
+ * function Something() {
+ * Something.base(this);
+ *
+ * ... set up object ...
+ *
+ * // Add event listeners
+ * this.listen(this.starEl, goog.events.EventType.CLICK, this.handleStar);
+ * this.listen(this.headerEl, goog.events.EventType.CLICK, this.expand);
+ * this.listen(this.collapseEl, goog.events.EventType.CLICK, this.collapse);
+ * this.listen(this.infoEl, goog.events.EventType.MOUSEOVER, this.showHover);
+ * this.listen(this.infoEl, goog.events.EventType.MOUSEOUT, this.hideHover);
+ * }
+ * goog.inherits(Something, goog.events.EventHandler);
+ *
+ * Something.prototype.disposeInternal = function() {
+ * Something.base(this, 'disposeInternal');
+ * goog.dom.removeNode(this.container);
+ * };
+ *
+ *
+ * // Then elsewhere:
+ *
+ * var activeSomething = null;
+ * function openSomething() {
+ * activeSomething = new Something();
+ * }
+ *
+ * function closeSomething() {
+ * if (activeSomething) {
+ * activeSomething.dispose(); // Remove event listeners
+ * activeSomething = null;
+ * }
+ * }
+ * </pre>
+ *
+ * @author pupius@google.com (Daniel Pupius)
+ */
+
+goog.provide('goog.events.EventHandler');
+
+goog.require('goog.Disposable');
+goog.require('goog.events');
+goog.require('goog.object');
+
+goog.forwardDeclare('goog.events.EventWrapper');
+
+
+
+/**
+ * Super class for objects that want to easily manage a number of event
+ * listeners. It allows a short cut to listen and also provides a quick way
+ * to remove all events listeners belonging to this object.
+ * @param {SCOPE=} opt_scope Object in whose scope to call the listeners.
+ * @constructor
+ * @extends {goog.Disposable}
+ * @template SCOPE
+ */
+goog.events.EventHandler = function(opt_scope) {
+ goog.Disposable.call(this);
+ // TODO(mknichel): Rename this to this.scope_ and fix the classes in google3
+ // that access this private variable. :(
+ this.handler_ = opt_scope;
+
+ /**
+ * Keys for events that are being listened to.
+ * @type {!Object<!goog.events.Key>}
+ * @private
+ */
+ this.keys_ = {};
+};
+goog.inherits(goog.events.EventHandler, goog.Disposable);
+
+
+/**
+ * Utility array used to unify the cases of listening for an array of types
+ * and listening for a single event, without using recursion or allocating
+ * an array each time.
+ * @type {!Array<string>}
+ * @const
+ * @private
+ */
+goog.events.EventHandler.typeArray_ = [];
+
+
+/**
+ * Listen to an event on a Listenable. If the function is omitted then the
+ * EventHandler's handleEvent method will be used.
+ * @param {goog.events.ListenableType} src Event source.
+ * @param {string|Array<string>|
+ * !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}
+ * type Event type to listen for or array of event types.
+ * @param {function(this:SCOPE, EVENTOBJ):?|{handleEvent:function(?):?}|null=}
+ * opt_fn Optional callback function to be used as the listener or an object
+ * with handleEvent function.
+ * @param {(boolean|!AddEventListenerOptions)=} opt_options
+ * @return {THIS} This object, allowing for chaining of calls.
+ * @this {THIS}
+ * @template EVENTOBJ, THIS
+ */
+goog.events.EventHandler.prototype.listen = function(
+ src, type, opt_fn, opt_options) {
+ var self = /** @type {!goog.events.EventHandler} */ (this);
+ return self.listen_(src, type, opt_fn, opt_options);
+};
+
+
+/**
+ * Listen to an event on a Listenable. If the function is omitted then the
+ * EventHandler's handleEvent method will be used.
+ * @param {goog.events.ListenableType} src Event source.
+ * @param {string|Array<string>|
+ * !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}
+ * type Event type to listen for or array of event types.
+ * @param {function(this:T, EVENTOBJ):?|{handleEvent:function(this:T, ?):?}|
+ * null|undefined} fn Optional callback function to be used as the
+ * listener or an object with handleEvent function.
+ * @param {boolean|!AddEventListenerOptions|undefined} options
+ * @param {T} scope Object in whose scope to call the listener.
+ * @return {THIS} This object, allowing for chaining of calls.
+ * @this {THIS}
+ * @template T, EVENTOBJ, THIS
+ */
+goog.events.EventHandler.prototype.listenWithScope = function(
+ src, type, fn, options, scope) {
+ var self = /** @type {!goog.events.EventHandler} */ (this);
+ // TODO(mknichel): Deprecate this function.
+ return self.listen_(src, type, fn, options, scope);
+};
+
+
+/**
+ * Listen to an event on a Listenable. If the function is omitted then the
+ * EventHandler's handleEvent method will be used.
+ * @param {goog.events.ListenableType} src Event source.
+ * @param {string|Array<string>|
+ * !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}
+ * type Event type to listen for or array of event types.
+ * @param {function(EVENTOBJ):?|{handleEvent:function(?):?}|null=} opt_fn
+ * Optional callback function to be used as the listener or an object with
+ * handleEvent function.
+ * @param {(boolean|!AddEventListenerOptions)=} opt_options
+ * @param {Object=} opt_scope Object in whose scope to call the listener.
+ * @return {THIS} This object, allowing for chaining of calls.
+ * @this {THIS}
+ * @template EVENTOBJ, THIS
+ * @private
+ */
+goog.events.EventHandler.prototype.listen_ = function(
+ src, type, opt_fn, opt_options, opt_scope) {
+ var self = /** @type {!goog.events.EventHandler} */ (this);
+ if (!goog.isArray(type)) {
+ if (type) {
+ goog.events.EventHandler.typeArray_[0] = type.toString();
+ }
+ type = goog.events.EventHandler.typeArray_;
+ }
+ for (var i = 0; i < type.length; i++) {
+ var listenerObj = goog.events.listen(
+ src, type[i], opt_fn || self.handleEvent, opt_options || false,
+ opt_scope || self.handler_ || self);
+
+ if (!listenerObj) {
+ // When goog.events.listen run on OFF_AND_FAIL or OFF_AND_SILENT
+ // (goog.events.CaptureSimulationMode) in IE8-, it will return null
+ // value.
+ return self;
+ }
+
+ var key = listenerObj.key;
+ self.keys_[key] = listenerObj;
+ }
+
+ return self;
+};
+
+
+/**
+ * Listen to an event on a Listenable. If the function is omitted, then the
+ * EventHandler's handleEvent method will be used. After the event has fired the
+ * event listener is removed from the target. If an array of event types is
+ * provided, each event type will be listened to once.
+ * @param {goog.events.ListenableType} src Event source.
+ * @param {string|Array<string>|
+ * !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}
+ * type Event type to listen for or array of event types.
+ * @param {function(this:SCOPE, EVENTOBJ):?|{handleEvent:function(?):?}|null=}
+ * opt_fn
+ * Optional callback function to be used as the listener or an object with
+ * handleEvent function.
+ * @param {(boolean|!AddEventListenerOptions)=} opt_options
+ * @return {THIS} This object, allowing for chaining of calls.
+ * @this {THIS}
+ * @template EVENTOBJ, THIS
+ */
+goog.events.EventHandler.prototype.listenOnce = function(
+ src, type, opt_fn, opt_options) {
+ var self = /** @type {!goog.events.EventHandler} */ (this);
+ return self.listenOnce_(src, type, opt_fn, opt_options);
+};
+
+
+/**
+ * Listen to an event on a Listenable. If the function is omitted, then the
+ * EventHandler's handleEvent method will be used. After the event has fired the
+ * event listener is removed from the target. If an array of event types is
+ * provided, each event type will be listened to once.
+ * @param {goog.events.ListenableType} src Event source.
+ * @param {string|Array<string>|
+ * !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}
+ * type Event type to listen for or array of event types.
+ * @param {function(this:T, EVENTOBJ):?|{handleEvent:function(this:T, ?):?}|
+ * null|undefined} fn Optional callback function to be used as the
+ * listener or an object with handleEvent function.
+ * @param {boolean|undefined} capture Optional whether to use capture phase.
+ * @param {T} scope Object in whose scope to call the listener.
+ * @return {THIS} This object, allowing for chaining of calls.
+ * @this {THIS}
+ * @template T, EVENTOBJ, THIS
+ */
+goog.events.EventHandler.prototype.listenOnceWithScope = function(
+ src, type, fn, capture, scope) {
+ var self = /** @type {!goog.events.EventHandler} */ (this);
+ // TODO(mknichel): Deprecate this function.
+ return self.listenOnce_(src, type, fn, capture, scope);
+};
+
+
+/**
+ * Listen to an event on a Listenable. If the function is omitted, then the
+ * EventHandler's handleEvent method will be used. After the event has fired
+ * the event listener is removed from the target. If an array of event types is
+ * provided, each event type will be listened to once.
+ * @param {goog.events.ListenableType} src Event source.
+ * @param {string|Array<string>|
+ * !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}
+ * type Event type to listen for or array of event types.
+ * @param {function(EVENTOBJ):?|{handleEvent:function(?):?}|null=} opt_fn
+ * Optional callback function to be used as the listener or an object with
+ * handleEvent function.
+ * @param {(boolean|!AddEventListenerOptions)=} opt_options
+ * @param {Object=} opt_scope Object in whose scope to call the listener.
+ * @return {THIS} This object, allowing for chaining of calls.
+ * @this {THIS}
+ * @template EVENTOBJ, THIS
+ * @private
+ */
+goog.events.EventHandler.prototype.listenOnce_ = function(
+ src, type, opt_fn, opt_options, opt_scope) {
+ var self = /** @type {!goog.events.EventHandler} */ (this);
+ if (goog.isArray(type)) {
+ for (var i = 0; i < type.length; i++) {
+ self.listenOnce_(src, type[i], opt_fn, opt_options, opt_scope);
+ }
+ } else {
+ var listenerObj = goog.events.listenOnce(
+ src, type, opt_fn || self.handleEvent, opt_options,
+ opt_scope || self.handler_ || self);
+ if (!listenerObj) {
+ // When goog.events.listen run on OFF_AND_FAIL or OFF_AND_SILENT
+ // (goog.events.CaptureSimulationMode) in IE8-, it will return null
+ // value.
+ return self;
+ }
+
+ var key = listenerObj.key;
+ self.keys_[key] = listenerObj;
+ }
+
+ return self;
+};
+
+
+/**
+ * Adds an event listener with a specific event wrapper on a DOM Node or an
+ * object that has implemented {@link goog.events.EventTarget}. A listener can
+ * only be added once to an object.
+ *
+ * @param {EventTarget|goog.events.EventTarget} src The node to listen to
+ * events on.
+ * @param {goog.events.EventWrapper} wrapper Event wrapper to use.
+ * @param {function(this:SCOPE, ?):?|{handleEvent:function(?):?}|null} listener
+ * Callback method, or an object with a handleEvent function.
+ * @param {boolean=} opt_capt Whether to fire in capture phase (defaults to
+ * false).
+ * @return {THIS} This object, allowing for chaining of calls.
+ * @this {THIS}
+ * @template THIS
+ */
+goog.events.EventHandler.prototype.listenWithWrapper = function(
+ src, wrapper, listener, opt_capt) {
+ var self = /** @type {!goog.events.EventHandler} */ (this);
+ // TODO(mknichel): Remove the opt_scope from this function and then
+ // templatize it.
+ return self.listenWithWrapper_(src, wrapper, listener, opt_capt);
+};
+
+
+/**
+ * Adds an event listener with a specific event wrapper on a DOM Node or an
+ * object that has implemented {@link goog.events.EventTarget}. A listener can
+ * only be added once to an object.
+ *
+ * @param {EventTarget|goog.events.EventTarget} src The node to listen to
+ * events on.
+ * @param {goog.events.EventWrapper} wrapper Event wrapper to use.
+ * @param {function(this:T, ?):?|{handleEvent:function(this:T, ?):?}|null}
+ * listener Optional callback function to be used as the
+ * listener or an object with handleEvent function.
+ * @param {boolean|undefined} capture Optional whether to use capture phase.
+ * @param {T} scope Object in whose scope to call the listener.
+ * @return {THIS} This object, allowing for chaining of calls.
+ * @this {THIS}
+ * @template T, THIS
+ */
+goog.events.EventHandler.prototype.listenWithWrapperAndScope = function(
+ src, wrapper, listener, capture, scope) {
+ var self = /** @type {!goog.events.EventHandler} */ (this);
+ // TODO(mknichel): Deprecate this function.
+ return self.listenWithWrapper_(src, wrapper, listener, capture, scope);
+};
+
+
+/**
+ * Adds an event listener with a specific event wrapper on a DOM Node or an
+ * object that has implemented {@link goog.events.EventTarget}. A listener can
+ * only be added once to an object.
+ *
+ * @param {EventTarget|goog.events.EventTarget} src The node to listen to
+ * events on.
+ * @param {goog.events.EventWrapper} wrapper Event wrapper to use.
+ * @param {function(?):?|{handleEvent:function(?):?}|null} listener Callback
+ * method, or an object with a handleEvent function.
+ * @param {boolean=} opt_capt Whether to fire in capture phase (defaults to
+ * false).
+ * @param {Object=} opt_scope Element in whose scope to call the listener.
+ * @return {THIS} This object, allowing for chaining of calls.
+ * @this {THIS}
+ * @template THIS
+ * @private
+ */
+goog.events.EventHandler.prototype.listenWithWrapper_ = function(
+ src, wrapper, listener, opt_capt, opt_scope) {
+ var self = /** @type {!goog.events.EventHandler} */ (this);
+ wrapper.listen(
+ src, listener, opt_capt, opt_scope || self.handler_ || self, self);
+ return self;
+};
+
+
+/**
+ * @return {number} Number of listeners registered by this handler.
+ */
+goog.events.EventHandler.prototype.getListenerCount = function() {
+ var count = 0;
+ for (var key in this.keys_) {
+ if (Object.prototype.hasOwnProperty.call(this.keys_, key)) {
+ count++;
+ }
+ }
+ return count;
+};
+
+
+/**
+ * Unlistens on an event.
+ * @param {goog.events.ListenableType} src Event source.
+ * @param {string|Array<string>|
+ * !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}
+ * type Event type or array of event types to unlisten to.
+ * @param {function(this:?, EVENTOBJ):?|{handleEvent:function(?):?}|null=}
+ * opt_fn Optional callback function to be used as the listener or an object
+ * with handleEvent function.
+ * @param {(boolean|!EventListenerOptions)=} opt_options
+ * @param {Object=} opt_scope Object in whose scope to call the listener.
+ * @return {THIS} This object, allowing for chaining of calls.
+ * @this {THIS}
+ * @template EVENTOBJ, THIS
+ */
+goog.events.EventHandler.prototype.unlisten = function(
+ src, type, opt_fn, opt_options, opt_scope) {
+ var self = /** @type {!goog.events.EventHandler} */ (this);
+ if (goog.isArray(type)) {
+ for (var i = 0; i < type.length; i++) {
+ self.unlisten(src, type[i], opt_fn, opt_options, opt_scope);
+ }
+ } else {
+ var capture =
+ goog.isObject(opt_options) ? !!opt_options.capture : !!opt_options;
+ var listener = goog.events.getListener(
+ src, type, opt_fn || self.handleEvent, capture,
+ opt_scope || self.handler_ || self);
+
+ if (listener) {
+ goog.events.unlistenByKey(listener);
+ delete self.keys_[listener.key];
+ }
+ }
+
+ return self;
+};
+
+
+/**
+ * Removes an event listener which was added with listenWithWrapper().
+ *
+ * @param {EventTarget|goog.events.EventTarget} src The target to stop
+ * listening to events on.
+ * @param {goog.events.EventWrapper} wrapper Event wrapper to use.
+ * @param {function(?):?|{handleEvent:function(?):?}|null} listener The
+ * listener function to remove.
+ * @param {boolean=} opt_capt In DOM-compliant browsers, this determines
+ * whether the listener is fired during the capture or bubble phase of the
+ * event.
+ * @param {Object=} opt_scope Element in whose scope to call the listener.
+ * @return {THIS} This object, allowing for chaining of calls.
+ * @this {THIS}
+ * @template THIS
+ */
+goog.events.EventHandler.prototype.unlistenWithWrapper = function(
+ src, wrapper, listener, opt_capt, opt_scope) {
+ var self = /** @type {!goog.events.EventHandler} */ (this);
+ wrapper.unlisten(
+ src, listener, opt_capt, opt_scope || self.handler_ || self, self);
+ return self;
+};
+
+
+/**
+ * Unlistens to all events.
+ */
+goog.events.EventHandler.prototype.removeAll = function() {
+ goog.object.forEach(this.keys_, function(listenerObj, key) {
+ if (this.keys_.hasOwnProperty(key)) {
+ goog.events.unlistenByKey(listenerObj);
+ }
+ }, this);
+
+ this.keys_ = {};
+};
+
+
+/**
+ * Disposes of this EventHandler and removes all listeners that it registered.
+ * @override
+ * @protected
+ */
+goog.events.EventHandler.prototype.disposeInternal = function() {
+ goog.events.EventHandler.superClass_.disposeInternal.call(this);
+ this.removeAll();
+};
+
+
+/**
+ * Default event handler
+ * @param {goog.events.Event} e Event object.
+ */
+goog.events.EventHandler.prototype.handleEvent = function(e) {
+ throw new Error('EventHandler.handleEvent not implemented');
+};
diff --git a/chromium/third_party/ink/closure/events/eventid.js b/chromium/third_party/ink/closure/events/eventid.js
new file mode 100644
index 00000000000..9ff9e407303
--- /dev/null
+++ b/chromium/third_party/ink/closure/events/eventid.js
@@ -0,0 +1,46 @@
+// Copyright 2013 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+goog.provide('goog.events.EventId');
+
+
+
+/**
+ * A templated class that is used when registering for events. Typical usage:
+ *
+ * /** @type {goog.events.EventId<MyEventObj>} *\
+ * var myEventId = new goog.events.EventId(
+ * goog.events.getUniqueId(('someEvent'));
+ *
+ * // No need to cast or declare here since the compiler knows the
+ * // correct type of 'evt' (MyEventObj).
+ * something.listen(myEventId, function(evt) {});
+ *
+ * @param {string} eventId
+ * @template T
+ * @constructor
+ * @struct
+ * @final
+ */
+goog.events.EventId = function(eventId) {
+ /** @const */ this.id = eventId;
+};
+
+
+/**
+ * @override
+ */
+goog.events.EventId.prototype.toString = function() {
+ return this.id;
+};
diff --git a/chromium/third_party/ink/closure/events/events.js b/chromium/third_party/ink/closure/events/events.js
new file mode 100644
index 00000000000..44669233a51
--- /dev/null
+++ b/chromium/third_party/ink/closure/events/events.js
@@ -0,0 +1,1006 @@
+// Copyright 2005 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview An event manager for both native browser event
+ * targets and custom JavaScript event targets
+ * ({@code goog.events.Listenable}). This provides an abstraction
+ * over browsers' event systems.
+ *
+ * It also provides a simulation of W3C event model's capture phase in
+ * Internet Explorer (IE 8 and below). Caveat: the simulation does not
+ * interact well with listeners registered directly on the elements
+ * (bypassing goog.events) or even with listeners registered via
+ * goog.events in a separate JS binary. In these cases, we provide
+ * no ordering guarantees.
+ *
+ * The listeners will receive a "patched" event object. Such event object
+ * contains normalized values for certain event properties that differs in
+ * different browsers.
+ *
+ * Example usage:
+ * <pre>
+ * goog.events.listen(myNode, 'click', function(e) { alert('woo') });
+ * goog.events.listen(myNode, 'mouseover', mouseHandler, true);
+ * goog.events.unlisten(myNode, 'mouseover', mouseHandler, true);
+ * goog.events.removeAll(myNode);
+ * </pre>
+ *
+ * @author aa@google.com (Aaron Boodman) [Original implementation of listen()]
+ * @author pupius@google.com (Daniel Pupius) [Port to closure plus capture phase
+ * in IE and event object patching]
+ * @author arv@google.com (Erik Arvidsson)
+ *
+ * @see ../demos/events.html
+ * @see ../demos/event-propagation.html
+ * @see ../demos/stopevent.html
+ */
+
+// IMPLEMENTATION NOTES:
+// goog.events stores an auxiliary data structure on each EventTarget
+// source being listened on. This allows us to take advantage of GC,
+// having the data structure GC'd when the EventTarget is GC'd. This
+// GC behavior is equivalent to using W3C DOM Events directly.
+
+goog.provide('goog.events');
+goog.provide('goog.events.CaptureSimulationMode');
+goog.provide('goog.events.Key');
+goog.provide('goog.events.ListenableType');
+
+goog.require('goog.asserts');
+goog.require('goog.debug.entryPointRegistry');
+goog.require('goog.events.BrowserEvent');
+goog.require('goog.events.BrowserFeature');
+goog.require('goog.events.Listenable');
+goog.require('goog.events.ListenerMap');
+
+goog.forwardDeclare('goog.debug.ErrorHandler');
+goog.forwardDeclare('goog.events.EventWrapper');
+
+
+/**
+ * @typedef {number|goog.events.ListenableKey}
+ */
+goog.events.Key;
+
+
+/**
+ * @typedef {EventTarget|goog.events.Listenable}
+ */
+goog.events.ListenableType;
+
+
+/**
+ * Property name on a native event target for the listener map
+ * associated with the event target.
+ * @private @const {string}
+ */
+goog.events.LISTENER_MAP_PROP_ = 'closure_lm_' + ((Math.random() * 1e6) | 0);
+
+
+/**
+ * String used to prepend to IE event types.
+ * @const
+ * @private
+ */
+goog.events.onString_ = 'on';
+
+
+/**
+ * Map of computed "on<eventname>" strings for IE event types. Caching
+ * this removes an extra object allocation in goog.events.listen which
+ * improves IE6 performance.
+ * @const
+ * @dict
+ * @private
+ */
+goog.events.onStringMap_ = {};
+
+
+/**
+ * @enum {number} Different capture simulation mode for IE8-.
+ */
+goog.events.CaptureSimulationMode = {
+ /**
+ * Does not perform capture simulation. Will asserts in IE8- when you
+ * add capture listeners.
+ */
+ OFF_AND_FAIL: 0,
+
+ /**
+ * Does not perform capture simulation, silently ignore capture
+ * listeners.
+ */
+ OFF_AND_SILENT: 1,
+
+ /**
+ * Performs capture simulation.
+ */
+ ON: 2
+};
+
+
+/**
+ * @define {number} The capture simulation mode for IE8-. By default,
+ * this is ON.
+ */
+goog.define('goog.events.CAPTURE_SIMULATION_MODE', 2);
+
+
+/**
+ * Estimated count of total native listeners.
+ * @private {number}
+ */
+goog.events.listenerCountEstimate_ = 0;
+
+
+/**
+ * Adds an event listener for a specific event on a native event
+ * target (such as a DOM element) or an object that has implemented
+ * {@link goog.events.Listenable}. A listener can only be added once
+ * to an object and if it is added again the key for the listener is
+ * returned. Note that if the existing listener is a one-off listener
+ * (registered via listenOnce), it will no longer be a one-off
+ * listener after a call to listen().
+ *
+ * @param {EventTarget|goog.events.Listenable} src The node to listen
+ * to events on.
+ * @param {string|Array<string>|
+ * !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}
+ * type Event type or array of event types.
+ * @param {function(this:T, EVENTOBJ):?|{handleEvent:function(?):?}|null}
+ * listener Callback method, or an object with a handleEvent function.
+ * WARNING: passing an Object is now softly deprecated.
+ * @param {(boolean|!AddEventListenerOptions)=} opt_options
+ * @param {T=} opt_handler Element in whose scope to call the listener.
+ * @return {goog.events.Key} Unique key for the listener.
+ * @template T,EVENTOBJ
+ */
+goog.events.listen = function(src, type, listener, opt_options, opt_handler) {
+ if (opt_options && opt_options.once) {
+ return goog.events.listenOnce(
+ src, type, listener, opt_options, opt_handler);
+ }
+ if (goog.isArray(type)) {
+ for (var i = 0; i < type.length; i++) {
+ goog.events.listen(src, type[i], listener, opt_options, opt_handler);
+ }
+ return null;
+ }
+
+ listener = goog.events.wrapListener(listener);
+ if (goog.events.Listenable.isImplementedBy(src)) {
+ var capture =
+ goog.isObject(opt_options) ? !!opt_options.capture : !!opt_options;
+ return src.listen(
+ /** @type {string|!goog.events.EventId} */ (type), listener, capture,
+ opt_handler);
+ } else {
+ return goog.events.listen_(
+ /** @type {!EventTarget} */ (src), type, listener,
+ /* callOnce */ false, opt_options, opt_handler);
+ }
+};
+
+
+/**
+ * Adds an event listener for a specific event on a native event
+ * target. A listener can only be added once to an object and if it
+ * is added again the key for the listener is returned.
+ *
+ * Note that a one-off listener will not change an existing listener,
+ * if any. On the other hand a normal listener will change existing
+ * one-off listener to become a normal listener.
+ *
+ * @param {EventTarget} src The node to listen to events on.
+ * @param {string|?goog.events.EventId<EVENTOBJ>} type Event type.
+ * @param {!Function} listener Callback function.
+ * @param {boolean} callOnce Whether the listener is a one-off
+ * listener or otherwise.
+ * @param {(boolean|!AddEventListenerOptions)=} opt_options
+ * @param {Object=} opt_handler Element in whose scope to call the listener.
+ * @return {goog.events.ListenableKey} Unique key for the listener.
+ * @template EVENTOBJ
+ * @private
+ */
+goog.events.listen_ = function(
+ src, type, listener, callOnce, opt_options, opt_handler) {
+ if (!type) {
+ throw new Error('Invalid event type');
+ }
+
+ var capture =
+ goog.isObject(opt_options) ? !!opt_options.capture : !!opt_options;
+ if (capture && !goog.events.BrowserFeature.HAS_W3C_EVENT_SUPPORT) {
+ if (goog.events.CAPTURE_SIMULATION_MODE ==
+ goog.events.CaptureSimulationMode.OFF_AND_FAIL) {
+ goog.asserts.fail('Can not register capture listener in IE8-.');
+ return null;
+ } else if (
+ goog.events.CAPTURE_SIMULATION_MODE ==
+ goog.events.CaptureSimulationMode.OFF_AND_SILENT) {
+ return null;
+ }
+ }
+
+ var listenerMap = goog.events.getListenerMap_(src);
+ if (!listenerMap) {
+ src[goog.events.LISTENER_MAP_PROP_] = listenerMap =
+ new goog.events.ListenerMap(src);
+ }
+
+ var listenerObj = /** @type {goog.events.Listener} */ (
+ listenerMap.add(type, listener, callOnce, capture, opt_handler));
+
+ // If the listenerObj already has a proxy, it has been set up
+ // previously. We simply return.
+ if (listenerObj.proxy) {
+ return listenerObj;
+ }
+
+ var proxy = goog.events.getProxy();
+ listenerObj.proxy = proxy;
+
+ proxy.src = src;
+ proxy.listener = listenerObj;
+
+ // Attach the proxy through the browser's API
+ if (src.addEventListener) {
+ // Don't pass an object as `capture` if the browser doesn't support that.
+ if (!goog.events.BrowserFeature.PASSIVE_EVENTS) {
+ opt_options = capture;
+ }
+ // Don't break tests that expect a boolean.
+ if (opt_options === undefined) opt_options = false;
+ src.addEventListener(type.toString(), proxy, opt_options);
+ } else if (src.attachEvent) {
+ // The else if above used to be an unconditional else. It would call
+ // attachEvent come gws or high water. This would sometimes throw an
+ // exception on IE11, spoiling the day of some callers. The previous
+ // incarnation of this code, from 2007, indicates that it replaced an
+ // earlier still version that caused excess allocations on IE6.
+ src.attachEvent(goog.events.getOnString_(type.toString()), proxy);
+ } else {
+ throw new Error('addEventListener and attachEvent are unavailable.');
+ }
+
+ goog.events.listenerCountEstimate_++;
+ return listenerObj;
+};
+
+
+/**
+ * Helper function for returning a proxy function.
+ * @return {!Function} A new or reused function object.
+ */
+goog.events.getProxy = function() {
+ var proxyCallbackFunction = goog.events.handleBrowserEvent_;
+ // Use a local var f to prevent one allocation.
+ var f =
+ goog.events.BrowserFeature.HAS_W3C_EVENT_SUPPORT ? function(eventObject) {
+ return proxyCallbackFunction.call(f.src, f.listener, eventObject);
+ } : function(eventObject) {
+ var v = proxyCallbackFunction.call(f.src, f.listener, eventObject);
+ // NOTE(chrishenry): In IE, we hack in a capture phase. However, if
+ // there is inline event handler which tries to prevent default (for
+ // example <a href="..." onclick="return false">...</a>) in a
+ // descendant element, the prevent default will be overridden
+ // by this listener if this listener were to return true. Hence, we
+ // return undefined.
+ if (!v) return v;
+ };
+ return f;
+};
+
+
+/**
+ * Adds an event listener for a specific event on a native event
+ * target (such as a DOM element) or an object that has implemented
+ * {@link goog.events.Listenable}. After the event has fired the event
+ * listener is removed from the target.
+ *
+ * If an existing listener already exists, listenOnce will do
+ * nothing. In particular, if the listener was previously registered
+ * via listen(), listenOnce() will not turn the listener into a
+ * one-off listener. Similarly, if there is already an existing
+ * one-off listener, listenOnce does not modify the listeners (it is
+ * still a once listener).
+ *
+ * @param {EventTarget|goog.events.Listenable} src The node to listen
+ * to events on.
+ * @param {string|Array<string>|
+ * !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}
+ * type Event type or array of event types.
+ * @param {function(this:T, EVENTOBJ):?|{handleEvent:function(?):?}|null}
+ * listener Callback method.
+ * @param {(boolean|!AddEventListenerOptions)=} opt_options
+ * @param {T=} opt_handler Element in whose scope to call the listener.
+ * @return {goog.events.Key} Unique key for the listener.
+ * @template T,EVENTOBJ
+ */
+goog.events.listenOnce = function(
+ src, type, listener, opt_options, opt_handler) {
+ if (goog.isArray(type)) {
+ for (var i = 0; i < type.length; i++) {
+ goog.events.listenOnce(src, type[i], listener, opt_options, opt_handler);
+ }
+ return null;
+ }
+
+ listener = goog.events.wrapListener(listener);
+ if (goog.events.Listenable.isImplementedBy(src)) {
+ var capture =
+ goog.isObject(opt_options) ? !!opt_options.capture : !!opt_options;
+ return src.listenOnce(
+ /** @type {string|!goog.events.EventId} */ (type), listener, capture,
+ opt_handler);
+ } else {
+ return goog.events.listen_(
+ /** @type {!EventTarget} */ (src), type, listener,
+ /* callOnce */ true, opt_options, opt_handler);
+ }
+};
+
+
+/**
+ * Adds an event listener with a specific event wrapper on a DOM Node or an
+ * object that has implemented {@link goog.events.Listenable}. A listener can
+ * only be added once to an object.
+ *
+ * @param {EventTarget|goog.events.Listenable} src The target to
+ * listen to events on.
+ * @param {goog.events.EventWrapper} wrapper Event wrapper to use.
+ * @param {function(this:T, ?):?|{handleEvent:function(?):?}|null} listener
+ * Callback method, or an object with a handleEvent function.
+ * @param {boolean=} opt_capt Whether to fire in capture phase (defaults to
+ * false).
+ * @param {T=} opt_handler Element in whose scope to call the listener.
+ * @template T
+ */
+goog.events.listenWithWrapper = function(
+ src, wrapper, listener, opt_capt, opt_handler) {
+ wrapper.listen(src, listener, opt_capt, opt_handler);
+};
+
+
+/**
+ * Removes an event listener which was added with listen().
+ *
+ * @param {EventTarget|goog.events.Listenable} src The target to stop
+ * listening to events on.
+ * @param {string|Array<string>|
+ * !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}
+ * type Event type or array of event types to unlisten to.
+ * @param {function(?):?|{handleEvent:function(?):?}|null} listener The
+ * listener function to remove.
+ * @param {(boolean|!EventListenerOptions)=} opt_options
+ * whether the listener is fired during the capture or bubble phase of the
+ * event.
+ * @param {Object=} opt_handler Element in whose scope to call the listener.
+ * @return {?boolean} indicating whether the listener was there to remove.
+ * @template EVENTOBJ
+ */
+goog.events.unlisten = function(src, type, listener, opt_options, opt_handler) {
+ if (goog.isArray(type)) {
+ for (var i = 0; i < type.length; i++) {
+ goog.events.unlisten(src, type[i], listener, opt_options, opt_handler);
+ }
+ return null;
+ }
+ var capture =
+ goog.isObject(opt_options) ? !!opt_options.capture : !!opt_options;
+
+ listener = goog.events.wrapListener(listener);
+ if (goog.events.Listenable.isImplementedBy(src)) {
+ return src.unlisten(
+ /** @type {string|!goog.events.EventId} */ (type), listener, capture,
+ opt_handler);
+ }
+
+ if (!src) {
+ // TODO(chrishenry): We should tighten the API to only accept
+ // non-null objects, or add an assertion here.
+ return false;
+ }
+
+ var listenerMap = goog.events.getListenerMap_(
+ /** @type {!EventTarget} */ (src));
+ if (listenerMap) {
+ var listenerObj = listenerMap.getListener(
+ /** @type {string|!goog.events.EventId} */ (type), listener, capture,
+ opt_handler);
+ if (listenerObj) {
+ return goog.events.unlistenByKey(listenerObj);
+ }
+ }
+
+ return false;
+};
+
+
+/**
+ * Removes an event listener which was added with listen() by the key
+ * returned by listen().
+ *
+ * @param {goog.events.Key} key The key returned by listen() for this
+ * event listener.
+ * @return {boolean} indicating whether the listener was there to remove.
+ */
+goog.events.unlistenByKey = function(key) {
+ // TODO(chrishenry): Remove this check when tests that rely on this
+ // are fixed.
+ if (goog.isNumber(key)) {
+ return false;
+ }
+
+ var listener = key;
+ if (!listener || listener.removed) {
+ return false;
+ }
+
+ var src = listener.src;
+ if (goog.events.Listenable.isImplementedBy(src)) {
+ return /** @type {!goog.events.Listenable} */ (src).unlistenByKey(listener);
+ }
+
+ var type = listener.type;
+ var proxy = listener.proxy;
+ if (src.removeEventListener) {
+ src.removeEventListener(type, proxy, listener.capture);
+ } else if (src.detachEvent) {
+ src.detachEvent(goog.events.getOnString_(type), proxy);
+ }
+ goog.events.listenerCountEstimate_--;
+
+ var listenerMap = goog.events.getListenerMap_(
+ /** @type {!EventTarget} */ (src));
+ // TODO(chrishenry): Try to remove this conditional and execute the
+ // first branch always. This should be safe.
+ if (listenerMap) {
+ listenerMap.removeByKey(listener);
+ if (listenerMap.getTypeCount() == 0) {
+ // Null the src, just because this is simple to do (and useful
+ // for IE <= 7).
+ listenerMap.src = null;
+ // We don't use delete here because IE does not allow delete
+ // on a window object.
+ src[goog.events.LISTENER_MAP_PROP_] = null;
+ }
+ } else {
+ /** @type {!goog.events.Listener} */ (listener).markAsRemoved();
+ }
+
+ return true;
+};
+
+
+/**
+ * Removes an event listener which was added with listenWithWrapper().
+ *
+ * @param {EventTarget|goog.events.Listenable} src The target to stop
+ * listening to events on.
+ * @param {goog.events.EventWrapper} wrapper Event wrapper to use.
+ * @param {function(?):?|{handleEvent:function(?):?}|null} listener The
+ * listener function to remove.
+ * @param {boolean=} opt_capt In DOM-compliant browsers, this determines
+ * whether the listener is fired during the capture or bubble phase of the
+ * event.
+ * @param {Object=} opt_handler Element in whose scope to call the listener.
+ */
+goog.events.unlistenWithWrapper = function(
+ src, wrapper, listener, opt_capt, opt_handler) {
+ wrapper.unlisten(src, listener, opt_capt, opt_handler);
+};
+
+
+/**
+ * Removes all listeners from an object. You can also optionally
+ * remove listeners of a particular type.
+ *
+ * @param {Object|undefined} obj Object to remove listeners from. Must be an
+ * EventTarget or a goog.events.Listenable.
+ * @param {string|!goog.events.EventId=} opt_type Type of event to remove.
+ * Default is all types.
+ * @return {number} Number of listeners removed.
+ */
+goog.events.removeAll = function(obj, opt_type) {
+ // TODO(chrishenry): Change the type of obj to
+ // (!EventTarget|!goog.events.Listenable).
+
+ if (!obj) {
+ return 0;
+ }
+
+ if (goog.events.Listenable.isImplementedBy(obj)) {
+ return /** @type {?} */ (obj).removeAllListeners(opt_type);
+ }
+
+ var listenerMap = goog.events.getListenerMap_(
+ /** @type {!EventTarget} */ (obj));
+ if (!listenerMap) {
+ return 0;
+ }
+
+ var count = 0;
+ var typeStr = opt_type && opt_type.toString();
+ for (var type in listenerMap.listeners) {
+ if (!typeStr || type == typeStr) {
+ // Clone so that we don't need to worry about unlistenByKey
+ // changing the content of the ListenerMap.
+ var listeners = listenerMap.listeners[type].concat();
+ for (var i = 0; i < listeners.length; ++i) {
+ if (goog.events.unlistenByKey(listeners[i])) {
+ ++count;
+ }
+ }
+ }
+ }
+ return count;
+};
+
+
+/**
+ * Gets the listeners for a given object, type and capture phase.
+ *
+ * @param {Object} obj Object to get listeners for.
+ * @param {string|!goog.events.EventId} type Event type.
+ * @param {boolean} capture Capture phase?.
+ * @return {Array<!goog.events.Listener>} Array of listener objects.
+ */
+goog.events.getListeners = function(obj, type, capture) {
+ if (goog.events.Listenable.isImplementedBy(obj)) {
+ return /** @type {!goog.events.Listenable} */ (obj).getListeners(
+ type, capture);
+ } else {
+ if (!obj) {
+ // TODO(chrishenry): We should tighten the API to accept
+ // !EventTarget|goog.events.Listenable, and add an assertion here.
+ return [];
+ }
+
+ var listenerMap = goog.events.getListenerMap_(
+ /** @type {!EventTarget} */ (obj));
+ return listenerMap ? listenerMap.getListeners(type, capture) : [];
+ }
+};
+
+
+/**
+ * Gets the goog.events.Listener for the event or null if no such listener is
+ * in use.
+ *
+ * @param {EventTarget|goog.events.Listenable} src The target from
+ * which to get listeners.
+ * @param {?string|!goog.events.EventId<EVENTOBJ>} type The type of the event.
+ * @param {function(EVENTOBJ):?|{handleEvent:function(?):?}|null} listener The
+ * listener function to get.
+ * @param {boolean=} opt_capt In DOM-compliant browsers, this determines
+ * whether the listener is fired during the
+ * capture or bubble phase of the event.
+ * @param {Object=} opt_handler Element in whose scope to call the listener.
+ * @return {goog.events.ListenableKey} the found listener or null if not found.
+ * @template EVENTOBJ
+ */
+goog.events.getListener = function(src, type, listener, opt_capt, opt_handler) {
+ // TODO(chrishenry): Change type from ?string to string, or add assertion.
+ type = /** @type {string} */ (type);
+ listener = goog.events.wrapListener(listener);
+ var capture = !!opt_capt;
+ if (goog.events.Listenable.isImplementedBy(src)) {
+ return src.getListener(type, listener, capture, opt_handler);
+ }
+
+ if (!src) {
+ // TODO(chrishenry): We should tighten the API to only accept
+ // non-null objects, or add an assertion here.
+ return null;
+ }
+
+ var listenerMap = goog.events.getListenerMap_(
+ /** @type {!EventTarget} */ (src));
+ if (listenerMap) {
+ return listenerMap.getListener(type, listener, capture, opt_handler);
+ }
+ return null;
+};
+
+
+/**
+ * Returns whether an event target has any active listeners matching the
+ * specified signature. If either the type or capture parameters are
+ * unspecified, the function will match on the remaining criteria.
+ *
+ * @param {EventTarget|goog.events.Listenable} obj Target to get
+ * listeners for.
+ * @param {string|!goog.events.EventId=} opt_type Event type.
+ * @param {boolean=} opt_capture Whether to check for capture or bubble-phase
+ * listeners.
+ * @return {boolean} Whether an event target has one or more listeners matching
+ * the requested type and/or capture phase.
+ */
+goog.events.hasListener = function(obj, opt_type, opt_capture) {
+ if (goog.events.Listenable.isImplementedBy(obj)) {
+ return obj.hasListener(opt_type, opt_capture);
+ }
+
+ var listenerMap = goog.events.getListenerMap_(
+ /** @type {!EventTarget} */ (obj));
+ return !!listenerMap && listenerMap.hasListener(opt_type, opt_capture);
+};
+
+
+/**
+ * Provides a nice string showing the normalized event objects public members
+ * @param {Object} e Event Object.
+ * @return {string} String of the public members of the normalized event object.
+ */
+goog.events.expose = function(e) {
+ var str = [];
+ for (var key in e) {
+ if (e[key] && e[key].id) {
+ str.push(key + ' = ' + e[key] + ' (' + e[key].id + ')');
+ } else {
+ str.push(key + ' = ' + e[key]);
+ }
+ }
+ return str.join('\n');
+};
+
+
+/**
+ * Returns a string with on prepended to the specified type. This is used for IE
+ * which expects "on" to be prepended. This function caches the string in order
+ * to avoid extra allocations in steady state.
+ * @param {string} type Event type.
+ * @return {string} The type string with 'on' prepended.
+ * @private
+ */
+goog.events.getOnString_ = function(type) {
+ if (type in goog.events.onStringMap_) {
+ return goog.events.onStringMap_[type];
+ }
+ return goog.events.onStringMap_[type] = goog.events.onString_ + type;
+};
+
+
+/**
+ * Fires an object's listeners of a particular type and phase
+ *
+ * @param {Object} obj Object whose listeners to call.
+ * @param {string|!goog.events.EventId} type Event type.
+ * @param {boolean} capture Which event phase.
+ * @param {Object} eventObject Event object to be passed to listener.
+ * @return {boolean} True if all listeners returned true else false.
+ */
+goog.events.fireListeners = function(obj, type, capture, eventObject) {
+ if (goog.events.Listenable.isImplementedBy(obj)) {
+ return /** @type {!goog.events.Listenable} */ (obj).fireListeners(
+ type, capture, eventObject);
+ }
+
+ return goog.events.fireListeners_(obj, type, capture, eventObject);
+};
+
+
+/**
+ * Fires an object's listeners of a particular type and phase.
+ * @param {Object} obj Object whose listeners to call.
+ * @param {string|!goog.events.EventId} type Event type.
+ * @param {boolean} capture Which event phase.
+ * @param {Object} eventObject Event object to be passed to listener.
+ * @return {boolean} True if all listeners returned true else false.
+ * @private
+ */
+goog.events.fireListeners_ = function(obj, type, capture, eventObject) {
+ /** @type {boolean} */
+ var retval = true;
+
+ var listenerMap = goog.events.getListenerMap_(
+ /** @type {EventTarget} */ (obj));
+ if (listenerMap) {
+ // TODO(chrishenry): Original code avoids array creation when there
+ // is no listener, so we do the same. If this optimization turns
+ // out to be not required, we can replace this with
+ // listenerMap.getListeners(type, capture) instead, which is simpler.
+ var listenerArray = listenerMap.listeners[type.toString()];
+ if (listenerArray) {
+ listenerArray = listenerArray.concat();
+ for (var i = 0; i < listenerArray.length; i++) {
+ var listener = listenerArray[i];
+ // We might not have a listener if the listener was removed.
+ if (listener && listener.capture == capture && !listener.removed) {
+ var result = goog.events.fireListener(listener, eventObject);
+ retval = retval && (result !== false);
+ }
+ }
+ }
+ }
+ return retval;
+};
+
+
+/**
+ * Fires a listener with a set of arguments
+ *
+ * @param {goog.events.Listener} listener The listener object to call.
+ * @param {Object} eventObject The event object to pass to the listener.
+ * @return {*} Result of listener.
+ */
+goog.events.fireListener = function(listener, eventObject) {
+ var listenerFn = listener.listener;
+ var listenerHandler = listener.handler || listener.src;
+
+ if (listener.callOnce) {
+ goog.events.unlistenByKey(listener);
+ }
+ return listenerFn.call(listenerHandler, eventObject);
+};
+
+
+/**
+ * Gets the total number of listeners currently in the system.
+ * @return {number} Number of listeners.
+ * @deprecated This returns estimated count, now that Closure no longer
+ * stores a central listener registry. We still return an estimation
+ * to keep existing listener-related tests passing. In the near future,
+ * this function will be removed.
+ */
+goog.events.getTotalListenerCount = function() {
+ return goog.events.listenerCountEstimate_;
+};
+
+
+/**
+ * Dispatches an event (or event like object) and calls all listeners
+ * listening for events of this type. The type of the event is decided by the
+ * type property on the event object.
+ *
+ * If any of the listeners returns false OR calls preventDefault then this
+ * function will return false. If one of the capture listeners calls
+ * stopPropagation, then the bubble listeners won't fire.
+ *
+ * @param {goog.events.Listenable} src The event target.
+ * @param {goog.events.EventLike} e Event object.
+ * @return {boolean} If anyone called preventDefault on the event object (or
+ * if any of the handlers returns false) this will also return false.
+ * If there are no handlers, or if all handlers return true, this returns
+ * true.
+ */
+goog.events.dispatchEvent = function(src, e) {
+ goog.asserts.assert(
+ goog.events.Listenable.isImplementedBy(src),
+ 'Can not use goog.events.dispatchEvent with ' +
+ 'non-goog.events.Listenable instance.');
+ return src.dispatchEvent(e);
+};
+
+
+/**
+ * Installs exception protection for the browser event entry point using the
+ * given error handler.
+ *
+ * @param {goog.debug.ErrorHandler} errorHandler Error handler with which to
+ * protect the entry point.
+ */
+goog.events.protectBrowserEventEntryPoint = function(errorHandler) {
+ goog.events.handleBrowserEvent_ =
+ errorHandler.protectEntryPoint(goog.events.handleBrowserEvent_);
+};
+
+
+/**
+ * Handles an event and dispatches it to the correct listeners. This
+ * function is a proxy for the real listener the user specified.
+ *
+ * @param {goog.events.Listener} listener The listener object.
+ * @param {Event=} opt_evt Optional event object that gets passed in via the
+ * native event handlers.
+ * @return {*} Result of the event handler.
+ * @this {EventTarget} The object or Element that fired the event.
+ * @private
+ */
+goog.events.handleBrowserEvent_ = function(listener, opt_evt) {
+ if (listener.removed) {
+ return true;
+ }
+
+ // Synthesize event propagation if the browser does not support W3C
+ // event model.
+ if (!goog.events.BrowserFeature.HAS_W3C_EVENT_SUPPORT) {
+ var ieEvent = opt_evt ||
+ /** @type {Event} */ (goog.getObjectByName('window.event'));
+ var evt = new goog.events.BrowserEvent(ieEvent, this);
+ /** @type {*} */
+ var retval = true;
+
+ if (goog.events.CAPTURE_SIMULATION_MODE ==
+ goog.events.CaptureSimulationMode.ON) {
+ // If we have not marked this event yet, we should perform capture
+ // simulation.
+ if (!goog.events.isMarkedIeEvent_(ieEvent)) {
+ goog.events.markIeEvent_(ieEvent);
+
+ var ancestors = [];
+ for (var parent = evt.currentTarget; parent;
+ parent = parent.parentNode) {
+ ancestors.push(parent);
+ }
+
+ // Fire capture listeners.
+ var type = listener.type;
+ for (var i = ancestors.length - 1; !evt.propagationStopped_ && i >= 0;
+ i--) {
+ evt.currentTarget = ancestors[i];
+ var result =
+ goog.events.fireListeners_(ancestors[i], type, true, evt);
+ retval = retval && result;
+ }
+
+ // Fire bubble listeners.
+ //
+ // We can technically rely on IE to perform bubble event
+ // propagation. However, it turns out that IE fires events in
+ // opposite order of attachEvent registration, which broke
+ // some code and tests that rely on the order. (While W3C DOM
+ // Level 2 Events TR leaves the event ordering unspecified,
+ // modern browsers and W3C DOM Level 3 Events Working Draft
+ // actually specify the order as the registration order.)
+ for (var i = 0; !evt.propagationStopped_ && i < ancestors.length; i++) {
+ evt.currentTarget = ancestors[i];
+ var result =
+ goog.events.fireListeners_(ancestors[i], type, false, evt);
+ retval = retval && result;
+ }
+ }
+ } else {
+ retval = goog.events.fireListener(listener, evt);
+ }
+ return retval;
+ }
+
+ // Otherwise, simply fire the listener.
+ return goog.events.fireListener(
+ listener, new goog.events.BrowserEvent(opt_evt, this));
+};
+
+
+/**
+ * This is used to mark the IE event object so we do not do the Closure pass
+ * twice for a bubbling event.
+ * @param {Event} e The IE browser event.
+ * @private
+ */
+goog.events.markIeEvent_ = function(e) {
+ // Only the keyCode and the returnValue can be changed. We use keyCode for
+ // non keyboard events.
+ // event.returnValue is a bit more tricky. It is undefined by default. A
+ // boolean false prevents the default action. In a window.onbeforeunload and
+ // the returnValue is non undefined it will be alerted. However, we will only
+ // modify the returnValue for keyboard events. We can get a problem if non
+ // closure events sets the keyCode or the returnValue
+
+ var useReturnValue = false;
+
+ if (e.keyCode == 0) {
+ // We cannot change the keyCode in case that srcElement is input[type=file].
+ // We could test that that is the case but that would allocate 3 objects.
+ // If we use try/catch we will only allocate extra objects in the case of a
+ // failure.
+
+ try {
+ e.keyCode = -1;
+ return;
+ } catch (ex) {
+ useReturnValue = true;
+ }
+ }
+
+ if (useReturnValue ||
+ /** @type {boolean|undefined} */ (e.returnValue) == undefined) {
+ e.returnValue = true;
+ }
+};
+
+
+/**
+ * This is used to check if an IE event has already been handled by the Closure
+ * system so we do not do the Closure pass twice for a bubbling event.
+ * @param {Event} e The IE browser event.
+ * @return {boolean} True if the event object has been marked.
+ * @private
+ */
+goog.events.isMarkedIeEvent_ = function(e) {
+ return e.keyCode < 0 || e.returnValue != undefined;
+};
+
+
+/**
+ * Counter to create unique event ids.
+ * @private {number}
+ */
+goog.events.uniqueIdCounter_ = 0;
+
+
+/**
+ * Creates a unique event id.
+ *
+ * @param {string} identifier The identifier.
+ * @return {string} A unique identifier.
+ * @idGenerator {unique}
+ */
+goog.events.getUniqueId = function(identifier) {
+ return identifier + '_' + goog.events.uniqueIdCounter_++;
+};
+
+
+/**
+ * @param {EventTarget} src The source object.
+ * @return {goog.events.ListenerMap} A listener map for the given
+ * source object, or null if none exists.
+ * @private
+ */
+goog.events.getListenerMap_ = function(src) {
+ var listenerMap = src[goog.events.LISTENER_MAP_PROP_];
+ // IE serializes the property as well (e.g. when serializing outer
+ // HTML). So we must check that the value is of the correct type.
+ return listenerMap instanceof goog.events.ListenerMap ? listenerMap : null;
+};
+
+
+/**
+ * Expando property for listener function wrapper for Object with
+ * handleEvent.
+ * @private @const {string}
+ */
+goog.events.LISTENER_WRAPPER_PROP_ =
+ '__closure_events_fn_' + ((Math.random() * 1e9) >>> 0);
+
+
+/**
+ * @param {Object|Function} listener The listener function or an
+ * object that contains handleEvent method.
+ * @return {!Function} Either the original function or a function that
+ * calls obj.handleEvent. If the same listener is passed to this
+ * function more than once, the same function is guaranteed to be
+ * returned.
+ */
+goog.events.wrapListener = function(listener) {
+ goog.asserts.assert(listener, 'Listener can not be null.');
+
+ if (goog.isFunction(listener)) {
+ return listener;
+ }
+
+ goog.asserts.assert(
+ listener.handleEvent, 'An object listener must have handleEvent method.');
+ if (!listener[goog.events.LISTENER_WRAPPER_PROP_]) {
+ listener[goog.events.LISTENER_WRAPPER_PROP_] = function(e) {
+ return /** @type {?} */ (listener).handleEvent(e);
+ };
+ }
+ return listener[goog.events.LISTENER_WRAPPER_PROP_];
+};
+
+
+// Register the browser event handler as an entry point, so that
+// it can be monitored for exception handling, etc.
+goog.debug.entryPointRegistry.register(
+ /**
+ * @param {function(!Function): !Function} transformer The transforming
+ * function.
+ */
+ function(transformer) {
+ goog.events.handleBrowserEvent_ =
+ transformer(goog.events.handleBrowserEvent_);
+ });
diff --git a/chromium/third_party/ink/closure/events/eventtarget.js b/chromium/third_party/ink/closure/events/eventtarget.js
new file mode 100644
index 00000000000..b8adcaee827
--- /dev/null
+++ b/chromium/third_party/ink/closure/events/eventtarget.js
@@ -0,0 +1,395 @@
+// Copyright 2005 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview A disposable implementation of a custom
+ * listenable/event target. See also: documentation for
+ * {@code goog.events.Listenable}.
+ *
+ * @author arv@google.com (Erik Arvidsson) [Original implementation]
+ * @author pupius@google.com (Daniel Pupius) [Port to use goog.events]
+ * @see ../demos/eventtarget.html
+ * @see goog.events.Listenable
+ */
+
+goog.provide('goog.events.EventTarget');
+
+goog.require('goog.Disposable');
+goog.require('goog.asserts');
+goog.require('goog.events');
+goog.require('goog.events.Event');
+goog.require('goog.events.Listenable');
+goog.require('goog.events.ListenerMap');
+goog.require('goog.object');
+
+
+
+/**
+ * An implementation of {@code goog.events.Listenable} with full W3C
+ * EventTarget-like support (capture/bubble mechanism, stopping event
+ * propagation, preventing default actions).
+ *
+ * You may subclass this class to turn your class into a Listenable.
+ *
+ * Unless propagation is stopped, an event dispatched by an
+ * EventTarget will bubble to the parent returned by
+ * {@code getParentEventTarget}. To set the parent, call
+ * {@code setParentEventTarget}. Subclasses that don't support
+ * changing the parent can override the setter to throw an error.
+ *
+ * Example usage:
+ * <pre>
+ * var source = new goog.events.EventTarget();
+ * function handleEvent(e) {
+ * alert('Type: ' + e.type + '; Target: ' + e.target);
+ * }
+ * source.listen('foo', handleEvent);
+ * // Or: goog.events.listen(source, 'foo', handleEvent);
+ * ...
+ * source.dispatchEvent('foo'); // will call handleEvent
+ * ...
+ * source.unlisten('foo', handleEvent);
+ * // Or: goog.events.unlisten(source, 'foo', handleEvent);
+ * </pre>
+ *
+ * @constructor
+ * @extends {goog.Disposable}
+ * @implements {goog.events.Listenable}
+ */
+goog.events.EventTarget = function() {
+ goog.Disposable.call(this);
+
+ /**
+ * Maps of event type to an array of listeners.
+ * @private {!goog.events.ListenerMap}
+ */
+ this.eventTargetListeners_ = new goog.events.ListenerMap(this);
+
+ /**
+ * The object to use for event.target. Useful when mixing in an
+ * EventTarget to another object.
+ * @private {!Object}
+ */
+ this.actualEventTarget_ = this;
+
+ /**
+ * Parent event target, used during event bubbling.
+ *
+ * TODO(chrishenry): Change this to goog.events.Listenable. This
+ * currently breaks people who expect getParentEventTarget to return
+ * goog.events.EventTarget.
+ *
+ * @private {goog.events.EventTarget}
+ */
+ this.parentEventTarget_ = null;
+};
+goog.inherits(goog.events.EventTarget, goog.Disposable);
+goog.events.Listenable.addImplementation(goog.events.EventTarget);
+
+
+/**
+ * An artificial cap on the number of ancestors you can have. This is mainly
+ * for loop detection.
+ * @const {number}
+ * @private
+ */
+goog.events.EventTarget.MAX_ANCESTORS_ = 1000;
+
+
+/**
+ * Returns the parent of this event target to use for bubbling.
+ *
+ * @return {goog.events.EventTarget} The parent EventTarget or null if
+ * there is no parent.
+ * @override
+ */
+goog.events.EventTarget.prototype.getParentEventTarget = function() {
+ return this.parentEventTarget_;
+};
+
+
+/**
+ * Sets the parent of this event target to use for capture/bubble
+ * mechanism.
+ * @param {goog.events.EventTarget} parent Parent listenable (null if none).
+ */
+goog.events.EventTarget.prototype.setParentEventTarget = function(parent) {
+ this.parentEventTarget_ = parent;
+};
+
+
+/**
+ * Adds an event listener to the event target. The same handler can only be
+ * added once per the type. Even if you add the same handler multiple times
+ * using the same type then it will only be called once when the event is
+ * dispatched.
+ *
+ * @param {string|!goog.events.EventId} type The type of the event to listen for
+ * @param {function(?):?|{handleEvent:function(?):?}|null} handler The function
+ * to handle the event. The handler can also be an object that implements
+ * the handleEvent method which takes the event object as argument.
+ * @param {boolean=} opt_capture In DOM-compliant browsers, this determines
+ * whether the listener is fired during the capture or bubble phase
+ * of the event.
+ * @param {Object=} opt_handlerScope Object in whose scope to call
+ * the listener.
+ * @deprecated Use {@code #listen} instead, when possible. Otherwise, use
+ * {@code goog.events.listen} if you are passing Object
+ * (instead of Function) as handler.
+ */
+goog.events.EventTarget.prototype.addEventListener = function(
+ type, handler, opt_capture, opt_handlerScope) {
+ goog.events.listen(this, type, handler, opt_capture, opt_handlerScope);
+};
+
+
+/**
+ * Removes an event listener from the event target. The handler must be the
+ * same object as the one added. If the handler has not been added then
+ * nothing is done.
+ *
+ * @param {string} type The type of the event to listen for.
+ * @param {function(?):?|{handleEvent:function(?):?}|null} handler The function
+ * to handle the event. The handler can also be an object that implements
+ * the handleEvent method which takes the event object as argument.
+ * @param {boolean=} opt_capture In DOM-compliant browsers, this determines
+ * whether the listener is fired during the capture or bubble phase
+ * of the event.
+ * @param {Object=} opt_handlerScope Object in whose scope to call
+ * the listener.
+ * @deprecated Use {@code #unlisten} instead, when possible. Otherwise, use
+ * {@code goog.events.unlisten} if you are passing Object
+ * (instead of Function) as handler.
+ */
+goog.events.EventTarget.prototype.removeEventListener = function(
+ type, handler, opt_capture, opt_handlerScope) {
+ goog.events.unlisten(this, type, handler, opt_capture, opt_handlerScope);
+};
+
+
+/** @override */
+goog.events.EventTarget.prototype.dispatchEvent = function(e) {
+ this.assertInitialized_();
+
+ var ancestorsTree, ancestor = this.getParentEventTarget();
+ if (ancestor) {
+ ancestorsTree = [];
+ var ancestorCount = 1;
+ for (; ancestor; ancestor = ancestor.getParentEventTarget()) {
+ ancestorsTree.push(ancestor);
+ goog.asserts.assert(
+ (++ancestorCount < goog.events.EventTarget.MAX_ANCESTORS_),
+ 'infinite loop');
+ }
+ }
+
+ return goog.events.EventTarget.dispatchEventInternal_(
+ this.actualEventTarget_, e, ancestorsTree);
+};
+
+
+/**
+ * Removes listeners from this object. Classes that extend EventTarget may
+ * need to override this method in order to remove references to DOM Elements
+ * and additional listeners.
+ * @override
+ */
+goog.events.EventTarget.prototype.disposeInternal = function() {
+ goog.events.EventTarget.superClass_.disposeInternal.call(this);
+
+ this.removeAllListeners();
+ this.parentEventTarget_ = null;
+};
+
+
+/** @override */
+goog.events.EventTarget.prototype.listen = function(
+ type, listener, opt_useCapture, opt_listenerScope) {
+ this.assertInitialized_();
+ return this.eventTargetListeners_.add(
+ String(type), listener, false /* callOnce */, opt_useCapture,
+ opt_listenerScope);
+};
+
+
+/** @override */
+goog.events.EventTarget.prototype.listenOnce = function(
+ type, listener, opt_useCapture, opt_listenerScope) {
+ return this.eventTargetListeners_.add(
+ String(type), listener, true /* callOnce */, opt_useCapture,
+ opt_listenerScope);
+};
+
+
+/** @override */
+goog.events.EventTarget.prototype.unlisten = function(
+ type, listener, opt_useCapture, opt_listenerScope) {
+ return this.eventTargetListeners_.remove(
+ String(type), listener, opt_useCapture, opt_listenerScope);
+};
+
+
+/** @override */
+goog.events.EventTarget.prototype.unlistenByKey = function(key) {
+ return this.eventTargetListeners_.removeByKey(key);
+};
+
+
+/** @override */
+goog.events.EventTarget.prototype.removeAllListeners = function(opt_type) {
+ // TODO(chrishenry): Previously, removeAllListeners can be called on
+ // uninitialized EventTarget, so we preserve that behavior. We
+ // should remove this when usages that rely on that fact are purged.
+ if (!this.eventTargetListeners_) {
+ return 0;
+ }
+ return this.eventTargetListeners_.removeAll(opt_type);
+};
+
+
+/** @override */
+goog.events.EventTarget.prototype.fireListeners = function(
+ type, capture, eventObject) {
+ // TODO(chrishenry): Original code avoids array creation when there
+ // is no listener, so we do the same. If this optimization turns
+ // out to be not required, we can replace this with
+ // getListeners(type, capture) instead, which is simpler.
+ var listenerArray = this.eventTargetListeners_.listeners[String(type)];
+ if (!listenerArray) {
+ return true;
+ }
+ listenerArray = listenerArray.concat();
+
+ var rv = true;
+ for (var i = 0; i < listenerArray.length; ++i) {
+ var listener = listenerArray[i];
+ // We might not have a listener if the listener was removed.
+ if (listener && !listener.removed && listener.capture == capture) {
+ var listenerFn = listener.listener;
+ var listenerHandler = listener.handler || listener.src;
+
+ if (listener.callOnce) {
+ this.unlistenByKey(listener);
+ }
+ rv = listenerFn.call(listenerHandler, eventObject) !== false && rv;
+ }
+ }
+
+ return rv && eventObject.returnValue_ != false;
+};
+
+
+/** @override */
+goog.events.EventTarget.prototype.getListeners = function(type, capture) {
+ return this.eventTargetListeners_.getListeners(String(type), capture);
+};
+
+
+/** @override */
+goog.events.EventTarget.prototype.getListener = function(
+ type, listener, capture, opt_listenerScope) {
+ return this.eventTargetListeners_.getListener(
+ String(type), listener, capture, opt_listenerScope);
+};
+
+
+/** @override */
+goog.events.EventTarget.prototype.hasListener = function(
+ opt_type, opt_capture) {
+ var id = goog.isDef(opt_type) ? String(opt_type) : undefined;
+ return this.eventTargetListeners_.hasListener(id, opt_capture);
+};
+
+
+/**
+ * Sets the target to be used for {@code event.target} when firing
+ * event. Mainly used for testing. For example, see
+ * {@code goog.testing.events.mixinListenable}.
+ * @param {!Object} target The target.
+ */
+goog.events.EventTarget.prototype.setTargetForTesting = function(target) {
+ this.actualEventTarget_ = target;
+};
+
+
+/**
+ * Asserts that the event target instance is initialized properly.
+ * @private
+ */
+goog.events.EventTarget.prototype.assertInitialized_ = function() {
+ goog.asserts.assert(
+ this.eventTargetListeners_,
+ 'Event target is not initialized. Did you call the superclass ' +
+ '(goog.events.EventTarget) constructor?');
+};
+
+
+/**
+ * Dispatches the given event on the ancestorsTree.
+ *
+ * @param {!Object} target The target to dispatch on.
+ * @param {goog.events.Event|Object|string} e The event object.
+ * @param {Array<goog.events.Listenable>=} opt_ancestorsTree The ancestors
+ * tree of the target, in reverse order from the closest ancestor
+ * to the root event target. May be null if the target has no ancestor.
+ * @return {boolean} If anyone called preventDefault on the event object (or
+ * if any of the listeners returns false) this will also return false.
+ * @private
+ */
+goog.events.EventTarget.dispatchEventInternal_ = function(
+ target, e, opt_ancestorsTree) {
+ var type = e.type || /** @type {string} */ (e);
+
+ // If accepting a string or object, create a custom event object so that
+ // preventDefault and stopPropagation work with the event.
+ if (goog.isString(e)) {
+ e = new goog.events.Event(e, target);
+ } else if (!(e instanceof goog.events.Event)) {
+ var oldEvent = e;
+ e = new goog.events.Event(type, target);
+ goog.object.extend(e, oldEvent);
+ } else {
+ e.target = e.target || target;
+ }
+
+ var rv = true, currentTarget;
+
+ // Executes all capture listeners on the ancestors, if any.
+ if (opt_ancestorsTree) {
+ for (var i = opt_ancestorsTree.length - 1; !e.propagationStopped_ && i >= 0;
+ i--) {
+ currentTarget = e.currentTarget = opt_ancestorsTree[i];
+ rv = currentTarget.fireListeners(type, true, e) && rv;
+ }
+ }
+
+ // Executes capture and bubble listeners on the target.
+ if (!e.propagationStopped_) {
+ currentTarget = /** @type {?} */ (e.currentTarget = target);
+ rv = currentTarget.fireListeners(type, true, e) && rv;
+ if (!e.propagationStopped_) {
+ rv = currentTarget.fireListeners(type, false, e) && rv;
+ }
+ }
+
+ // Executes all bubble listeners on the ancestors, if any.
+ if (opt_ancestorsTree) {
+ for (i = 0; !e.propagationStopped_ && i < opt_ancestorsTree.length; i++) {
+ currentTarget = e.currentTarget = opt_ancestorsTree[i];
+ rv = currentTarget.fireListeners(type, false, e) && rv;
+ }
+ }
+
+ return rv;
+};
diff --git a/chromium/third_party/ink/closure/events/eventtype.js b/chromium/third_party/ink/closure/events/eventtype.js
new file mode 100644
index 00000000000..801d7f9dd21
--- /dev/null
+++ b/chromium/third_party/ink/closure/events/eventtype.js
@@ -0,0 +1,360 @@
+// Copyright 2010 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Event Types.
+ *
+ * @author arv@google.com (Erik Arvidsson)
+ * @author mirkov@google.com (Mirko Visontai)
+ */
+
+
+goog.provide('goog.events.EventType');
+goog.provide('goog.events.PointerFallbackEventType');
+
+goog.require('goog.events.BrowserFeature');
+goog.require('goog.userAgent');
+
+
+/**
+ * Returns a prefixed event name for the current browser.
+ * @param {string} eventName The name of the event.
+ * @return {string} The prefixed event name.
+ * @suppress {missingRequire|missingProvide}
+ * @private
+ */
+goog.events.getVendorPrefixedName_ = function(eventName) {
+ return goog.userAgent.WEBKIT ?
+ 'webkit' + eventName :
+ (goog.userAgent.OPERA ? 'o' + eventName.toLowerCase() :
+ eventName.toLowerCase());
+};
+
+
+/**
+ * Constants for event names.
+ * @enum {string}
+ */
+goog.events.EventType = {
+ // Mouse events
+ CLICK: 'click',
+ RIGHTCLICK: 'rightclick',
+ DBLCLICK: 'dblclick',
+ MOUSEDOWN: 'mousedown',
+ MOUSEUP: 'mouseup',
+ MOUSEOVER: 'mouseover',
+ MOUSEOUT: 'mouseout',
+ MOUSEMOVE: 'mousemove',
+ MOUSEENTER: 'mouseenter',
+ MOUSELEAVE: 'mouseleave',
+
+ // Selection events.
+ // https://www.w3.org/TR/selection-api/
+ SELECTIONCHANGE: 'selectionchange',
+ SELECTSTART: 'selectstart', // IE, Safari, Chrome
+
+ // Wheel events
+ // http://www.w3.org/TR/DOM-Level-3-Events/#events-wheelevents
+ WHEEL: 'wheel',
+
+ // Key events
+ KEYPRESS: 'keypress',
+ KEYDOWN: 'keydown',
+ KEYUP: 'keyup',
+
+ // Focus
+ BLUR: 'blur',
+ FOCUS: 'focus',
+ DEACTIVATE: 'deactivate', // IE only
+ // NOTE: The following two events are not stable in cross-browser usage.
+ // WebKit and Opera implement DOMFocusIn/Out.
+ // IE implements focusin/out.
+ // Gecko implements neither see bug at
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=396927.
+ // The DOM Events Level 3 Draft deprecates DOMFocusIn in favor of focusin:
+ // http://dev.w3.org/2006/webapi/DOM-Level-3-Events/html/DOM3-Events.html
+ // You can use FOCUS in Capture phase until implementations converge.
+ FOCUSIN: goog.userAgent.IE ? 'focusin' : 'DOMFocusIn',
+ FOCUSOUT: goog.userAgent.IE ? 'focusout' : 'DOMFocusOut',
+
+ // Forms
+ CHANGE: 'change',
+ RESET: 'reset',
+ SELECT: 'select',
+ SUBMIT: 'submit',
+ INPUT: 'input',
+ PROPERTYCHANGE: 'propertychange', // IE only
+
+ // Drag and drop
+ DRAGSTART: 'dragstart',
+ DRAG: 'drag',
+ DRAGENTER: 'dragenter',
+ DRAGOVER: 'dragover',
+ DRAGLEAVE: 'dragleave',
+ DROP: 'drop',
+ DRAGEND: 'dragend',
+
+ // Touch events
+ // Note that other touch events exist, but we should follow the W3C list here.
+ // http://www.w3.org/TR/touch-events/#list-of-touchevent-types
+ TOUCHSTART: 'touchstart',
+ TOUCHMOVE: 'touchmove',
+ TOUCHEND: 'touchend',
+ TOUCHCANCEL: 'touchcancel',
+
+ // Misc
+ BEFOREUNLOAD: 'beforeunload',
+ CONSOLEMESSAGE: 'consolemessage',
+ CONTEXTMENU: 'contextmenu',
+ DEVICEMOTION: 'devicemotion',
+ DEVICEORIENTATION: 'deviceorientation',
+ DOMCONTENTLOADED: 'DOMContentLoaded',
+ ERROR: 'error',
+ HELP: 'help',
+ LOAD: 'load',
+ LOSECAPTURE: 'losecapture',
+ ORIENTATIONCHANGE: 'orientationchange',
+ READYSTATECHANGE: 'readystatechange',
+ RESIZE: 'resize',
+ SCROLL: 'scroll',
+ UNLOAD: 'unload',
+
+ // Media events
+ CANPLAY: 'canplay',
+ CANPLAYTHROUGH: 'canplaythrough',
+ DURATIONCHANGE: 'durationchange',
+ EMPTIED: 'emptied',
+ ENDED: 'ended',
+ LOADEDDATA: 'loadeddata',
+ LOADEDMETADATA: 'loadedmetadata',
+ PAUSE: 'pause',
+ PLAY: 'play',
+ PLAYING: 'playing',
+ RATECHANGE: 'ratechange',
+ SEEKED: 'seeked',
+ SEEKING: 'seeking',
+ STALLED: 'stalled',
+ SUSPEND: 'suspend',
+ TIMEUPDATE: 'timeupdate',
+ VOLUMECHANGE: 'volumechange',
+ WAITING: 'waiting',
+
+ // Media Source Extensions events
+ // https://www.w3.org/TR/media-source/#mediasource-events
+ SOURCEOPEN: 'sourceopen',
+ SOURCEENDED: 'sourceended',
+ SOURCECLOSED: 'sourceclosed',
+ // https://www.w3.org/TR/media-source/#sourcebuffer-events
+ ABORT: 'abort',
+ UPDATE: 'update',
+ UPDATESTART: 'updatestart',
+ UPDATEEND: 'updateend',
+
+ // HTML 5 History events
+ // See http://www.w3.org/TR/html5/browsers.html#event-definitions-0
+ HASHCHANGE: 'hashchange',
+ PAGEHIDE: 'pagehide',
+ PAGESHOW: 'pageshow',
+ POPSTATE: 'popstate',
+
+ // Copy and Paste
+ // Support is limited. Make sure it works on your favorite browser
+ // before using.
+ // http://www.quirksmode.org/dom/events/cutcopypaste.html
+ COPY: 'copy',
+ PASTE: 'paste',
+ CUT: 'cut',
+ BEFORECOPY: 'beforecopy',
+ BEFORECUT: 'beforecut',
+ BEFOREPASTE: 'beforepaste',
+
+ // HTML5 online/offline events.
+ // http://www.w3.org/TR/offline-webapps/#related
+ ONLINE: 'online',
+ OFFLINE: 'offline',
+
+ // HTML 5 worker events
+ MESSAGE: 'message',
+ CONNECT: 'connect',
+
+ // Service Worker Events - ServiceWorkerGlobalScope context
+ // See https://w3c.github.io/ServiceWorker/#execution-context-events
+ // Note: message event defined in worker events section
+ INSTALL: 'install',
+ ACTIVATE: 'activate',
+ FETCH: 'fetch',
+ FOREIGNFETCH: 'foreignfetch',
+ MESSAGEERROR: 'messageerror',
+
+ // Service Worker Events - Document context
+ // See https://w3c.github.io/ServiceWorker/#document-context-events
+ STATECHANGE: 'statechange',
+ UPDATEFOUND: 'updatefound',
+ CONTROLLERCHANGE: 'controllerchange',
+
+ // CSS animation events.
+ /** @suppress {missingRequire} */
+ ANIMATIONSTART: goog.events.getVendorPrefixedName_('AnimationStart'),
+ /** @suppress {missingRequire} */
+ ANIMATIONEND: goog.events.getVendorPrefixedName_('AnimationEnd'),
+ /** @suppress {missingRequire} */
+ ANIMATIONITERATION: goog.events.getVendorPrefixedName_('AnimationIteration'),
+
+ // CSS transition events. Based on the browser support described at:
+ // https://developer.mozilla.org/en/css/css_transitions#Browser_compatibility
+ /** @suppress {missingRequire} */
+ TRANSITIONEND: goog.events.getVendorPrefixedName_('TransitionEnd'),
+
+ // W3C Pointer Events
+ // http://www.w3.org/TR/pointerevents/
+ POINTERDOWN: 'pointerdown',
+ POINTERUP: 'pointerup',
+ POINTERCANCEL: 'pointercancel',
+ POINTERMOVE: 'pointermove',
+ POINTEROVER: 'pointerover',
+ POINTEROUT: 'pointerout',
+ POINTERENTER: 'pointerenter',
+ POINTERLEAVE: 'pointerleave',
+ GOTPOINTERCAPTURE: 'gotpointercapture',
+ LOSTPOINTERCAPTURE: 'lostpointercapture',
+
+ // IE specific events.
+ // See http://msdn.microsoft.com/en-us/library/ie/hh772103(v=vs.85).aspx
+ // Note: these events will be supplanted in IE11.
+ MSGESTURECHANGE: 'MSGestureChange',
+ MSGESTUREEND: 'MSGestureEnd',
+ MSGESTUREHOLD: 'MSGestureHold',
+ MSGESTURESTART: 'MSGestureStart',
+ MSGESTURETAP: 'MSGestureTap',
+ MSGOTPOINTERCAPTURE: 'MSGotPointerCapture',
+ MSINERTIASTART: 'MSInertiaStart',
+ MSLOSTPOINTERCAPTURE: 'MSLostPointerCapture',
+ MSPOINTERCANCEL: 'MSPointerCancel',
+ MSPOINTERDOWN: 'MSPointerDown',
+ MSPOINTERENTER: 'MSPointerEnter',
+ MSPOINTERHOVER: 'MSPointerHover',
+ MSPOINTERLEAVE: 'MSPointerLeave',
+ MSPOINTERMOVE: 'MSPointerMove',
+ MSPOINTEROUT: 'MSPointerOut',
+ MSPOINTEROVER: 'MSPointerOver',
+ MSPOINTERUP: 'MSPointerUp',
+
+ // Native IMEs/input tools events.
+ TEXT: 'text',
+ // The textInput event is supported in IE9+, but only in lower case. All other
+ // browsers use the camel-case event name.
+ TEXTINPUT: goog.userAgent.IE ? 'textinput' : 'textInput',
+ COMPOSITIONSTART: 'compositionstart',
+ COMPOSITIONUPDATE: 'compositionupdate',
+ COMPOSITIONEND: 'compositionend',
+
+ // The beforeinput event is initially only supported in Safari. See
+ // https://bugs.chromium.org/p/chromium/issues/detail?id=342670 for Chrome
+ // implementation tracking.
+ BEFOREINPUT: 'beforeinput',
+
+ // Webview tag events
+ // See http://developer.chrome.com/dev/apps/webview_tag.html
+ EXIT: 'exit',
+ LOADABORT: 'loadabort',
+ LOADCOMMIT: 'loadcommit',
+ LOADREDIRECT: 'loadredirect',
+ LOADSTART: 'loadstart',
+ LOADSTOP: 'loadstop',
+ RESPONSIVE: 'responsive',
+ SIZECHANGED: 'sizechanged',
+ UNRESPONSIVE: 'unresponsive',
+
+ // HTML5 Page Visibility API. See details at
+ // {@code goog.labs.dom.PageVisibilityMonitor}.
+ VISIBILITYCHANGE: 'visibilitychange',
+
+ // LocalStorage event.
+ STORAGE: 'storage',
+
+ // DOM Level 2 mutation events (deprecated).
+ DOMSUBTREEMODIFIED: 'DOMSubtreeModified',
+ DOMNODEINSERTED: 'DOMNodeInserted',
+ DOMNODEREMOVED: 'DOMNodeRemoved',
+ DOMNODEREMOVEDFROMDOCUMENT: 'DOMNodeRemovedFromDocument',
+ DOMNODEINSERTEDINTODOCUMENT: 'DOMNodeInsertedIntoDocument',
+ DOMATTRMODIFIED: 'DOMAttrModified',
+ DOMCHARACTERDATAMODIFIED: 'DOMCharacterDataModified',
+
+ // Print events.
+ BEFOREPRINT: 'beforeprint',
+ AFTERPRINT: 'afterprint'
+};
+
+
+/**
+ * Returns one of the given pointer fallback event names in order of preference:
+ * 1. pointerEventName
+ * 2. msPointerEventName
+ * 3. mouseEventName
+ * @param {string} pointerEventName
+ * @param {string} msPointerEventName
+ * @param {string} mouseEventName
+ * @return {string} The supported pointer or mouse event name.
+ * @private
+ */
+goog.events.getPointerFallbackEventName_ = function(
+ pointerEventName, msPointerEventName, mouseEventName) {
+ if (goog.events.BrowserFeature.POINTER_EVENTS) {
+ return pointerEventName;
+ }
+ if (goog.events.BrowserFeature.MSPOINTER_EVENTS) {
+ return msPointerEventName;
+ }
+ return mouseEventName;
+};
+
+
+/**
+ * Constants for pointer event names that fall back to corresponding mouse event
+ * names on unsupported platforms. These are intended to be drop-in replacements
+ * for corresponding values in {@code goog.events.EventType}.
+ * @enum {string}
+ */
+goog.events.PointerFallbackEventType = {
+ POINTERDOWN: goog.events.getPointerFallbackEventName_(
+ goog.events.EventType.POINTERDOWN, goog.events.EventType.MSPOINTERDOWN,
+ goog.events.EventType.MOUSEDOWN),
+ POINTERUP: goog.events.getPointerFallbackEventName_(
+ goog.events.EventType.POINTERUP, goog.events.EventType.MSPOINTERUP,
+ goog.events.EventType.MOUSEUP),
+ POINTERCANCEL: goog.events.getPointerFallbackEventName_(
+ goog.events.EventType.POINTERCANCEL,
+ goog.events.EventType.MSPOINTERCANCEL,
+ // When falling back to mouse events, there is no MOUSECANCEL equivalent
+ // of POINTERCANCEL. In this case POINTERUP already falls back to MOUSEUP
+ // which represents both UP and CANCEL. POINTERCANCEL does not fall back
+ // to MOUSEUP to prevent listening twice on the same event.
+ 'mousecancel'), // non-existent event; will never fire
+ POINTERMOVE: goog.events.getPointerFallbackEventName_(
+ goog.events.EventType.POINTERMOVE, goog.events.EventType.MSPOINTERMOVE,
+ goog.events.EventType.MOUSEMOVE),
+ POINTEROVER: goog.events.getPointerFallbackEventName_(
+ goog.events.EventType.POINTEROVER, goog.events.EventType.MSPOINTEROVER,
+ goog.events.EventType.MOUSEOVER),
+ POINTEROUT: goog.events.getPointerFallbackEventName_(
+ goog.events.EventType.POINTEROUT, goog.events.EventType.MSPOINTEROUT,
+ goog.events.EventType.MOUSEOUT),
+ POINTERENTER: goog.events.getPointerFallbackEventName_(
+ goog.events.EventType.POINTERENTER, goog.events.EventType.MSPOINTERENTER,
+ goog.events.EventType.MOUSEENTER),
+ POINTERLEAVE: goog.events.getPointerFallbackEventName_(
+ goog.events.EventType.POINTERLEAVE, goog.events.EventType.MSPOINTERLEAVE,
+ goog.events.EventType.MOUSELEAVE)
+};
diff --git a/chromium/third_party/ink/closure/events/keycodes.js b/chromium/third_party/ink/closure/events/keycodes.js
new file mode 100644
index 00000000000..745a6995474
--- /dev/null
+++ b/chromium/third_party/ink/closure/events/keycodes.js
@@ -0,0 +1,439 @@
+// Copyright 2006 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Constant declarations for common key codes.
+ *
+ * @author eae@google.com (Emil A Eklund)
+ * @see ../demos/keyhandler.html
+ */
+
+goog.provide('goog.events.KeyCodes');
+
+goog.require('goog.userAgent');
+
+goog.forwardDeclare('goog.events.BrowserEvent');
+
+
+/**
+ * Key codes for common characters.
+ *
+ * This list is not localized and therefore some of the key codes are not
+ * correct for non US keyboard layouts. See comments below.
+ *
+ * @enum {number}
+ */
+goog.events.KeyCodes = {
+ WIN_KEY_FF_LINUX: 0,
+ MAC_ENTER: 3,
+ BACKSPACE: 8,
+ TAB: 9,
+ NUM_CENTER: 12, // NUMLOCK on FF/Safari Mac
+ ENTER: 13,
+ SHIFT: 16,
+ CTRL: 17,
+ ALT: 18,
+ PAUSE: 19,
+ CAPS_LOCK: 20,
+ ESC: 27,
+ SPACE: 32,
+ PAGE_UP: 33, // also NUM_NORTH_EAST
+ PAGE_DOWN: 34, // also NUM_SOUTH_EAST
+ END: 35, // also NUM_SOUTH_WEST
+ HOME: 36, // also NUM_NORTH_WEST
+ LEFT: 37, // also NUM_WEST
+ UP: 38, // also NUM_NORTH
+ RIGHT: 39, // also NUM_EAST
+ DOWN: 40, // also NUM_SOUTH
+ PLUS_SIGN: 43, // NOT numpad plus
+ PRINT_SCREEN: 44,
+ INSERT: 45, // also NUM_INSERT
+ DELETE: 46, // also NUM_DELETE
+ ZERO: 48,
+ ONE: 49,
+ TWO: 50,
+ THREE: 51,
+ FOUR: 52,
+ FIVE: 53,
+ SIX: 54,
+ SEVEN: 55,
+ EIGHT: 56,
+ NINE: 57,
+ FF_SEMICOLON: 59, // Firefox (Gecko) fires this for semicolon instead of 186
+ FF_EQUALS: 61, // Firefox (Gecko) fires this for equals instead of 187
+ FF_DASH: 173, // Firefox (Gecko) fires this for dash instead of 189
+ QUESTION_MARK: 63, // needs localization
+ AT_SIGN: 64,
+ A: 65,
+ B: 66,
+ C: 67,
+ D: 68,
+ E: 69,
+ F: 70,
+ G: 71,
+ H: 72,
+ I: 73,
+ J: 74,
+ K: 75,
+ L: 76,
+ M: 77,
+ N: 78,
+ O: 79,
+ P: 80,
+ Q: 81,
+ R: 82,
+ S: 83,
+ T: 84,
+ U: 85,
+ V: 86,
+ W: 87,
+ X: 88,
+ Y: 89,
+ Z: 90,
+ META: 91, // WIN_KEY_LEFT
+ WIN_KEY_RIGHT: 92,
+ CONTEXT_MENU: 93,
+ NUM_ZERO: 96,
+ NUM_ONE: 97,
+ NUM_TWO: 98,
+ NUM_THREE: 99,
+ NUM_FOUR: 100,
+ NUM_FIVE: 101,
+ NUM_SIX: 102,
+ NUM_SEVEN: 103,
+ NUM_EIGHT: 104,
+ NUM_NINE: 105,
+ NUM_MULTIPLY: 106,
+ NUM_PLUS: 107,
+ NUM_MINUS: 109,
+ NUM_PERIOD: 110,
+ NUM_DIVISION: 111,
+ F1: 112,
+ F2: 113,
+ F3: 114,
+ F4: 115,
+ F5: 116,
+ F6: 117,
+ F7: 118,
+ F8: 119,
+ F9: 120,
+ F10: 121,
+ F11: 122,
+ F12: 123,
+ NUMLOCK: 144,
+ SCROLL_LOCK: 145,
+
+ // OS-specific media keys like volume controls and browser controls.
+ FIRST_MEDIA_KEY: 166,
+ LAST_MEDIA_KEY: 183,
+
+ SEMICOLON: 186, // needs localization
+ DASH: 189, // needs localization
+ EQUALS: 187, // needs localization
+ COMMA: 188, // needs localization
+ PERIOD: 190, // needs localization
+ SLASH: 191, // needs localization
+ APOSTROPHE: 192, // needs localization
+ TILDE: 192, // needs localization
+ SINGLE_QUOTE: 222, // needs localization
+ OPEN_SQUARE_BRACKET: 219, // needs localization
+ BACKSLASH: 220, // needs localization
+ CLOSE_SQUARE_BRACKET: 221, // needs localization
+ WIN_KEY: 224,
+ MAC_FF_META:
+ 224, // Firefox (Gecko) fires this for the meta key instead of 91
+ MAC_WK_CMD_LEFT: 91, // WebKit Left Command key fired, same as META
+ MAC_WK_CMD_RIGHT: 93, // WebKit Right Command key fired, different from META
+ WIN_IME: 229,
+
+ // "Reserved for future use". Some programs (e.g. the SlingPlayer 2.4 ActiveX
+ // control) fire this as a hacky way to disable screensavers.
+ VK_NONAME: 252,
+
+ // We've seen users whose machines fire this keycode at regular one
+ // second intervals. The common thread among these users is that
+ // they're all using Dell Inspiron laptops, so we suspect that this
+ // indicates a hardware/bios problem.
+ // http://en.community.dell.com/support-forums/laptop/f/3518/p/19285957/19523128.aspx
+ PHANTOM: 255
+};
+
+
+/**
+ * Returns false if the event does not contain a text modifying key.
+ *
+ * When it returns true, the event might be text modifying. It is infeasible to
+ * say for sure because of the many different keyboard layouts, so this method
+ * errs on the side of assuming a key event is text-modifiable if we cannot be
+ * certain it is not. As an example, it will return true for ctrl+a, though in
+ * many standard keyboard layouts that key combination would mean "select all",
+ * and not actually modify the text.
+ *
+ * @param {goog.events.BrowserEvent} e A key event.
+ * @return {boolean} Whether it's a text modifying key.
+ */
+goog.events.KeyCodes.isTextModifyingKeyEvent = function(e) {
+ if (e.altKey && !e.ctrlKey || e.metaKey ||
+ // Function keys don't generate text
+ e.keyCode >= goog.events.KeyCodes.F1 &&
+ e.keyCode <= goog.events.KeyCodes.F12) {
+ return false;
+ }
+
+ // The following keys are quite harmless, even in combination with
+ // CTRL, ALT or SHIFT.
+ switch (e.keyCode) {
+ case goog.events.KeyCodes.ALT:
+ case goog.events.KeyCodes.CAPS_LOCK:
+ case goog.events.KeyCodes.CONTEXT_MENU:
+ case goog.events.KeyCodes.CTRL:
+ case goog.events.KeyCodes.DOWN:
+ case goog.events.KeyCodes.END:
+ case goog.events.KeyCodes.ESC:
+ case goog.events.KeyCodes.HOME:
+ case goog.events.KeyCodes.INSERT:
+ case goog.events.KeyCodes.LEFT:
+ case goog.events.KeyCodes.MAC_FF_META:
+ case goog.events.KeyCodes.META:
+ case goog.events.KeyCodes.NUMLOCK:
+ case goog.events.KeyCodes.NUM_CENTER:
+ case goog.events.KeyCodes.PAGE_DOWN:
+ case goog.events.KeyCodes.PAGE_UP:
+ case goog.events.KeyCodes.PAUSE:
+ case goog.events.KeyCodes.PHANTOM:
+ case goog.events.KeyCodes.PRINT_SCREEN:
+ case goog.events.KeyCodes.RIGHT:
+ case goog.events.KeyCodes.SCROLL_LOCK:
+ case goog.events.KeyCodes.SHIFT:
+ case goog.events.KeyCodes.UP:
+ case goog.events.KeyCodes.VK_NONAME:
+ case goog.events.KeyCodes.WIN_KEY:
+ case goog.events.KeyCodes.WIN_KEY_RIGHT:
+ return false;
+ case goog.events.KeyCodes.WIN_KEY_FF_LINUX:
+ return !goog.userAgent.GECKO;
+ default:
+ return e.keyCode < goog.events.KeyCodes.FIRST_MEDIA_KEY ||
+ e.keyCode > goog.events.KeyCodes.LAST_MEDIA_KEY;
+ }
+};
+
+
+/**
+ * Returns true if the key fires a keypress event in the current browser.
+ *
+ * Accoridng to MSDN [1] IE only fires keypress events for the following keys:
+ * - Letters: A - Z (uppercase and lowercase)
+ * - Numerals: 0 - 9
+ * - Symbols: ! @ # $ % ^ & * ( ) _ - + = < [ ] { } , . / ? \ | ' ` " ~
+ * - System: ESC, SPACEBAR, ENTER
+ *
+ * That's not entirely correct though, for instance there's no distinction
+ * between upper and lower case letters.
+ *
+ * [1] http://msdn2.microsoft.com/en-us/library/ms536939(VS.85).aspx)
+ *
+ * Safari is similar to IE, but does not fire keypress for ESC.
+ *
+ * Additionally, IE6 does not fire keydown or keypress events for letters when
+ * the control or alt keys are held down and the shift key is not. IE7 does
+ * fire keydown in these cases, though, but not keypress.
+ *
+ * @param {number} keyCode A key code.
+ * @param {number=} opt_heldKeyCode Key code of a currently-held key.
+ * @param {boolean=} opt_shiftKey Whether the shift key is held down.
+ * @param {boolean=} opt_ctrlKey Whether the control key is held down.
+ * @param {boolean=} opt_altKey Whether the alt key is held down.
+ * @param {boolean=} opt_metaKey Whether the meta key is held down.
+ * @return {boolean} Whether it's a key that fires a keypress event.
+ */
+goog.events.KeyCodes.firesKeyPressEvent = function(
+ keyCode, opt_heldKeyCode, opt_shiftKey, opt_ctrlKey, opt_altKey,
+ opt_metaKey) {
+ if (!goog.userAgent.IE && !goog.userAgent.EDGE &&
+ !(goog.userAgent.WEBKIT && goog.userAgent.isVersionOrHigher('525'))) {
+ return true;
+ }
+
+ if (goog.userAgent.MAC && opt_altKey) {
+ return goog.events.KeyCodes.isCharacterKey(keyCode);
+ }
+
+ // Alt but not AltGr which is represented as Alt+Ctrl.
+ if (opt_altKey && !opt_ctrlKey) {
+ return false;
+ }
+
+ // Saves Ctrl or Alt + key for IE and WebKit 525+, which won't fire keypress.
+ // Non-IE browsers and WebKit prior to 525 won't get this far so no need to
+ // check the user agent.
+ if (goog.isNumber(opt_heldKeyCode)) {
+ opt_heldKeyCode = goog.events.KeyCodes.normalizeKeyCode(opt_heldKeyCode);
+ }
+ var heldKeyIsModifier = opt_heldKeyCode == goog.events.KeyCodes.CTRL ||
+ opt_heldKeyCode == goog.events.KeyCodes.ALT ||
+ goog.userAgent.MAC && opt_heldKeyCode == goog.events.KeyCodes.META;
+ // The Shift key blocks keypresses on Mac iff accompanied by another modifier.
+ var modifiedShiftKey = opt_heldKeyCode == goog.events.KeyCodes.SHIFT &&
+ (opt_ctrlKey || opt_metaKey);
+ if ((!opt_shiftKey || goog.userAgent.MAC) && heldKeyIsModifier ||
+ goog.userAgent.MAC && modifiedShiftKey) {
+ return false;
+ }
+
+ // Some keys with Ctrl/Shift do not issue keypress in WEBKIT.
+ if ((goog.userAgent.WEBKIT || goog.userAgent.EDGE) && opt_ctrlKey &&
+ opt_shiftKey) {
+ switch (keyCode) {
+ case goog.events.KeyCodes.BACKSLASH:
+ case goog.events.KeyCodes.OPEN_SQUARE_BRACKET:
+ case goog.events.KeyCodes.CLOSE_SQUARE_BRACKET:
+ case goog.events.KeyCodes.TILDE:
+ case goog.events.KeyCodes.SEMICOLON:
+ case goog.events.KeyCodes.DASH:
+ case goog.events.KeyCodes.EQUALS:
+ case goog.events.KeyCodes.COMMA:
+ case goog.events.KeyCodes.PERIOD:
+ case goog.events.KeyCodes.SLASH:
+ case goog.events.KeyCodes.APOSTROPHE:
+ case goog.events.KeyCodes.SINGLE_QUOTE:
+ return false;
+ }
+ }
+
+ // When Ctrl+<somekey> is held in IE, it only fires a keypress once, but it
+ // continues to fire keydown events as the event repeats.
+ if (goog.userAgent.IE && opt_ctrlKey && opt_heldKeyCode == keyCode) {
+ return false;
+ }
+
+ switch (keyCode) {
+ case goog.events.KeyCodes.ENTER:
+ return true;
+ case goog.events.KeyCodes.ESC:
+ return !(goog.userAgent.WEBKIT || goog.userAgent.EDGE);
+ }
+
+ return goog.events.KeyCodes.isCharacterKey(keyCode);
+};
+
+
+/**
+ * Returns true if the key produces a character.
+ * This does not cover characters on non-US keyboards (Russian, Hebrew, etc.).
+ *
+ * @param {number} keyCode A key code.
+ * @return {boolean} Whether it's a character key.
+ */
+goog.events.KeyCodes.isCharacterKey = function(keyCode) {
+ if (keyCode >= goog.events.KeyCodes.ZERO &&
+ keyCode <= goog.events.KeyCodes.NINE) {
+ return true;
+ }
+
+ if (keyCode >= goog.events.KeyCodes.NUM_ZERO &&
+ keyCode <= goog.events.KeyCodes.NUM_MULTIPLY) {
+ return true;
+ }
+
+ if (keyCode >= goog.events.KeyCodes.A && keyCode <= goog.events.KeyCodes.Z) {
+ return true;
+ }
+
+ // Safari sends zero key code for non-latin characters.
+ if ((goog.userAgent.WEBKIT || goog.userAgent.EDGE) && keyCode == 0) {
+ return true;
+ }
+
+ switch (keyCode) {
+ case goog.events.KeyCodes.SPACE:
+ case goog.events.KeyCodes.PLUS_SIGN:
+ case goog.events.KeyCodes.QUESTION_MARK:
+ case goog.events.KeyCodes.AT_SIGN:
+ case goog.events.KeyCodes.NUM_PLUS:
+ case goog.events.KeyCodes.NUM_MINUS:
+ case goog.events.KeyCodes.NUM_PERIOD:
+ case goog.events.KeyCodes.NUM_DIVISION:
+ case goog.events.KeyCodes.SEMICOLON:
+ case goog.events.KeyCodes.FF_SEMICOLON:
+ case goog.events.KeyCodes.DASH:
+ case goog.events.KeyCodes.EQUALS:
+ case goog.events.KeyCodes.FF_EQUALS:
+ case goog.events.KeyCodes.COMMA:
+ case goog.events.KeyCodes.PERIOD:
+ case goog.events.KeyCodes.SLASH:
+ case goog.events.KeyCodes.APOSTROPHE:
+ case goog.events.KeyCodes.SINGLE_QUOTE:
+ case goog.events.KeyCodes.OPEN_SQUARE_BRACKET:
+ case goog.events.KeyCodes.BACKSLASH:
+ case goog.events.KeyCodes.CLOSE_SQUARE_BRACKET:
+ return true;
+ default:
+ return false;
+ }
+};
+
+
+/**
+ * Normalizes key codes from OS/Browser-specific value to the general one.
+ * @param {number} keyCode The native key code.
+ * @return {number} The normalized key code.
+ */
+goog.events.KeyCodes.normalizeKeyCode = function(keyCode) {
+ if (goog.userAgent.GECKO) {
+ return goog.events.KeyCodes.normalizeGeckoKeyCode(keyCode);
+ } else if (goog.userAgent.MAC && goog.userAgent.WEBKIT) {
+ return goog.events.KeyCodes.normalizeMacWebKitKeyCode(keyCode);
+ } else {
+ return keyCode;
+ }
+};
+
+
+/**
+ * Normalizes key codes from their Gecko-specific value to the general one.
+ * @param {number} keyCode The native key code.
+ * @return {number} The normalized key code.
+ */
+goog.events.KeyCodes.normalizeGeckoKeyCode = function(keyCode) {
+ switch (keyCode) {
+ case goog.events.KeyCodes.FF_EQUALS:
+ return goog.events.KeyCodes.EQUALS;
+ case goog.events.KeyCodes.FF_SEMICOLON:
+ return goog.events.KeyCodes.SEMICOLON;
+ case goog.events.KeyCodes.FF_DASH:
+ return goog.events.KeyCodes.DASH;
+ case goog.events.KeyCodes.MAC_FF_META:
+ return goog.events.KeyCodes.META;
+ case goog.events.KeyCodes.WIN_KEY_FF_LINUX:
+ return goog.events.KeyCodes.WIN_KEY;
+ default:
+ return keyCode;
+ }
+};
+
+
+/**
+ * Normalizes key codes from their Mac WebKit-specific value to the general one.
+ * @param {number} keyCode The native key code.
+ * @return {number} The normalized key code.
+ */
+goog.events.KeyCodes.normalizeMacWebKitKeyCode = function(keyCode) {
+ switch (keyCode) {
+ case goog.events.KeyCodes.MAC_WK_CMD_RIGHT: // 93
+ return goog.events.KeyCodes.META; // 91
+ default:
+ return keyCode;
+ }
+};
diff --git a/chromium/third_party/ink/closure/events/listenable.js b/chromium/third_party/ink/closure/events/listenable.js
new file mode 100644
index 00000000000..0f29d81ffdb
--- /dev/null
+++ b/chromium/third_party/ink/closure/events/listenable.js
@@ -0,0 +1,338 @@
+// Copyright 2012 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview An interface for a listenable JavaScript object.
+ * @author chrishenry@google.com (Chris Henry)
+ */
+
+goog.provide('goog.events.Listenable');
+goog.provide('goog.events.ListenableKey');
+
+/** @suppress {extraRequire} */
+goog.require('goog.events.EventId');
+
+goog.forwardDeclare('goog.events.EventLike');
+goog.forwardDeclare('goog.events.EventTarget');
+
+
+
+/**
+ * A listenable interface. A listenable is an object with the ability
+ * to dispatch/broadcast events to "event listeners" registered via
+ * listen/listenOnce.
+ *
+ * The interface allows for an event propagation mechanism similar
+ * to one offered by native browser event targets, such as
+ * capture/bubble mechanism, stopping propagation, and preventing
+ * default actions. Capture/bubble mechanism depends on the ancestor
+ * tree constructed via {@code #getParentEventTarget}; this tree
+ * must be directed acyclic graph. The meaning of default action(s)
+ * in preventDefault is specific to a particular use case.
+ *
+ * Implementations that do not support capture/bubble or can not have
+ * a parent listenable can simply not implement any ability to set the
+ * parent listenable (and have {@code #getParentEventTarget} return
+ * null).
+ *
+ * Implementation of this class can be used with or independently from
+ * goog.events.
+ *
+ * Implementation must call {@code #addImplementation(implClass)}.
+ *
+ * @interface
+ * @see goog.events
+ * @see http://www.w3.org/TR/DOM-Level-2-Events/events.html
+ */
+goog.events.Listenable = function() {};
+
+
+/**
+ * An expando property to indicate that an object implements
+ * goog.events.Listenable.
+ *
+ * See addImplementation/isImplementedBy.
+ *
+ * @type {string}
+ * @const
+ */
+goog.events.Listenable.IMPLEMENTED_BY_PROP =
+ 'closure_listenable_' + ((Math.random() * 1e6) | 0);
+
+
+/**
+ * Marks a given class (constructor) as an implementation of
+ * Listenable, do that we can query that fact at runtime. The class
+ * must have already implemented the interface.
+ * @param {!function(new:goog.events.Listenable,...)} cls The class constructor.
+ * The corresponding class must have already implemented the interface.
+ */
+goog.events.Listenable.addImplementation = function(cls) {
+ cls.prototype[goog.events.Listenable.IMPLEMENTED_BY_PROP] = true;
+};
+
+
+/**
+ * @param {Object} obj The object to check.
+ * @return {boolean} Whether a given instance implements Listenable. The
+ * class/superclass of the instance must call addImplementation.
+ */
+goog.events.Listenable.isImplementedBy = function(obj) {
+ return !!(obj && obj[goog.events.Listenable.IMPLEMENTED_BY_PROP]);
+};
+
+
+/**
+ * Adds an event listener. A listener can only be added once to an
+ * object and if it is added again the key for the listener is
+ * returned. Note that if the existing listener is a one-off listener
+ * (registered via listenOnce), it will no longer be a one-off
+ * listener after a call to listen().
+ *
+ * @param {string|!goog.events.EventId<EVENTOBJ>} type The event type id.
+ * @param {function(this:SCOPE, EVENTOBJ):(boolean|undefined)} listener Callback
+ * method.
+ * @param {boolean=} opt_useCapture Whether to fire in capture phase
+ * (defaults to false).
+ * @param {SCOPE=} opt_listenerScope Object in whose scope to call the
+ * listener.
+ * @return {!goog.events.ListenableKey} Unique key for the listener.
+ * @template SCOPE,EVENTOBJ
+ */
+goog.events.Listenable.prototype.listen;
+
+
+/**
+ * Adds an event listener that is removed automatically after the
+ * listener fired once.
+ *
+ * If an existing listener already exists, listenOnce will do
+ * nothing. In particular, if the listener was previously registered
+ * via listen(), listenOnce() will not turn the listener into a
+ * one-off listener. Similarly, if there is already an existing
+ * one-off listener, listenOnce does not modify the listeners (it is
+ * still a once listener).
+ *
+ * @param {string|!goog.events.EventId<EVENTOBJ>} type The event type id.
+ * @param {function(this:SCOPE, EVENTOBJ):(boolean|undefined)} listener Callback
+ * method.
+ * @param {boolean=} opt_useCapture Whether to fire in capture phase
+ * (defaults to false).
+ * @param {SCOPE=} opt_listenerScope Object in whose scope to call the
+ * listener.
+ * @return {!goog.events.ListenableKey} Unique key for the listener.
+ * @template SCOPE,EVENTOBJ
+ */
+goog.events.Listenable.prototype.listenOnce;
+
+
+/**
+ * Removes an event listener which was added with listen() or listenOnce().
+ *
+ * @param {string|!goog.events.EventId<EVENTOBJ>} type The event type id.
+ * @param {function(this:SCOPE, EVENTOBJ):(boolean|undefined)} listener Callback
+ * method.
+ * @param {boolean=} opt_useCapture Whether to fire in capture phase
+ * (defaults to false).
+ * @param {SCOPE=} opt_listenerScope Object in whose scope to call
+ * the listener.
+ * @return {boolean} Whether any listener was removed.
+ * @template SCOPE,EVENTOBJ
+ */
+goog.events.Listenable.prototype.unlisten;
+
+
+/**
+ * Removes an event listener which was added with listen() by the key
+ * returned by listen().
+ *
+ * @param {!goog.events.ListenableKey} key The key returned by
+ * listen() or listenOnce().
+ * @return {boolean} Whether any listener was removed.
+ */
+goog.events.Listenable.prototype.unlistenByKey;
+
+
+/**
+ * Dispatches an event (or event like object) and calls all listeners
+ * listening for events of this type. The type of the event is decided by the
+ * type property on the event object.
+ *
+ * If any of the listeners returns false OR calls preventDefault then this
+ * function will return false. If one of the capture listeners calls
+ * stopPropagation, then the bubble listeners won't fire.
+ *
+ * @param {goog.events.EventLike} e Event object.
+ * @return {boolean} If anyone called preventDefault on the event object (or
+ * if any of the listeners returns false) this will also return false.
+ */
+goog.events.Listenable.prototype.dispatchEvent;
+
+
+/**
+ * Removes all listeners from this listenable. If type is specified,
+ * it will only remove listeners of the particular type. otherwise all
+ * registered listeners will be removed.
+ *
+ * @param {string=} opt_type Type of event to remove, default is to
+ * remove all types.
+ * @return {number} Number of listeners removed.
+ */
+goog.events.Listenable.prototype.removeAllListeners;
+
+
+/**
+ * Returns the parent of this event target to use for capture/bubble
+ * mechanism.
+ *
+ * NOTE(chrishenry): The name reflects the original implementation of
+ * custom event target ({@code goog.events.EventTarget}). We decided
+ * that changing the name is not worth it.
+ *
+ * @return {goog.events.Listenable} The parent EventTarget or null if
+ * there is no parent.
+ */
+goog.events.Listenable.prototype.getParentEventTarget;
+
+
+/**
+ * Fires all registered listeners in this listenable for the given
+ * type and capture mode, passing them the given eventObject. This
+ * does not perform actual capture/bubble. Only implementors of the
+ * interface should be using this.
+ *
+ * @param {string|!goog.events.EventId<EVENTOBJ>} type The type of the
+ * listeners to fire.
+ * @param {boolean} capture The capture mode of the listeners to fire.
+ * @param {EVENTOBJ} eventObject The event object to fire.
+ * @return {boolean} Whether all listeners succeeded without
+ * attempting to prevent default behavior. If any listener returns
+ * false or called goog.events.Event#preventDefault, this returns
+ * false.
+ * @template EVENTOBJ
+ */
+goog.events.Listenable.prototype.fireListeners;
+
+
+/**
+ * Gets all listeners in this listenable for the given type and
+ * capture mode.
+ *
+ * @param {string|!goog.events.EventId} type The type of the listeners to fire.
+ * @param {boolean} capture The capture mode of the listeners to fire.
+ * @return {!Array<!goog.events.ListenableKey>} An array of registered
+ * listeners.
+ * @template EVENTOBJ
+ */
+goog.events.Listenable.prototype.getListeners;
+
+
+/**
+ * Gets the goog.events.ListenableKey for the event or null if no such
+ * listener is in use.
+ *
+ * @param {string|!goog.events.EventId<EVENTOBJ>} type The name of the event
+ * without the 'on' prefix.
+ * @param {function(this:SCOPE, EVENTOBJ):(boolean|undefined)} listener The
+ * listener function to get.
+ * @param {boolean} capture Whether the listener is a capturing listener.
+ * @param {SCOPE=} opt_listenerScope Object in whose scope to call the
+ * listener.
+ * @return {goog.events.ListenableKey} the found listener or null if not found.
+ * @template SCOPE,EVENTOBJ
+ */
+goog.events.Listenable.prototype.getListener;
+
+
+/**
+ * Whether there is any active listeners matching the specified
+ * signature. If either the type or capture parameters are
+ * unspecified, the function will match on the remaining criteria.
+ *
+ * @param {string|!goog.events.EventId<EVENTOBJ>=} opt_type Event type.
+ * @param {boolean=} opt_capture Whether to check for capture or bubble
+ * listeners.
+ * @return {boolean} Whether there is any active listeners matching
+ * the requested type and/or capture phase.
+ * @template EVENTOBJ
+ */
+goog.events.Listenable.prototype.hasListener;
+
+
+
+/**
+ * An interface that describes a single registered listener.
+ * @interface
+ */
+goog.events.ListenableKey = function() {};
+
+
+/**
+ * Counter used to create a unique key
+ * @type {number}
+ * @private
+ */
+goog.events.ListenableKey.counter_ = 0;
+
+
+/**
+ * Reserves a key to be used for ListenableKey#key field.
+ * @return {number} A number to be used to fill ListenableKey#key
+ * field.
+ */
+goog.events.ListenableKey.reserveKey = function() {
+ return ++goog.events.ListenableKey.counter_;
+};
+
+
+/**
+ * The source event target.
+ * @type {Object|goog.events.Listenable|goog.events.EventTarget}
+ */
+goog.events.ListenableKey.prototype.src;
+
+
+/**
+ * The event type the listener is listening to.
+ * @type {string}
+ */
+goog.events.ListenableKey.prototype.type;
+
+
+/**
+ * The listener function.
+ * @type {function(?):?|{handleEvent:function(?):?}|null}
+ */
+goog.events.ListenableKey.prototype.listener;
+
+
+/**
+ * Whether the listener works on capture phase.
+ * @type {boolean}
+ */
+goog.events.ListenableKey.prototype.capture;
+
+
+/**
+ * The 'this' object for the listener function's scope.
+ * @type {Object|undefined}
+ */
+goog.events.ListenableKey.prototype.handler;
+
+
+/**
+ * A globally unique number to identify the key.
+ * @type {number}
+ */
+goog.events.ListenableKey.prototype.key;
diff --git a/chromium/third_party/ink/closure/events/listener.js b/chromium/third_party/ink/closure/events/listener.js
new file mode 100644
index 00000000000..282c69fcb92
--- /dev/null
+++ b/chromium/third_party/ink/closure/events/listener.js
@@ -0,0 +1,129 @@
+// Copyright 2005 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Listener object.
+ * @author pupius@google.com (Daniel Pupius)
+ * @see ../demos/events.html
+ */
+
+goog.provide('goog.events.Listener');
+
+goog.require('goog.events.ListenableKey');
+
+
+
+/**
+ * Simple class that stores information about a listener
+ * @param {function(?):?} listener Callback function.
+ * @param {Function} proxy Wrapper for the listener that patches the event.
+ * @param {EventTarget|goog.events.Listenable} src Source object for
+ * the event.
+ * @param {string} type Event type.
+ * @param {boolean} capture Whether in capture or bubble phase.
+ * @param {Object=} opt_handler Object in whose context to execute the callback.
+ * @implements {goog.events.ListenableKey}
+ * @constructor
+ */
+goog.events.Listener = function(
+ listener, proxy, src, type, capture, opt_handler) {
+ if (goog.events.Listener.ENABLE_MONITORING) {
+ this.creationStack = new Error().stack;
+ }
+
+ /** @override */
+ this.listener = listener;
+
+ /**
+ * A wrapper over the original listener. This is used solely to
+ * handle native browser events (it is used to simulate the capture
+ * phase and to patch the event object).
+ * @type {Function}
+ */
+ this.proxy = proxy;
+
+ /**
+ * Object or node that callback is listening to
+ * @type {EventTarget|goog.events.Listenable}
+ */
+ this.src = src;
+
+ /**
+ * The event type.
+ * @const {string}
+ */
+ this.type = type;
+
+ /**
+ * Whether the listener is being called in the capture or bubble phase
+ * @const {boolean}
+ */
+ this.capture = !!capture;
+
+ /**
+ * Optional object whose context to execute the listener in
+ * @type {Object|undefined}
+ */
+ this.handler = opt_handler;
+
+ /**
+ * The key of the listener.
+ * @const {number}
+ * @override
+ */
+ this.key = goog.events.ListenableKey.reserveKey();
+
+ /**
+ * Whether to remove the listener after it has been called.
+ * @type {boolean}
+ */
+ this.callOnce = false;
+
+ /**
+ * Whether the listener has been removed.
+ * @type {boolean}
+ */
+ this.removed = false;
+};
+
+
+/**
+ * @define {boolean} Whether to enable the monitoring of the
+ * goog.events.Listener instances. Switching on the monitoring is only
+ * recommended for debugging because it has a significant impact on
+ * performance and memory usage. If switched off, the monitoring code
+ * compiles down to 0 bytes.
+ */
+goog.define('goog.events.Listener.ENABLE_MONITORING', false);
+
+
+/**
+ * If monitoring the goog.events.Listener instances is enabled, stores the
+ * creation stack trace of the Disposable instance.
+ * @type {string}
+ */
+goog.events.Listener.prototype.creationStack;
+
+
+/**
+ * Marks this listener as removed. This also remove references held by
+ * this listener object (such as listener and event source).
+ */
+goog.events.Listener.prototype.markAsRemoved = function() {
+ this.removed = true;
+ this.listener = null;
+ this.proxy = null;
+ this.src = null;
+ this.handler = null;
+};
diff --git a/chromium/third_party/ink/closure/events/listenermap.js b/chromium/third_party/ink/closure/events/listenermap.js
new file mode 100644
index 00000000000..30fea184661
--- /dev/null
+++ b/chromium/third_party/ink/closure/events/listenermap.js
@@ -0,0 +1,307 @@
+// Copyright 2013 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview A map of listeners that provides utility functions to
+ * deal with listeners on an event target. Used by
+ * {@code goog.events.EventTarget}.
+ *
+ * WARNING: Do not use this class from outside goog.events package.
+ *
+ * @visibility {//javascript/closure/bin/sizetests:__pkg__}
+ * @visibility {//javascript/closure:__pkg__}
+ * @visibility {//javascript/closure/events:__pkg__}
+ * @visibility {//javascript/closure/labs/events:__pkg__}
+ */
+
+goog.provide('goog.events.ListenerMap');
+
+goog.require('goog.array');
+goog.require('goog.events.Listener');
+goog.require('goog.object');
+
+
+
+/**
+ * Creates a new listener map.
+ * @param {EventTarget|goog.events.Listenable} src The src object.
+ * @constructor
+ * @final
+ */
+goog.events.ListenerMap = function(src) {
+ /** @type {EventTarget|goog.events.Listenable} */
+ this.src = src;
+
+ /**
+ * Maps of event type to an array of listeners.
+ * @type {!Object<string, !Array<!goog.events.Listener>>}
+ */
+ this.listeners = {};
+
+ /**
+ * The count of types in this map that have registered listeners.
+ * @private {number}
+ */
+ this.typeCount_ = 0;
+};
+
+
+/**
+ * @return {number} The count of event types in this map that actually
+ * have registered listeners.
+ */
+goog.events.ListenerMap.prototype.getTypeCount = function() {
+ return this.typeCount_;
+};
+
+
+/**
+ * @return {number} Total number of registered listeners.
+ */
+goog.events.ListenerMap.prototype.getListenerCount = function() {
+ var count = 0;
+ for (var type in this.listeners) {
+ count += this.listeners[type].length;
+ }
+ return count;
+};
+
+
+/**
+ * Adds an event listener. A listener can only be added once to an
+ * object and if it is added again the key for the listener is
+ * returned.
+ *
+ * Note that a one-off listener will not change an existing listener,
+ * if any. On the other hand a normal listener will change existing
+ * one-off listener to become a normal listener.
+ *
+ * @param {string|!goog.events.EventId} type The listener event type.
+ * @param {!Function} listener This listener callback method.
+ * @param {boolean} callOnce Whether the listener is a one-off
+ * listener.
+ * @param {boolean=} opt_useCapture The capture mode of the listener.
+ * @param {Object=} opt_listenerScope Object in whose scope to call the
+ * listener.
+ * @return {!goog.events.ListenableKey} Unique key for the listener.
+ */
+goog.events.ListenerMap.prototype.add = function(
+ type, listener, callOnce, opt_useCapture, opt_listenerScope) {
+ var typeStr = type.toString();
+ var listenerArray = this.listeners[typeStr];
+ if (!listenerArray) {
+ listenerArray = this.listeners[typeStr] = [];
+ this.typeCount_++;
+ }
+
+ var listenerObj;
+ var index = goog.events.ListenerMap.findListenerIndex_(
+ listenerArray, listener, opt_useCapture, opt_listenerScope);
+ if (index > -1) {
+ listenerObj = listenerArray[index];
+ if (!callOnce) {
+ // Ensure that, if there is an existing callOnce listener, it is no
+ // longer a callOnce listener.
+ listenerObj.callOnce = false;
+ }
+ } else {
+ listenerObj = new goog.events.Listener(
+ listener, null, this.src, typeStr, !!opt_useCapture, opt_listenerScope);
+ listenerObj.callOnce = callOnce;
+ listenerArray.push(listenerObj);
+ }
+ return listenerObj;
+};
+
+
+/**
+ * Removes a matching listener.
+ * @param {string|!goog.events.EventId} type The listener event type.
+ * @param {!Function} listener This listener callback method.
+ * @param {boolean=} opt_useCapture The capture mode of the listener.
+ * @param {Object=} opt_listenerScope Object in whose scope to call the
+ * listener.
+ * @return {boolean} Whether any listener was removed.
+ */
+goog.events.ListenerMap.prototype.remove = function(
+ type, listener, opt_useCapture, opt_listenerScope) {
+ var typeStr = type.toString();
+ if (!(typeStr in this.listeners)) {
+ return false;
+ }
+
+ var listenerArray = this.listeners[typeStr];
+ var index = goog.events.ListenerMap.findListenerIndex_(
+ listenerArray, listener, opt_useCapture, opt_listenerScope);
+ if (index > -1) {
+ var listenerObj = listenerArray[index];
+ listenerObj.markAsRemoved();
+ goog.array.removeAt(listenerArray, index);
+ if (listenerArray.length == 0) {
+ delete this.listeners[typeStr];
+ this.typeCount_--;
+ }
+ return true;
+ }
+ return false;
+};
+
+
+/**
+ * Removes the given listener object.
+ * @param {!goog.events.ListenableKey} listener The listener to remove.
+ * @return {boolean} Whether the listener is removed.
+ */
+goog.events.ListenerMap.prototype.removeByKey = function(listener) {
+ var type = listener.type;
+ if (!(type in this.listeners)) {
+ return false;
+ }
+
+ var removed = goog.array.remove(this.listeners[type], listener);
+ if (removed) {
+ /** @type {!goog.events.Listener} */ (listener).markAsRemoved();
+ if (this.listeners[type].length == 0) {
+ delete this.listeners[type];
+ this.typeCount_--;
+ }
+ }
+ return removed;
+};
+
+
+/**
+ * Removes all listeners from this map. If opt_type is provided, only
+ * listeners that match the given type are removed.
+ * @param {string|!goog.events.EventId=} opt_type Type of event to remove.
+ * @return {number} Number of listeners removed.
+ */
+goog.events.ListenerMap.prototype.removeAll = function(opt_type) {
+ var typeStr = opt_type && opt_type.toString();
+ var count = 0;
+ for (var type in this.listeners) {
+ if (!typeStr || type == typeStr) {
+ var listenerArray = this.listeners[type];
+ for (var i = 0; i < listenerArray.length; i++) {
+ ++count;
+ listenerArray[i].markAsRemoved();
+ }
+ delete this.listeners[type];
+ this.typeCount_--;
+ }
+ }
+ return count;
+};
+
+
+/**
+ * Gets all listeners that match the given type and capture mode. The
+ * returned array is a copy (but the listener objects are not).
+ * @param {string|!goog.events.EventId} type The type of the listeners
+ * to retrieve.
+ * @param {boolean} capture The capture mode of the listeners to retrieve.
+ * @return {!Array<!goog.events.ListenableKey>} An array of matching
+ * listeners.
+ */
+goog.events.ListenerMap.prototype.getListeners = function(type, capture) {
+ var listenerArray = this.listeners[type.toString()];
+ var rv = [];
+ if (listenerArray) {
+ for (var i = 0; i < listenerArray.length; ++i) {
+ var listenerObj = listenerArray[i];
+ if (listenerObj.capture == capture) {
+ rv.push(listenerObj);
+ }
+ }
+ }
+ return rv;
+};
+
+
+/**
+ * Gets the goog.events.ListenableKey for the event or null if no such
+ * listener is in use.
+ *
+ * @param {string|!goog.events.EventId} type The type of the listener
+ * to retrieve.
+ * @param {!Function} listener The listener function to get.
+ * @param {boolean} capture Whether the listener is a capturing listener.
+ * @param {Object=} opt_listenerScope Object in whose scope to call the
+ * listener.
+ * @return {goog.events.ListenableKey} the found listener or null if not found.
+ */
+goog.events.ListenerMap.prototype.getListener = function(
+ type, listener, capture, opt_listenerScope) {
+ var listenerArray = this.listeners[type.toString()];
+ var i = -1;
+ if (listenerArray) {
+ i = goog.events.ListenerMap.findListenerIndex_(
+ listenerArray, listener, capture, opt_listenerScope);
+ }
+ return i > -1 ? listenerArray[i] : null;
+};
+
+
+/**
+ * Whether there is a matching listener. If either the type or capture
+ * parameters are unspecified, the function will match on the
+ * remaining criteria.
+ *
+ * @param {string|!goog.events.EventId=} opt_type The type of the listener.
+ * @param {boolean=} opt_capture The capture mode of the listener.
+ * @return {boolean} Whether there is an active listener matching
+ * the requested type and/or capture phase.
+ */
+goog.events.ListenerMap.prototype.hasListener = function(
+ opt_type, opt_capture) {
+ var hasType = goog.isDef(opt_type);
+ var typeStr = hasType ? opt_type.toString() : '';
+ var hasCapture = goog.isDef(opt_capture);
+
+ return goog.object.some(this.listeners, function(listenerArray, type) {
+ for (var i = 0; i < listenerArray.length; ++i) {
+ if ((!hasType || listenerArray[i].type == typeStr) &&
+ (!hasCapture || listenerArray[i].capture == opt_capture)) {
+ return true;
+ }
+ }
+
+ return false;
+ });
+};
+
+
+/**
+ * Finds the index of a matching goog.events.Listener in the given
+ * listenerArray.
+ * @param {!Array<!goog.events.Listener>} listenerArray Array of listener.
+ * @param {!Function} listener The listener function.
+ * @param {boolean=} opt_useCapture The capture flag for the listener.
+ * @param {Object=} opt_listenerScope The listener scope.
+ * @return {number} The index of the matching listener within the
+ * listenerArray.
+ * @private
+ */
+goog.events.ListenerMap.findListenerIndex_ = function(
+ listenerArray, listener, opt_useCapture, opt_listenerScope) {
+ for (var i = 0; i < listenerArray.length; ++i) {
+ var listenerObj = listenerArray[i];
+ if (!listenerObj.removed && listenerObj.listener == listener &&
+ listenerObj.capture == !!opt_useCapture &&
+ listenerObj.handler == opt_listenerScope) {
+ return i;
+ }
+ }
+ return -1;
+};
diff --git a/chromium/third_party/ink/closure/events/wheelevent.js b/chromium/third_party/ink/closure/events/wheelevent.js
new file mode 100644
index 00000000000..770724af790
--- /dev/null
+++ b/chromium/third_party/ink/closure/events/wheelevent.js
@@ -0,0 +1,170 @@
+// Copyright 2014 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview This class aims to smooth out inconsistencies between browser
+ * handling of wheel events by providing an event that is similar to that
+ * defined in the standard, but also easier to consume.
+ *
+ * It is based upon the WheelEvent, which allows for up to 3 dimensional
+ * scrolling events that come in units of either pixels, lines or pages.
+ * http://www.w3.org/TR/2014/WD-DOM-Level-3-Events-20140925/#interface-WheelEvent
+ *
+ * The significant difference here is that it also provides reasonable pixel
+ * deltas for clients that do not want to treat line and page scrolling events
+ * specially.
+ *
+ * Clients of this code should be aware that some input devices only fire a few
+ * discrete events (such as a mouse wheel without acceleration) whereas some can
+ * generate a large number of events for a single interaction (such as a
+ * touchpad with acceleration). There is no signal in the events to reliably
+ * distinguish between these.
+ *
+ * @author joshuawilder@google.com (Joshua Wilder)
+ * @see ../demos/wheelhandler.html
+ */
+
+goog.provide('goog.events.WheelEvent');
+
+goog.require('goog.asserts');
+goog.require('goog.events.BrowserEvent');
+
+
+
+/**
+ * A common class for wheel events. This is used with the WheelHandler.
+ *
+ * @param {Event} browserEvent Browser event object.
+ * @param {goog.events.WheelEvent.DeltaMode} deltaMode The delta mode units of
+ * the wheel event.
+ * @param {number} deltaX The number of delta units the user in the X axis.
+ * @param {number} deltaY The number of delta units the user in the Y axis.
+ * @param {number} deltaZ The number of delta units the user in the Z axis.
+ * @constructor
+ * @extends {goog.events.BrowserEvent}
+ * @final
+ */
+goog.events.WheelEvent = function(
+ browserEvent, deltaMode, deltaX, deltaY, deltaZ) {
+ goog.events.WheelEvent.base(this, 'constructor', browserEvent);
+ goog.asserts.assert(browserEvent, 'Expecting a non-null browserEvent');
+
+ /** @type {goog.events.WheelEvent.EventType} */
+ this.type = goog.events.WheelEvent.EventType.WHEEL;
+
+ /**
+ * An enum corresponding to the units of this event.
+ * @type {goog.events.WheelEvent.DeltaMode}
+ */
+ this.deltaMode = deltaMode;
+
+ /**
+ * The number of delta units in the X axis.
+ * @type {number}
+ */
+ this.deltaX = deltaX;
+
+ /**
+ * The number of delta units in the Y axis.
+ * @type {number}
+ */
+ this.deltaY = deltaY;
+
+ /**
+ * The number of delta units in the Z axis.
+ * @type {number}
+ */
+ this.deltaZ = deltaZ;
+
+ // Ratio between delta and pixel values.
+ var pixelRatio = 1; // Value for DeltaMode.PIXEL
+ switch (deltaMode) {
+ case goog.events.WheelEvent.DeltaMode.PAGE:
+ pixelRatio *= goog.events.WheelEvent.PIXELS_PER_PAGE_;
+ break;
+ case goog.events.WheelEvent.DeltaMode.LINE:
+ pixelRatio *= goog.events.WheelEvent.PIXELS_PER_LINE_;
+ break;
+ }
+
+ /**
+ * The number of delta pixels in the X axis. Code that doesn't want to handle
+ * different deltaMode units can just look here.
+ * @type {number}
+ */
+ this.pixelDeltaX = this.deltaX * pixelRatio;
+
+ /**
+ * The number of pixels in the Y axis. Code that doesn't want to
+ * handle different deltaMode units can just look here.
+ * @type {number}
+ */
+ this.pixelDeltaY = this.deltaY * pixelRatio;
+
+ /**
+ * The number of pixels scrolled in the Z axis. Code that doesn't want to
+ * handle different deltaMode units can just look here.
+ * @type {number}
+ */
+ this.pixelDeltaZ = this.deltaZ * pixelRatio;
+};
+goog.inherits(goog.events.WheelEvent, goog.events.BrowserEvent);
+
+
+/**
+ * Enum type for the events fired by the wheel handler.
+ * @enum {string}
+ */
+goog.events.WheelEvent.EventType = {
+ /** The user has provided wheel-based input. */
+ WHEEL: 'wheel'
+};
+
+
+/**
+ * Units for the deltas in a WheelEvent.
+ * @enum {number}
+ */
+goog.events.WheelEvent.DeltaMode = {
+ /** The units are in pixels. From DOM_DELTA_PIXEL. */
+ PIXEL: 0,
+ /** The units are in lines. From DOM_DELTA_LINE. */
+ LINE: 1,
+ /** The units are in pages. From DOM_DELTA_PAGE. */
+ PAGE: 2
+};
+
+
+/**
+ * A conversion number between line scroll units and pixel scroll units. The
+ * actual value per line can vary a lot between devices and font sizes. This
+ * number can not be perfect, but it should be reasonable for converting lines
+ * scroll events into pixels.
+ * @const {number}
+ * @private
+ */
+goog.events.WheelEvent.PIXELS_PER_LINE_ = 15;
+
+
+/**
+ * A conversion number between page scroll units and pixel scroll units. The
+ * actual value per page can vary a lot as many different devices have different
+ * screen sizes, and the window might not be taking up the full screen. This
+ * number can not be perfect, but it should be reasonable for converting page
+ * scroll events into pixels.
+ * @const {number}
+ * @private
+ */
+goog.events.WheelEvent.PIXELS_PER_PAGE_ =
+ 30 * goog.events.WheelEvent.PIXELS_PER_LINE_;
diff --git a/chromium/third_party/ink/closure/format/format.js b/chromium/third_party/ink/closure/format/format.js
new file mode 100644
index 00000000000..b9389050b65
--- /dev/null
+++ b/chromium/third_party/ink/closure/format/format.js
@@ -0,0 +1,503 @@
+// Copyright 2006 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Provides utility functions for formatting strings, numbers etc.
+ *
+ * @author pupius@google.com (Daniel Pupius)
+ */
+
+goog.provide('goog.format');
+
+goog.require('goog.i18n.GraphemeBreak');
+goog.require('goog.string');
+goog.require('goog.userAgent');
+
+
+/**
+ * Formats a number of bytes in human readable form.
+ * 54, 450K, 1.3M, 5G etc.
+ * @param {number} bytes The number of bytes to show.
+ * @param {number=} opt_decimals The number of decimals to use. Defaults to 2.
+ * @return {string} The human readable form of the byte size.
+ */
+goog.format.fileSize = function(bytes, opt_decimals) {
+ return goog.format.numBytesToString(bytes, opt_decimals, false);
+};
+
+
+/**
+ * Checks whether string value containing scaling units (K, M, G, T, P, m,
+ * u, n) can be converted to a number.
+ *
+ * Where there is a decimal, there must be a digit to the left of the
+ * decimal point.
+ *
+ * Negative numbers are valid.
+ *
+ * Examples:
+ * 0, 1, 1.0, 10.4K, 2.3M, -0.3P, 1.2m
+ *
+ * @param {string} val String value to check.
+ * @return {boolean} True if string could be converted to a numeric value.
+ */
+goog.format.isConvertableScaledNumber = function(val) {
+ return goog.format.SCALED_NUMERIC_RE_.test(val);
+};
+
+
+/**
+ * Converts a string to numeric value, taking into account the units.
+ * If string ends in 'B', use binary conversion.
+ * @param {string} stringValue String to be converted to numeric value.
+ * @return {number} Numeric value for string.
+ */
+goog.format.stringToNumericValue = function(stringValue) {
+ if (goog.string.endsWith(stringValue, 'B')) {
+ return goog.format.stringToNumericValue_(
+ stringValue, goog.format.NUMERIC_SCALES_BINARY_);
+ }
+ return goog.format.stringToNumericValue_(
+ stringValue, goog.format.NUMERIC_SCALES_SI_);
+};
+
+
+/**
+ * Converts a string to number of bytes, taking into account the units.
+ * Binary conversion.
+ * @param {string} stringValue String to be converted to numeric value.
+ * @return {number} Numeric value for string.
+ */
+goog.format.stringToNumBytes = function(stringValue) {
+ return goog.format.stringToNumericValue_(
+ stringValue, goog.format.NUMERIC_SCALES_BINARY_);
+};
+
+
+/**
+ * Converts a numeric value to string representation. SI conversion.
+ * @param {number} val Value to be converted.
+ * @param {number=} opt_decimals The number of decimals to use. Defaults to 2.
+ * @return {string} String representation of number.
+ */
+goog.format.numericValueToString = function(val, opt_decimals) {
+ return goog.format.numericValueToString_(
+ val, goog.format.NUMERIC_SCALES_SI_, opt_decimals);
+};
+
+
+/**
+ * Converts number of bytes to string representation. Binary conversion.
+ * Default is to return the additional 'B' suffix only for scales greater than
+ * 1K, e.g. '10.5KB' to minimize confusion with counts that are scaled by powers
+ * of 1000. Otherwise, suffix is empty string.
+ * @param {number} val Value to be converted.
+ * @param {number=} opt_decimals The number of decimals to use. Defaults to 2.
+ * @param {boolean=} opt_suffix If true, include trailing 'B' in returned
+ * string. Default is true.
+ * @param {boolean=} opt_useSeparator If true, number and scale will be
+ * separated by a no break space. Default is false.
+ * @return {string} String representation of number of bytes.
+ */
+goog.format.numBytesToString = function(
+ val, opt_decimals, opt_suffix, opt_useSeparator) {
+ var suffix = '';
+ if (!goog.isDef(opt_suffix) || opt_suffix) {
+ suffix = 'B';
+ }
+ return goog.format.numericValueToString_(
+ val, goog.format.NUMERIC_SCALES_BINARY_, opt_decimals, suffix,
+ opt_useSeparator);
+};
+
+
+/**
+ * Converts a string to numeric value, taking into account the units.
+ * @param {string} stringValue String to be converted to numeric value.
+ * @param {Object} conversion Dictionary of conversion scales.
+ * @return {number} Numeric value for string. If it cannot be converted,
+ * returns NaN.
+ * @private
+ */
+goog.format.stringToNumericValue_ = function(stringValue, conversion) {
+ var match = stringValue.match(goog.format.SCALED_NUMERIC_RE_);
+ if (!match) {
+ return NaN;
+ }
+ var val = Number(match[1]) * conversion[match[2]];
+ return val;
+};
+
+
+/**
+ * Converts a numeric value to string, using specified conversion
+ * scales.
+ * @param {number} val Value to be converted.
+ * @param {Object} conversion Dictionary of scaling factors.
+ * @param {number=} opt_decimals The number of decimals to use. Default is 2.
+ * @param {string=} opt_suffix Optional suffix to append.
+ * @param {boolean=} opt_useSeparator If true, number and scale will be
+ * separated by a space. Default is false.
+ * @return {string} The human readable form of the byte size.
+ * @private
+ */
+goog.format.numericValueToString_ = function(
+ val, conversion, opt_decimals, opt_suffix, opt_useSeparator) {
+ var prefixes = goog.format.NUMERIC_SCALE_PREFIXES_;
+ var orig_val = val;
+ var symbol = '';
+ var separator = '';
+ var scale = 1;
+ if (val < 0) {
+ val = -val;
+ }
+ for (var i = 0; i < prefixes.length; i++) {
+ var unit = prefixes[i];
+ scale = conversion[unit];
+ if (val >= scale || (scale <= 1 && val > 0.1 * scale)) {
+ // Treat values less than 1 differently, allowing 0.5 to be "0.5" rather
+ // than "500m"
+ symbol = unit;
+ break;
+ }
+ }
+ if (!symbol) {
+ scale = 1;
+ } else {
+ if (opt_suffix) {
+ symbol += opt_suffix;
+ }
+ if (opt_useSeparator) {
+ separator = ' ';
+ }
+ }
+ var ex = Math.pow(10, goog.isDef(opt_decimals) ? opt_decimals : 2);
+ return Math.round(orig_val / scale * ex) / ex + separator + symbol;
+};
+
+
+/**
+ * Regular expression for detecting scaling units, such as K, M, G, etc. for
+ * converting a string representation to a numeric value.
+ *
+ * Also allow 'k' to be aliased to 'K'. These could be used for SI (powers
+ * of 1000) or Binary (powers of 1024) conversions.
+ *
+ * Also allow final 'B' to be interpreted as byte-count, implicitly triggering
+ * binary conversion (e.g., '10.2MB').
+ *
+ * @type {RegExp}
+ * @private
+ */
+goog.format.SCALED_NUMERIC_RE_ =
+ /^([-]?\d+\.?\d*)([K,M,G,T,P,E,Z,Y,k,m,u,n]?)[B]?$/;
+
+
+/**
+ * Ordered list of scaling prefixes in decreasing order.
+ * @private {Array<string>}
+ */
+goog.format.NUMERIC_SCALE_PREFIXES_ =
+ ['Y', 'Z', 'E', 'P', 'T', 'G', 'M', 'K', '', 'm', 'u', 'n'];
+
+
+/**
+ * Scaling factors for conversion of numeric value to string. SI conversion.
+ * @type {Object}
+ * @private
+ */
+goog.format.NUMERIC_SCALES_SI_ = {
+ '': 1,
+ 'n': 1e-9,
+ 'u': 1e-6,
+ 'm': 1e-3,
+ 'k': 1e3,
+ 'K': 1e3,
+ 'M': 1e6,
+ 'G': 1e9,
+ 'T': 1e12,
+ 'P': 1e15,
+ 'E': 1e18,
+ 'Z': 1e21,
+ 'Y': 1e24
+};
+
+
+/**
+ * Scaling factors for conversion of numeric value to string. Binary
+ * conversion.
+ * @type {Object}
+ * @private
+ */
+goog.format.NUMERIC_SCALES_BINARY_ = {
+ '': 1,
+ 'n': Math.pow(1024, -3),
+ 'u': Math.pow(1024, -2),
+ 'm': 1.0 / 1024,
+ 'k': 1024,
+ 'K': 1024,
+ 'M': Math.pow(1024, 2),
+ 'G': Math.pow(1024, 3),
+ 'T': Math.pow(1024, 4),
+ 'P': Math.pow(1024, 5),
+ 'E': Math.pow(1024, 6),
+ 'Z': Math.pow(1024, 7),
+ 'Y': Math.pow(1024, 8)
+};
+
+
+/**
+ * First Unicode code point that has the Mark property.
+ * @type {number}
+ * @private
+ */
+goog.format.FIRST_GRAPHEME_EXTEND_ = 0x300;
+
+
+/**
+ * Returns true if and only if given character should be treated as a breaking
+ * space. All ASCII control characters, the main Unicode range of spacing
+ * characters (U+2000 to U+200B inclusive except for U+2007), and several other
+ * Unicode space characters are treated as breaking spaces.
+ * @param {number} charCode The character code under consideration.
+ * @return {boolean} True if the character is a breaking space.
+ * @private
+ */
+goog.format.isTreatedAsBreakingSpace_ = function(charCode) {
+ return (charCode <= goog.format.WbrToken_.SPACE) ||
+ (charCode >= 0x1000 &&
+ ((charCode >= 0x2000 && charCode <= 0x2006) ||
+ (charCode >= 0x2008 && charCode <= 0x200B) || charCode == 0x1680 ||
+ charCode == 0x180E || charCode == 0x2028 || charCode == 0x2029 ||
+ charCode == 0x205f || charCode == 0x3000));
+};
+
+
+/**
+ * Returns true if and only if given character is an invisible formatting
+ * character.
+ * @param {number} charCode The character code under consideration.
+ * @return {boolean} True if the character is an invisible formatting character.
+ * @private
+ */
+goog.format.isInvisibleFormattingCharacter_ = function(charCode) {
+ // See: http://unicode.org/charts/PDF/U2000.pdf
+ return (charCode >= 0x200C && charCode <= 0x200F) ||
+ (charCode >= 0x202A && charCode <= 0x202E);
+};
+
+
+/**
+ * Inserts word breaks into an HTML string at a given interval. The counter is
+ * reset if a space or a character which behaves like a space is encountered,
+ * but it isn't incremented if an invisible formatting character is encountered.
+ * WBRs aren't inserted into HTML tags or entities. Entities count towards the
+ * character count, HTML tags do not.
+ *
+ * With common strings aliased, objects allocations are constant based on the
+ * length of the string: N + 3. This guarantee does not hold if the string
+ * contains an element >= U+0300 and hasGraphemeBreak is non-trivial.
+ *
+ * @param {string} str HTML to insert word breaks into.
+ * @param {function(number, number, boolean): boolean} hasGraphemeBreak A
+ * function determining if there is a grapheme break between two characters,
+ * in the same signature as goog.i18n.GraphemeBreak.hasGraphemeBreak.
+ * @param {number=} opt_maxlen Maximum length after which to ensure
+ * there is a break. Default is 10 characters.
+ * @return {string} The string including word breaks.
+ * @private
+ */
+goog.format.insertWordBreaksGeneric_ = function(
+ str, hasGraphemeBreak, opt_maxlen) {
+ var maxlen = opt_maxlen || 10;
+ if (maxlen > str.length) return str;
+
+ var rv = [];
+ var n = 0; // The length of the current token
+
+ // This will contain the ampersand or less-than character if one of the
+ // two has been seen; otherwise, the value is zero.
+ var nestingCharCode = 0;
+
+ // First character position from input string that has not been outputted.
+ var lastDumpPosition = 0;
+
+ var charCode = 0;
+ for (var i = 0; i < str.length; i++) {
+ // Using charCodeAt versus charAt avoids allocating new string objects.
+ var lastCharCode = charCode;
+ charCode = str.charCodeAt(i);
+
+ // Don't add a WBR before characters that might be grapheme extending.
+ var isPotentiallyGraphemeExtending =
+ charCode >= goog.format.FIRST_GRAPHEME_EXTEND_ &&
+ !hasGraphemeBreak(lastCharCode, charCode, true);
+
+ // Don't add a WBR at the end of a word. For the purposes of determining
+ // work breaks, all ASCII control characters and some commonly encountered
+ // Unicode spacing characters are treated as breaking spaces.
+ if (n >= maxlen && !goog.format.isTreatedAsBreakingSpace_(charCode) &&
+ !isPotentiallyGraphemeExtending) {
+ // Flush everything seen so far, and append a word break.
+ rv.push(str.substring(lastDumpPosition, i), goog.format.WORD_BREAK_HTML);
+ lastDumpPosition = i;
+ n = 0;
+ }
+
+ if (!nestingCharCode) {
+ // Not currently within an HTML tag or entity
+
+ if (charCode == goog.format.WbrToken_.LT ||
+ charCode == goog.format.WbrToken_.AMP) {
+ // Entering an HTML Entity '&' or open tag '<'
+ nestingCharCode = charCode;
+ } else if (goog.format.isTreatedAsBreakingSpace_(charCode)) {
+ // A space or control character -- reset the token length
+ n = 0;
+ } else if (!goog.format.isInvisibleFormattingCharacter_(charCode)) {
+ // A normal flow character - increment. For grapheme extending
+ // characters, this is not *technically* a new character. However,
+ // since the grapheme break detector might be overly conservative,
+ // we have to continue incrementing, or else we won't even be able
+ // to add breaks when we get to things like punctuation. For the
+ // case where we have a full grapheme break detector, it is okay if
+ // we occasionally break slightly early.
+ n++;
+ }
+ } else if (
+ charCode == goog.format.WbrToken_.GT &&
+ nestingCharCode == goog.format.WbrToken_.LT) {
+ // Leaving an HTML tag, treat the tag as zero-length
+ nestingCharCode = 0;
+ } else if (
+ charCode == goog.format.WbrToken_.SEMI_COLON &&
+ nestingCharCode == goog.format.WbrToken_.AMP) {
+ // Leaving an HTML entity, treat it as length one
+ nestingCharCode = 0;
+ n++;
+ }
+ }
+
+ // Take care of anything we haven't flushed so far.
+ rv.push(str.substr(lastDumpPosition));
+
+ return rv.join('');
+};
+
+
+/**
+ * Inserts word breaks into an HTML string at a given interval.
+ *
+ * This method is as aggressive as possible, using a full table of Unicode
+ * characters where it is legal to insert word breaks; however, this table
+ * comes at a 2.5k pre-gzip (~1k post-gzip) size cost. Consider using
+ * insertWordBreaksBasic to minimize the size impact.
+ *
+ * @param {string} str HTML to insert word breaks into.
+ * @param {number=} opt_maxlen Maximum length after which to ensure there is a
+ * break. Default is 10 characters.
+ * @return {string} The string including word breaks.
+ * @deprecated Prefer wrapping with CSS word-wrap: break-word.
+ */
+goog.format.insertWordBreaks = function(str, opt_maxlen) {
+ return goog.format.insertWordBreaksGeneric_(
+ str, goog.i18n.GraphemeBreak.hasGraphemeBreak, opt_maxlen);
+};
+
+
+/**
+ * Determines conservatively if a character has a Grapheme break.
+ *
+ * Conforms to a similar signature as goog.i18n.GraphemeBreak, but is overly
+ * conservative, returning true only for characters in common scripts that
+ * are simple to account for.
+ *
+ * @param {number} lastCharCode The previous character code. Ignored.
+ * @param {number} charCode The character code under consideration. It must be
+ * at least \u0300 as a precondition -- this case is covered by
+ * insertWordBreaksGeneric_.
+ * @param {boolean=} opt_extended Ignored, to conform with the interface.
+ * @return {boolean} Whether it is one of the recognized subsets of characters
+ * with a grapheme break.
+ * @private
+ */
+goog.format.conservativelyHasGraphemeBreak_ = function(
+ lastCharCode, charCode, opt_extended) {
+ // Return false for everything except the most common Cyrillic characters.
+ // Don't worry about Latin characters, because insertWordBreaksGeneric_
+ // itself already handles those.
+ // TODO(gboyer): Also account for Greek, Armenian, and Georgian if it is
+ // simple to do so.
+ return charCode >= 0x400 && charCode < 0x523;
+};
+
+
+// TODO(gboyer): Consider using a compile-time flag to switch implementations
+// rather than relying on the developers to toggle implementations.
+/**
+ * Inserts word breaks into an HTML string at a given interval.
+ *
+ * This method is less aggressive than insertWordBreaks, only inserting
+ * breaks next to punctuation and between Latin or Cyrillic characters.
+ * However, this is good enough for the common case of URLs. It also
+ * works for all Latin and Cyrillic languages, plus CJK has no need for word
+ * breaks. When this method is used, goog.i18n.GraphemeBreak may be dead
+ * code eliminated.
+ *
+ * @param {string} str HTML to insert word breaks into.
+ * @param {number=} opt_maxlen Maximum length after which to ensure there is a
+ * break. Default is 10 characters.
+ * @return {string} The string including word breaks.
+ * @deprecated Prefer wrapping with CSS word-wrap: break-word.
+ */
+goog.format.insertWordBreaksBasic = function(str, opt_maxlen) {
+ return goog.format.insertWordBreaksGeneric_(
+ str, goog.format.conservativelyHasGraphemeBreak_, opt_maxlen);
+};
+
+
+/**
+ * True iff the current userAgent is IE8 or above.
+ * @type {boolean}
+ * @private
+ */
+goog.format.IS_IE8_OR_ABOVE_ =
+ goog.userAgent.IE && goog.userAgent.isVersionOrHigher(8);
+
+
+/**
+ * Constant for the WBR replacement used by insertWordBreaks. Safari requires
+ * <wbr></wbr>, Opera needs the &shy; entity, though this will give a visible
+ * hyphen at breaks. IE8 uses a zero width space.
+ * Other browsers just use <wbr>.
+ * @type {string}
+ */
+goog.format.WORD_BREAK_HTML =
+ goog.userAgent.WEBKIT ? '<wbr></wbr>' : goog.userAgent.OPERA ?
+ '&shy;' :
+ goog.format.IS_IE8_OR_ABOVE_ ? '&#8203;' : '<wbr>';
+
+
+/**
+ * Tokens used within insertWordBreaks.
+ * @private
+ * @enum {number}
+ */
+goog.format.WbrToken_ = {
+ LT: 60, // '<'.charCodeAt(0)
+ GT: 62, // '>'.charCodeAt(0)
+ AMP: 38, // '&'.charCodeAt(0)
+ SEMI_COLON: 59, // ';'.charCodeAt(0)
+ SPACE: 32 // ' '.charCodeAt(0)
+};
diff --git a/chromium/third_party/ink/closure/fs/url.js b/chromium/third_party/ink/closure/fs/url.js
new file mode 100644
index 00000000000..1204908ffd3
--- /dev/null
+++ b/chromium/third_party/ink/closure/fs/url.js
@@ -0,0 +1,106 @@
+// Copyright 2015 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Wrapper for URL and its createObjectUrl and revokeObjectUrl
+ * methods that are part of the HTML5 File API.
+ */
+
+goog.provide('goog.fs.url');
+
+
+/**
+ * Creates a blob URL for a blob object.
+ * Throws an error if the browser does not support Object Urls.
+ *
+ * @param {!Blob} blob The object for which to create the URL.
+ * @return {string} The URL for the object.
+ */
+goog.fs.url.createObjectUrl = function(blob) {
+ return goog.fs.url.getUrlObject_().createObjectURL(blob);
+};
+
+
+/**
+ * Revokes a URL created by {@link goog.fs.url.createObjectUrl}.
+ * Throws an error if the browser does not support Object Urls.
+ *
+ * @param {string} url The URL to revoke.
+ */
+goog.fs.url.revokeObjectUrl = function(url) {
+ goog.fs.url.getUrlObject_().revokeObjectURL(url);
+};
+
+
+/**
+ * @typedef {{createObjectURL: (function(!Blob): string),
+ * revokeObjectURL: function(string): void}}
+ */
+goog.fs.url.UrlObject_;
+
+
+/**
+ * Get the object that has the createObjectURL and revokeObjectURL functions for
+ * this browser.
+ *
+ * @return {goog.fs.url.UrlObject_} The object for this browser.
+ * @private
+ */
+goog.fs.url.getUrlObject_ = function() {
+ var urlObject = goog.fs.url.findUrlObject_();
+ if (urlObject != null) {
+ return urlObject;
+ } else {
+ throw new Error('This browser doesn\'t seem to support blob URLs');
+ }
+};
+
+
+/**
+ * Finds the object that has the createObjectURL and revokeObjectURL functions
+ * for this browser.
+ *
+ * @return {?goog.fs.url.UrlObject_} The object for this browser or null if the
+ * browser does not support Object Urls.
+ * @private
+ */
+goog.fs.url.findUrlObject_ = function() {
+ // This is what the spec says to do
+ // http://dev.w3.org/2006/webapi/FileAPI/#dfn-createObjectURL
+ if (goog.isDef(goog.global.URL) &&
+ goog.isDef(goog.global.URL.createObjectURL)) {
+ return /** @type {goog.fs.url.UrlObject_} */ (goog.global.URL);
+ // This is what Chrome does (as of 10.0.648.6 dev)
+ } else if (
+ goog.isDef(goog.global.webkitURL) &&
+ goog.isDef(goog.global.webkitURL.createObjectURL)) {
+ return /** @type {goog.fs.url.UrlObject_} */ (goog.global.webkitURL);
+ // This is what the spec used to say to do
+ } else if (goog.isDef(goog.global.createObjectURL)) {
+ return /** @type {goog.fs.url.UrlObject_} */ (goog.global);
+ } else {
+ return null;
+ }
+};
+
+
+/**
+ * Checks whether this browser supports Object Urls. If not, calls to
+ * createObjectUrl and revokeObjectUrl will result in an error.
+ *
+ * @return {boolean} True if this browser supports Object Urls.
+ */
+goog.fs.url.browserSupportsObjectUrls = function() {
+ return goog.fs.url.findUrlObject_() != null;
+};
diff --git a/chromium/third_party/ink/closure/functions/functions.js b/chromium/third_party/ink/closure/functions/functions.js
new file mode 100644
index 00000000000..f7358885e74
--- /dev/null
+++ b/chromium/third_party/ink/closure/functions/functions.js
@@ -0,0 +1,485 @@
+// Copyright 2008 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Utilities for creating functions. Loosely inspired by the
+ * java classes: http://goo.gl/GM0Hmu and http://goo.gl/6k7nI8.
+ *
+ * @author nicksantos@google.com (Nick Santos)
+ */
+
+
+goog.provide('goog.functions');
+
+
+/**
+ * Creates a function that always returns the same value.
+ * @param {T} retValue The value to return.
+ * @return {function():T} The new function.
+ * @template T
+ */
+goog.functions.constant = function(retValue) {
+ return function() { return retValue; };
+};
+
+
+/**
+ * Always returns false.
+ * @type {function(...): boolean}
+ */
+goog.functions.FALSE = goog.functions.constant(false);
+
+
+/**
+ * Always returns true.
+ * @type {function(...): boolean}
+ */
+goog.functions.TRUE = goog.functions.constant(true);
+
+
+/**
+ * Always returns NULL.
+ * @type {function(...): null}
+ */
+goog.functions.NULL = goog.functions.constant(null);
+
+
+/**
+ * A simple function that returns the first argument of whatever is passed
+ * into it.
+ * @param {T=} opt_returnValue The single value that will be returned.
+ * @param {...*} var_args Optional trailing arguments. These are ignored.
+ * @return {T} The first argument passed in, or undefined if nothing was passed.
+ * @template T
+ */
+goog.functions.identity = function(opt_returnValue, var_args) {
+ return opt_returnValue;
+};
+
+
+/**
+ * Creates a function that always throws an error with the given message.
+ * @param {string} message The error message.
+ * @return {!Function} The error-throwing function.
+ */
+goog.functions.error = function(message) {
+ return function() {
+ throw new Error(message);
+ };
+};
+
+
+/**
+ * Creates a function that throws the given object.
+ * @param {*} err An object to be thrown.
+ * @return {!Function} The error-throwing function.
+ */
+goog.functions.fail = function(err) {
+ return function() { throw err; };
+};
+
+
+/**
+ * Given a function, create a function that keeps opt_numArgs arguments and
+ * silently discards all additional arguments.
+ * @param {Function} f The original function.
+ * @param {number=} opt_numArgs The number of arguments to keep. Defaults to 0.
+ * @return {!Function} A version of f that only keeps the first opt_numArgs
+ * arguments.
+ */
+goog.functions.lock = function(f, opt_numArgs) {
+ opt_numArgs = opt_numArgs || 0;
+ return function() {
+ return f.apply(this, Array.prototype.slice.call(arguments, 0, opt_numArgs));
+ };
+};
+
+
+/**
+ * Creates a function that returns its nth argument.
+ * @param {number} n The position of the return argument.
+ * @return {!Function} A new function.
+ */
+goog.functions.nth = function(n) {
+ return function() { return arguments[n]; };
+};
+
+
+/**
+ * Like goog.partial(), except that arguments are added after arguments to the
+ * returned function.
+ *
+ * Usage:
+ * function f(arg1, arg2, arg3, arg4) { ... }
+ * var g = goog.functions.partialRight(f, arg3, arg4);
+ * g(arg1, arg2);
+ *
+ * @param {!Function} fn A function to partially apply.
+ * @param {...*} var_args Additional arguments that are partially applied to fn
+ * at the end.
+ * @return {!Function} A partially-applied form of the function goog.partial()
+ * was invoked as a method of.
+ */
+goog.functions.partialRight = function(fn, var_args) {
+ var rightArgs = Array.prototype.slice.call(arguments, 1);
+ return function() {
+ var newArgs = Array.prototype.slice.call(arguments);
+ newArgs.push.apply(newArgs, rightArgs);
+ return fn.apply(this, newArgs);
+ };
+};
+
+
+/**
+ * Given a function, create a new function that swallows its return value
+ * and replaces it with a new one.
+ * @param {Function} f A function.
+ * @param {T} retValue A new return value.
+ * @return {function(...?):T} A new function.
+ * @template T
+ */
+goog.functions.withReturnValue = function(f, retValue) {
+ return goog.functions.sequence(f, goog.functions.constant(retValue));
+};
+
+
+/**
+ * Creates a function that returns whether its argument equals the given value.
+ *
+ * Example:
+ * var key = goog.object.findKey(obj, goog.functions.equalTo('needle'));
+ *
+ * @param {*} value The value to compare to.
+ * @param {boolean=} opt_useLooseComparison Whether to use a loose (==)
+ * comparison rather than a strict (===) one. Defaults to false.
+ * @return {function(*):boolean} The new function.
+ */
+goog.functions.equalTo = function(value, opt_useLooseComparison) {
+ return function(other) {
+ return opt_useLooseComparison ? (value == other) : (value === other);
+ };
+};
+
+
+/**
+ * Creates the composition of the functions passed in.
+ * For example, (goog.functions.compose(f, g))(a) is equivalent to f(g(a)).
+ * @param {function(...?):T} fn The final function.
+ * @param {...Function} var_args A list of functions.
+ * @return {function(...?):T} The composition of all inputs.
+ * @template T
+ */
+goog.functions.compose = function(fn, var_args) {
+ var functions = arguments;
+ var length = functions.length;
+ return function() {
+ var result;
+ if (length) {
+ result = functions[length - 1].apply(this, arguments);
+ }
+
+ for (var i = length - 2; i >= 0; i--) {
+ result = functions[i].call(this, result);
+ }
+ return result;
+ };
+};
+
+
+/**
+ * Creates a function that calls the functions passed in in sequence, and
+ * returns the value of the last function. For example,
+ * (goog.functions.sequence(f, g))(x) is equivalent to f(x),g(x).
+ * @param {...Function} var_args A list of functions.
+ * @return {!Function} A function that calls all inputs in sequence.
+ */
+goog.functions.sequence = function(var_args) {
+ var functions = arguments;
+ var length = functions.length;
+ return function() {
+ var result;
+ for (var i = 0; i < length; i++) {
+ result = functions[i].apply(this, arguments);
+ }
+ return result;
+ };
+};
+
+
+/**
+ * Creates a function that returns true if each of its components evaluates
+ * to true. The components are evaluated in order, and the evaluation will be
+ * short-circuited as soon as a function returns false.
+ * For example, (goog.functions.and(f, g))(x) is equivalent to f(x) && g(x).
+ * @param {...Function} var_args A list of functions.
+ * @return {function(...?):boolean} A function that ANDs its component
+ * functions.
+ */
+goog.functions.and = function(var_args) {
+ var functions = arguments;
+ var length = functions.length;
+ return function() {
+ for (var i = 0; i < length; i++) {
+ if (!functions[i].apply(this, arguments)) {
+ return false;
+ }
+ }
+ return true;
+ };
+};
+
+
+/**
+ * Creates a function that returns true if any of its components evaluates
+ * to true. The components are evaluated in order, and the evaluation will be
+ * short-circuited as soon as a function returns true.
+ * For example, (goog.functions.or(f, g))(x) is equivalent to f(x) || g(x).
+ * @param {...Function} var_args A list of functions.
+ * @return {function(...?):boolean} A function that ORs its component
+ * functions.
+ */
+goog.functions.or = function(var_args) {
+ var functions = arguments;
+ var length = functions.length;
+ return function() {
+ for (var i = 0; i < length; i++) {
+ if (functions[i].apply(this, arguments)) {
+ return true;
+ }
+ }
+ return false;
+ };
+};
+
+
+/**
+ * Creates a function that returns the Boolean opposite of a provided function.
+ * For example, (goog.functions.not(f))(x) is equivalent to !f(x).
+ * @param {!Function} f The original function.
+ * @return {function(...?):boolean} A function that delegates to f and returns
+ * opposite.
+ */
+goog.functions.not = function(f) {
+ return function() { return !f.apply(this, arguments); };
+};
+
+
+/**
+ * Generic factory function to construct an object given the constructor
+ * and the arguments. Intended to be bound to create object factories.
+ *
+ * Example:
+ *
+ * var factory = goog.partial(goog.functions.create, Class);
+ *
+ * @param {function(new:T, ...)} constructor The constructor for the Object.
+ * @param {...*} var_args The arguments to be passed to the constructor.
+ * @return {T} A new instance of the class given in {@code constructor}.
+ * @template T
+ */
+goog.functions.create = function(constructor, var_args) {
+ /**
+ * @constructor
+ * @final
+ */
+ var temp = function() {};
+ temp.prototype = constructor.prototype;
+
+ // obj will have constructor's prototype in its chain and
+ // 'obj instanceof constructor' will be true.
+ var obj = new temp();
+
+ // obj is initialized by constructor.
+ // arguments is only array-like so lacks shift(), but can be used with
+ // the Array prototype function.
+ constructor.apply(obj, Array.prototype.slice.call(arguments, 1));
+ return obj;
+};
+
+
+/**
+ * @define {boolean} Whether the return value cache should be used.
+ * This should only be used to disable caches when testing.
+ */
+goog.define('goog.functions.CACHE_RETURN_VALUE', true);
+
+
+/**
+ * Gives a wrapper function that caches the return value of a parameterless
+ * function when first called.
+ *
+ * When called for the first time, the given function is called and its
+ * return value is cached (thus this is only appropriate for idempotent
+ * functions). Subsequent calls will return the cached return value. This
+ * allows the evaluation of expensive functions to be delayed until first used.
+ *
+ * To cache the return values of functions with parameters, see goog.memoize.
+ *
+ * @param {function():T} fn A function to lazily evaluate.
+ * @return {function():T} A wrapped version the function.
+ * @template T
+ */
+goog.functions.cacheReturnValue = function(fn) {
+ var called = false;
+ var value;
+
+ return function() {
+ if (!goog.functions.CACHE_RETURN_VALUE) {
+ return fn();
+ }
+
+ if (!called) {
+ value = fn();
+ called = true;
+ }
+
+ return value;
+ };
+};
+
+
+/**
+ * Wraps a function to allow it to be called, at most, once. All
+ * additional calls are no-ops.
+ *
+ * This is particularly useful for initialization functions
+ * that should be called, at most, once.
+ *
+ * @param {function():*} f Function to call.
+ * @return {function():undefined} Wrapped function.
+ */
+goog.functions.once = function(f) {
+ // Keep a reference to the function that we null out when we're done with
+ // it -- that way, the function can be GC'd when we're done with it.
+ var inner = f;
+ return function() {
+ if (inner) {
+ var tmp = inner;
+ inner = null;
+ tmp();
+ }
+ };
+};
+
+
+/**
+ * Wraps a function to allow it to be called, at most, once per interval
+ * (specified in milliseconds). If the wrapper function is called N times within
+ * that interval, only the Nth call will go through.
+ *
+ * This is particularly useful for batching up repeated actions where the
+ * last action should win. This can be used, for example, for refreshing an
+ * autocomplete pop-up every so often rather than updating with every keystroke,
+ * since the final text typed by the user is the one that should produce the
+ * final autocomplete results. For more stateful debouncing with support for
+ * pausing, resuming, and canceling debounced actions, use {@code
+ * goog.async.Debouncer}.
+ *
+ * @param {function(this:SCOPE, ...?)} f Function to call.
+ * @param {number} interval Interval over which to debounce. The function will
+ * only be called after the full interval has elapsed since the last call.
+ * @param {SCOPE=} opt_scope Object in whose scope to call the function.
+ * @return {function(...?): undefined} Wrapped function.
+ * @template SCOPE
+ */
+goog.functions.debounce = function(f, interval, opt_scope) {
+ var timeout = 0;
+ return /** @type {function(...?)} */ (function(var_args) {
+ goog.global.clearTimeout(timeout);
+ var args = arguments;
+ timeout = goog.global.setTimeout(function() {
+ f.apply(opt_scope, args);
+ }, interval);
+ });
+};
+
+
+/**
+ * Wraps a function to allow it to be called, at most, once per interval
+ * (specified in milliseconds). If the wrapper function is called N times in
+ * that interval, both the 1st and the Nth calls will go through.
+ *
+ * This is particularly useful for limiting repeated user requests where the
+ * the last action should win, but you also don't want to wait until the end of
+ * the interval before sending a request out, as it leads to a perception of
+ * slowness for the user.
+ *
+ * @param {function(this:SCOPE, ...?)} f Function to call.
+ * @param {number} interval Interval over which to throttle. The function can
+ * only be called once per interval.
+ * @param {SCOPE=} opt_scope Object in whose scope to call the function.
+ * @return {function(...?): undefined} Wrapped function.
+ * @template SCOPE
+ */
+goog.functions.throttle = function(f, interval, opt_scope) {
+ var timeout = 0;
+ var shouldFire = false;
+ var args = [];
+
+ var handleTimeout = function() {
+ timeout = 0;
+ if (shouldFire) {
+ shouldFire = false;
+ fire();
+ }
+ };
+
+ var fire = function() {
+ timeout = goog.global.setTimeout(handleTimeout, interval);
+ f.apply(opt_scope, args);
+ };
+
+ return /** @type {function(...?)} */ (function(var_args) {
+ args = arguments;
+ if (!timeout) {
+ fire();
+ } else {
+ shouldFire = true;
+ }
+ });
+};
+
+
+/**
+ * Wraps a function to allow it to be called, at most, once per interval
+ * (specified in milliseconds). If the wrapper function is called N times within
+ * that interval, only the 1st call will go through.
+ *
+ * This is particularly useful for limiting repeated user requests where the
+ * first request is guaranteed to have all the data required to perform the
+ * final action, so there's no need to wait until the end of the interval before
+ * sending the request out.
+ *
+ * @param {function(this:SCOPE, ...?)} f Function to call.
+ * @param {number} interval Interval over which to rate-limit. The function will
+ * only be called once per interval, and ignored for the remainer of the
+ * interval.
+ * @param {SCOPE=} opt_scope Object in whose scope to call the function.
+ * @return {function(...?): undefined} Wrapped function.
+ * @template SCOPE
+ */
+goog.functions.rateLimit = function(f, interval, opt_scope) {
+ var timeout = 0;
+
+ var handleTimeout = function() {
+ timeout = 0;
+ };
+
+ return /** @type {function(...?)} */ (function(var_args) {
+ if (!timeout) {
+ timeout = goog.global.setTimeout(handleTimeout, interval);
+ f.apply(opt_scope, arguments);
+ }
+ });
+};
diff --git a/chromium/third_party/ink/closure/html/legacyconversions.js b/chromium/third_party/ink/closure/html/legacyconversions.js
new file mode 100644
index 00000000000..0148977ac62
--- /dev/null
+++ b/chromium/third_party/ink/closure/html/legacyconversions.js
@@ -0,0 +1,204 @@
+// Copyright 2013 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Transitional utilities to unsafely trust random strings as
+ * goog.html types. Intended for temporary use when upgrading a library that
+ * used to accept plain strings to use safe types, but where it's not
+ * practical to transitively update callers.
+ *
+ * IMPORTANT: No new code should use the conversion functions in this file,
+ * they are intended for refactoring old code to use goog.html types. New code
+ * should construct goog.html types via their APIs, template systems or
+ * sanitizers. If that’s not possible it should use
+ * goog.html.uncheckedconversions and undergo security review.
+
+ * MOE:begin_intracomment_strip
+ * At Google goog.html.legacyconversions are restricted via both BUILD
+ * visibility and Conformance rules. The goal is to allow us to progressively
+ * get rid of using strings to represent HTML-related data which is passed to
+ * DOM APIs that execute script (like innerHTML or Anchor.href), while avoiding
+ * regressions. Please carefully read the documentation below before using
+ * these functions. If you have questions contact ise-hardening@ and we’ll
+ * gladly help.
+ * MOE:end_intracomment_strip
+ *
+ * The semantics of the conversions in goog.html.legacyconversions are very
+ * different from the ones provided by goog.html.uncheckedconversions. The
+ * latter are for use in code where it has been established through manual
+ * security review that the value produced by a piece of code will always
+ * satisfy the SafeHtml contract (e.g., the output of a secure HTML sanitizer).
+ * In uses of goog.html.legacyconversions, this guarantee is not given -- the
+ * value in question originates in unreviewed legacy code and there is no
+ * guarantee that it satisfies the SafeHtml contract.
+ *
+ * There are only three valid uses of legacyconversions:
+ *
+ * 1. Introducing a goog.html version of a function which currently consumes
+ * string and passes that string to a DOM API which can execute script - and
+ * hence cause XSS - like innerHTML. For example, Dialog might expose a
+ * setContent method which takes a string and sets the innerHTML property of
+ * an element with it. In this case a setSafeHtmlContent function could be
+ * added, consuming goog.html.SafeHtml instead of string, and using
+ * goog.dom.safe.setInnerHtml instead of directly setting innerHTML.
+ * setContent could then internally use legacyconversions to create a SafeHtml
+ * from string and pass the SafeHtml to setSafeHtmlContent. In this scenario
+ * remember to document the use of legacyconversions in the modified setContent
+ * and consider deprecating it as well.
+ *
+ * 2. Automated refactoring of application code which handles HTML as string
+ * but needs to call a function which only takes goog.html types. For example,
+ * in the Dialog scenario from (1) an alternative option would be to refactor
+ * setContent to accept goog.html.SafeHtml instead of string and then refactor
+ * all current callers to use legacyconversions to pass SafeHtml. This is
+ * generally preferable to (1) because it keeps the library clean of
+ * legacyconversions, and makes code sites in application code that are
+ * potentially vulnerable to XSS more apparent.
+ *
+ * 3. Old code which needs to call APIs which consume goog.html types and for
+ * which it is prohibitively expensive to refactor to use goog.html types.
+ * Generally, this is code where safety from XSS is either hopeless or
+ * unimportant.
+ *
+ * @visibility {//javascript/closure/html:approved_for_legacy_conversion}
+ * @visibility {//javascript/closure/bin/sizetests:__pkg__}
+ */
+
+
+goog.provide('goog.html.legacyconversions');
+
+goog.require('goog.html.SafeHtml');
+goog.require('goog.html.SafeScript');
+goog.require('goog.html.SafeStyle');
+goog.require('goog.html.SafeStyleSheet');
+goog.require('goog.html.SafeUrl');
+goog.require('goog.html.TrustedResourceUrl');
+
+
+/**
+ * Performs an "unchecked conversion" from string to SafeHtml for legacy API
+ * purposes.
+ *
+ * Please read fileoverview documentation before using.
+ *
+ * @param {string} html A string to be converted to SafeHtml.
+ * @return {!goog.html.SafeHtml} The value of html, wrapped in a SafeHtml
+ * object.
+ */
+goog.html.legacyconversions.safeHtmlFromString = function(html) {
+ goog.html.legacyconversions.reportCallback_();
+ return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
+ html, null /* dir */);
+};
+
+
+/**
+ * Performs an "unchecked conversion" from string to SafeScript for legacy API
+ * purposes.
+ *
+ * Please read fileoverview documentation before using.
+ *
+ * @param {string} script A string to be converted to SafeScript.
+ * @return {!goog.html.SafeScript} The value of script, wrapped in a SafeScript
+ * object.
+ */
+goog.html.legacyconversions.safeScriptFromString = function(script) {
+ goog.html.legacyconversions.reportCallback_();
+ return goog.html.SafeScript.createSafeScriptSecurityPrivateDoNotAccessOrElse(
+ script);
+};
+
+
+/**
+ * Performs an "unchecked conversion" from string to SafeStyle for legacy API
+ * purposes.
+ *
+ * Please read fileoverview documentation before using.
+ *
+ * @param {string} style A string to be converted to SafeStyle.
+ * @return {!goog.html.SafeStyle} The value of style, wrapped in a SafeStyle
+ * object.
+ */
+goog.html.legacyconversions.safeStyleFromString = function(style) {
+ goog.html.legacyconversions.reportCallback_();
+ return goog.html.SafeStyle.createSafeStyleSecurityPrivateDoNotAccessOrElse(
+ style);
+};
+
+
+/**
+ * Performs an "unchecked conversion" from string to SafeStyleSheet for legacy
+ * API purposes.
+ *
+ * Please read fileoverview documentation before using.
+ *
+ * @param {string} styleSheet A string to be converted to SafeStyleSheet.
+ * @return {!goog.html.SafeStyleSheet} The value of style sheet, wrapped in
+ * a SafeStyleSheet object.
+ */
+goog.html.legacyconversions.safeStyleSheetFromString = function(styleSheet) {
+ goog.html.legacyconversions.reportCallback_();
+ return goog.html.SafeStyleSheet
+ .createSafeStyleSheetSecurityPrivateDoNotAccessOrElse(styleSheet);
+};
+
+
+/**
+ * Performs an "unchecked conversion" from string to SafeUrl for legacy API
+ * purposes.
+ *
+ * Please read fileoverview documentation before using.
+ *
+ * @param {string} url A string to be converted to SafeUrl.
+ * @return {!goog.html.SafeUrl} The value of url, wrapped in a SafeUrl
+ * object.
+ */
+goog.html.legacyconversions.safeUrlFromString = function(url) {
+ goog.html.legacyconversions.reportCallback_();
+ return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url);
+};
+
+
+/**
+ * Performs an "unchecked conversion" from string to TrustedResourceUrl for
+ * legacy API purposes.
+ *
+ * Please read fileoverview documentation before using.
+ *
+ * @param {string} url A string to be converted to TrustedResourceUrl.
+ * @return {!goog.html.TrustedResourceUrl} The value of url, wrapped in a
+ * TrustedResourceUrl object.
+ */
+goog.html.legacyconversions.trustedResourceUrlFromString = function(url) {
+ goog.html.legacyconversions.reportCallback_();
+ return goog.html.TrustedResourceUrl
+ .createTrustedResourceUrlSecurityPrivateDoNotAccessOrElse(url);
+};
+
+/**
+ * @private {function(): undefined}
+ */
+goog.html.legacyconversions.reportCallback_ = goog.nullFunction;
+
+
+/**
+ * Sets a function that will be called every time a legacy conversion is
+ * performed. The function is called with no parameters but it can use
+ * goog.debug.getStacktrace to get a stacktrace.
+ *
+ * @param {function(): undefined} callback Error callback as defined above.
+ */
+goog.html.legacyconversions.setReportCallback = function(callback) {
+ goog.html.legacyconversions.reportCallback_ = callback;
+};
diff --git a/chromium/third_party/ink/closure/html/safehtml.js b/chromium/third_party/ink/closure/html/safehtml.js
new file mode 100644
index 00000000000..088e014093d
--- /dev/null
+++ b/chromium/third_party/ink/closure/html/safehtml.js
@@ -0,0 +1,994 @@
+// Copyright 2013 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+
+/**
+ * @fileoverview The SafeHtml type and its builders.
+ *
+ * TODO(xtof): Link to document stating type contract.
+ */
+
+goog.provide('goog.html.SafeHtml');
+
+goog.require('goog.array');
+goog.require('goog.asserts');
+goog.require('goog.dom.TagName');
+goog.require('goog.dom.tags');
+goog.require('goog.html.SafeScript');
+goog.require('goog.html.SafeStyle');
+goog.require('goog.html.SafeStyleSheet');
+goog.require('goog.html.SafeUrl');
+goog.require('goog.html.TrustedResourceUrl');
+goog.require('goog.i18n.bidi.Dir');
+goog.require('goog.i18n.bidi.DirectionalString');
+goog.require('goog.labs.userAgent.browser');
+goog.require('goog.object');
+goog.require('goog.string');
+goog.require('goog.string.Const');
+goog.require('goog.string.TypedString');
+
+
+
+/**
+ * A string that is safe to use in HTML context in DOM APIs and HTML documents.
+ *
+ * A SafeHtml is a string-like object that carries the security type contract
+ * that its value as a string will not cause untrusted script execution when
+ * evaluated as HTML in a browser.
+ *
+ * Values of this type are guaranteed to be safe to use in HTML contexts,
+ * such as, assignment to the innerHTML DOM property, or interpolation into
+ * a HTML template in HTML PC_DATA context, in the sense that the use will not
+ * result in a Cross-Site-Scripting vulnerability.
+ *
+ * Instances of this type must be created via the factory methods
+ * ({@code goog.html.SafeHtml.create}, {@code goog.html.SafeHtml.htmlEscape}),
+ * etc and not by invoking its constructor. The constructor intentionally
+ * takes no parameters and the type is immutable; hence only a default instance
+ * corresponding to the empty string can be obtained via constructor invocation.
+ *
+ * @see goog.html.SafeHtml#create
+ * @see goog.html.SafeHtml#htmlEscape
+ * @constructor
+ * @final
+ * @struct
+ * @implements {goog.i18n.bidi.DirectionalString}
+ * @implements {goog.string.TypedString}
+ */
+goog.html.SafeHtml = function() {
+ /**
+ * The contained value of this SafeHtml. The field has a purposely ugly
+ * name to make (non-compiled) code that attempts to directly access this
+ * field stand out.
+ * @private {string}
+ */
+ this.privateDoNotAccessOrElseSafeHtmlWrappedValue_ = '';
+
+ /**
+ * A type marker used to implement additional run-time type checking.
+ * @see goog.html.SafeHtml#unwrap
+ * @const {!Object}
+ * @private
+ */
+ this.SAFE_HTML_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ =
+ goog.html.SafeHtml.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_;
+
+ /**
+ * This SafeHtml's directionality, or null if unknown.
+ * @private {?goog.i18n.bidi.Dir}
+ */
+ this.dir_ = null;
+};
+
+
+/**
+ * @override
+ * @const
+ */
+goog.html.SafeHtml.prototype.implementsGoogI18nBidiDirectionalString = true;
+
+
+/** @override */
+goog.html.SafeHtml.prototype.getDirection = function() {
+ return this.dir_;
+};
+
+
+/**
+ * @override
+ * @const
+ */
+goog.html.SafeHtml.prototype.implementsGoogStringTypedString = true;
+
+
+/**
+ * Returns this SafeHtml's value as string.
+ *
+ * IMPORTANT: In code where it is security relevant that an object's type is
+ * indeed {@code SafeHtml}, use {@code goog.html.SafeHtml.unwrap} instead of
+ * this method. If in doubt, assume that it's security relevant. In particular,
+ * note that goog.html functions which return a goog.html type do not guarantee
+ * that the returned instance is of the right type. For example:
+ *
+ * <pre>
+ * var fakeSafeHtml = new String('fake');
+ * fakeSafeHtml.__proto__ = goog.html.SafeHtml.prototype;
+ * var newSafeHtml = goog.html.SafeHtml.htmlEscape(fakeSafeHtml);
+ * // newSafeHtml is just an alias for fakeSafeHtml, it's passed through by
+ * // goog.html.SafeHtml.htmlEscape() as fakeSafeHtml
+ * // instanceof goog.html.SafeHtml.
+ * </pre>
+ *
+ * @see goog.html.SafeHtml#unwrap
+ * @override
+ */
+goog.html.SafeHtml.prototype.getTypedStringValue = function() {
+ return this.privateDoNotAccessOrElseSafeHtmlWrappedValue_;
+};
+
+
+if (goog.DEBUG) {
+ /**
+ * Returns a debug string-representation of this value.
+ *
+ * To obtain the actual string value wrapped in a SafeHtml, use
+ * {@code goog.html.SafeHtml.unwrap}.
+ *
+ * @see goog.html.SafeHtml#unwrap
+ * @override
+ */
+ goog.html.SafeHtml.prototype.toString = function() {
+ return 'SafeHtml{' + this.privateDoNotAccessOrElseSafeHtmlWrappedValue_ +
+ '}';
+ };
+}
+
+
+/**
+ * Performs a runtime check that the provided object is indeed a SafeHtml
+ * object, and returns its value.
+ * @param {!goog.html.SafeHtml} safeHtml The object to extract from.
+ * @return {string} The SafeHtml object's contained string, unless the run-time
+ * type check fails. In that case, {@code unwrap} returns an innocuous
+ * string, or, if assertions are enabled, throws
+ * {@code goog.asserts.AssertionError}.
+ */
+goog.html.SafeHtml.unwrap = function(safeHtml) {
+ // Perform additional run-time type-checking to ensure that safeHtml is indeed
+ // an instance of the expected type. This provides some additional protection
+ // against security bugs due to application code that disables type checks.
+ // Specifically, the following checks are performed:
+ // 1. The object is an instance of the expected type.
+ // 2. The object is not an instance of a subclass.
+ // 3. The object carries a type marker for the expected type. "Faking" an
+ // object requires a reference to the type marker, which has names intended
+ // to stand out in code reviews.
+ if (safeHtml instanceof goog.html.SafeHtml &&
+ safeHtml.constructor === goog.html.SafeHtml &&
+ safeHtml.SAFE_HTML_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ ===
+ goog.html.SafeHtml.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_) {
+ return safeHtml.privateDoNotAccessOrElseSafeHtmlWrappedValue_;
+ } else {
+ goog.asserts.fail('expected object of type SafeHtml, got \'' +
+ safeHtml + '\' of type ' + goog.typeOf(safeHtml));
+ return 'type_error:SafeHtml';
+ }
+};
+
+
+/**
+ * Shorthand for union of types that can sensibly be converted to strings
+ * or might already be SafeHtml (as SafeHtml is a goog.string.TypedString).
+ * @private
+ * @typedef {string|number|boolean|!goog.string.TypedString|
+ * !goog.i18n.bidi.DirectionalString}
+ */
+goog.html.SafeHtml.TextOrHtml_;
+
+
+/**
+ * Returns HTML-escaped text as a SafeHtml object.
+ *
+ * If text is of a type that implements
+ * {@code goog.i18n.bidi.DirectionalString}, the directionality of the new
+ * {@code SafeHtml} object is set to {@code text}'s directionality, if known.
+ * Otherwise, the directionality of the resulting SafeHtml is unknown (i.e.,
+ * {@code null}).
+ *
+ * @param {!goog.html.SafeHtml.TextOrHtml_} textOrHtml The text to escape. If
+ * the parameter is of type SafeHtml it is returned directly (no escaping
+ * is done).
+ * @return {!goog.html.SafeHtml} The escaped text, wrapped as a SafeHtml.
+ */
+goog.html.SafeHtml.htmlEscape = function(textOrHtml) {
+ if (textOrHtml instanceof goog.html.SafeHtml) {
+ return textOrHtml;
+ }
+ var dir = null;
+ if (textOrHtml.implementsGoogI18nBidiDirectionalString) {
+ dir = textOrHtml.getDirection();
+ }
+ var textAsString;
+ if (textOrHtml.implementsGoogStringTypedString) {
+ textAsString = textOrHtml.getTypedStringValue();
+ } else {
+ textAsString = String(textOrHtml);
+ }
+ return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
+ goog.string.htmlEscape(textAsString), dir);
+};
+
+
+/**
+ * Returns HTML-escaped text as a SafeHtml object, with newlines changed to
+ * &lt;br&gt;.
+ * @param {!goog.html.SafeHtml.TextOrHtml_} textOrHtml The text to escape. If
+ * the parameter is of type SafeHtml it is returned directly (no escaping
+ * is done).
+ * @return {!goog.html.SafeHtml} The escaped text, wrapped as a SafeHtml.
+ */
+goog.html.SafeHtml.htmlEscapePreservingNewlines = function(textOrHtml) {
+ if (textOrHtml instanceof goog.html.SafeHtml) {
+ return textOrHtml;
+ }
+ var html = goog.html.SafeHtml.htmlEscape(textOrHtml);
+ return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
+ goog.string.newLineToBr(goog.html.SafeHtml.unwrap(html)),
+ html.getDirection());
+};
+
+
+/**
+ * Returns HTML-escaped text as a SafeHtml object, with newlines changed to
+ * &lt;br&gt; and escaping whitespace to preserve spatial formatting. Character
+ * entity #160 is used to make it safer for XML.
+ * @param {!goog.html.SafeHtml.TextOrHtml_} textOrHtml The text to escape. If
+ * the parameter is of type SafeHtml it is returned directly (no escaping
+ * is done).
+ * @return {!goog.html.SafeHtml} The escaped text, wrapped as a SafeHtml.
+ */
+goog.html.SafeHtml.htmlEscapePreservingNewlinesAndSpaces = function(
+ textOrHtml) {
+ if (textOrHtml instanceof goog.html.SafeHtml) {
+ return textOrHtml;
+ }
+ var html = goog.html.SafeHtml.htmlEscape(textOrHtml);
+ return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
+ goog.string.whitespaceEscape(goog.html.SafeHtml.unwrap(html)),
+ html.getDirection());
+};
+
+
+/**
+ * Coerces an arbitrary object into a SafeHtml object.
+ *
+ * If {@code textOrHtml} is already of type {@code goog.html.SafeHtml}, the same
+ * object is returned. Otherwise, {@code textOrHtml} is coerced to string, and
+ * HTML-escaped. If {@code textOrHtml} is of a type that implements
+ * {@code goog.i18n.bidi.DirectionalString}, its directionality, if known, is
+ * preserved.
+ *
+ * @param {!goog.html.SafeHtml.TextOrHtml_} textOrHtml The text or SafeHtml to
+ * coerce.
+ * @return {!goog.html.SafeHtml} The resulting SafeHtml object.
+ * @deprecated Use goog.html.SafeHtml.htmlEscape.
+ */
+goog.html.SafeHtml.from = goog.html.SafeHtml.htmlEscape;
+
+
+/**
+ * @const
+ * @private
+ */
+goog.html.SafeHtml.VALID_NAMES_IN_TAG_ = /^[a-zA-Z0-9-]+$/;
+
+
+/**
+ * Set of attributes containing URL as defined at
+ * http://www.w3.org/TR/html5/index.html#attributes-1.
+ * @private @const {!Object<string,boolean>}
+ */
+goog.html.SafeHtml.URL_ATTRIBUTES_ = goog.object.createSet(
+ 'action', 'cite', 'data', 'formaction', 'href', 'manifest', 'poster',
+ 'src');
+
+
+/**
+ * Tags which are unsupported via create(). They might be supported via a
+ * tag-specific create method. These are tags which might require a
+ * TrustedResourceUrl in one of their attributes or a restricted type for
+ * their content.
+ * @private @const {!Object<string,boolean>}
+ */
+goog.html.SafeHtml.NOT_ALLOWED_TAG_NAMES_ = goog.object.createSet(
+ goog.dom.TagName.APPLET, goog.dom.TagName.BASE, goog.dom.TagName.EMBED,
+ goog.dom.TagName.IFRAME, goog.dom.TagName.LINK, goog.dom.TagName.MATH,
+ goog.dom.TagName.META, goog.dom.TagName.OBJECT, goog.dom.TagName.SCRIPT,
+ goog.dom.TagName.STYLE, goog.dom.TagName.SVG, goog.dom.TagName.TEMPLATE);
+
+
+/**
+ * @typedef {string|number|goog.string.TypedString|
+ * goog.html.SafeStyle.PropertyMap|undefined}
+ */
+goog.html.SafeHtml.AttributeValue;
+
+
+/**
+ * Creates a SafeHtml content consisting of a tag with optional attributes and
+ * optional content.
+ *
+ * For convenience tag names and attribute names are accepted as regular
+ * strings, instead of goog.string.Const. Nevertheless, you should not pass
+ * user-controlled values to these parameters. Note that these parameters are
+ * syntactically validated at runtime, and invalid values will result in
+ * an exception.
+ *
+ * Example usage:
+ *
+ * goog.html.SafeHtml.create('br');
+ * goog.html.SafeHtml.create('div', {'class': 'a'});
+ * goog.html.SafeHtml.create('p', {}, 'a');
+ * goog.html.SafeHtml.create('p', {}, goog.html.SafeHtml.create('br'));
+ *
+ * goog.html.SafeHtml.create('span', {
+ * 'style': {'margin': '0'}
+ * });
+ *
+ * To guarantee SafeHtml's type contract is upheld there are restrictions on
+ * attribute values and tag names.
+ *
+ * - For attributes which contain script code (on*), a goog.string.Const is
+ * required.
+ * - For attributes which contain style (style), a goog.html.SafeStyle or a
+ * goog.html.SafeStyle.PropertyMap is required.
+ * - For attributes which are interpreted as URLs (e.g. src, href) a
+ * goog.html.SafeUrl, goog.string.Const or string is required. If a string
+ * is passed, it will be sanitized with SafeUrl.sanitize().
+ * - For tags which can load code or set security relevant page metadata,
+ * more specific goog.html.SafeHtml.create*() functions must be used. Tags
+ * which are not supported by this function are applet, base, embed, iframe,
+ * link, math, object, script, style, svg, and template.
+ *
+ * @param {!goog.dom.TagName|string} tagName The name of the tag. Only tag names
+ * consisting of [a-zA-Z0-9-] are allowed. Tag names documented above are
+ * disallowed.
+ * @param {?Object<string, ?goog.html.SafeHtml.AttributeValue>=} opt_attributes
+ * Mapping from attribute names to their values. Only attribute names
+ * consisting of [a-zA-Z0-9-] are allowed. Value of null or undefined causes
+ * the attribute to be omitted.
+ * @param {!goog.html.SafeHtml.TextOrHtml_|
+ * !Array<!goog.html.SafeHtml.TextOrHtml_>=} opt_content Content to
+ * HTML-escape and put inside the tag. This must be empty for void tags
+ * like <br>. Array elements are concatenated.
+ * @return {!goog.html.SafeHtml} The SafeHtml content with the tag.
+ * @throws {Error} If invalid tag name, attribute name, or attribute value is
+ * provided.
+ * @throws {goog.asserts.AssertionError} If content for void tag is provided.
+ */
+goog.html.SafeHtml.create = function(tagName, opt_attributes, opt_content) {
+ goog.html.SafeHtml.verifyTagName(String(tagName));
+ return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse(
+ String(tagName), opt_attributes, opt_content);
+};
+
+
+/**
+ * Verifies if the tag name is valid and if it doesn't change the context.
+ * E.g. STRONG is fine but SCRIPT throws because it changes context. See
+ * goog.html.SafeHtml.create for an explanation of allowed tags.
+ * @param {string} tagName
+ * @throws {Error} If invalid tag name is provided.
+ * @package
+ */
+goog.html.SafeHtml.verifyTagName = function(tagName) {
+ if (!goog.html.SafeHtml.VALID_NAMES_IN_TAG_.test(tagName)) {
+ throw new Error('Invalid tag name <' + tagName + '>.');
+ }
+ if (tagName.toUpperCase() in goog.html.SafeHtml.NOT_ALLOWED_TAG_NAMES_) {
+ throw new Error('Tag name <' + tagName + '> is not allowed for SafeHtml.');
+ }
+};
+
+
+/**
+ * Creates a SafeHtml representing an iframe tag.
+ *
+ * This by default restricts the iframe as much as possible by setting the
+ * sandbox attribute to the empty string. If the iframe requires less
+ * restrictions, set the sandbox attribute as tight as possible, but do not rely
+ * on the sandbox as a security feature because it is not supported by older
+ * browsers. If a sandbox is essential to security (e.g. for third-party
+ * frames), use createSandboxIframe which checks for browser support.
+ *
+ * @see https://developer.mozilla.org/en/docs/Web/HTML/Element/iframe#attr-sandbox
+ *
+ * @param {?goog.html.TrustedResourceUrl=} opt_src The value of the src
+ * attribute. If null or undefined src will not be set.
+ * @param {?goog.html.SafeHtml=} opt_srcdoc The value of the srcdoc attribute.
+ * If null or undefined srcdoc will not be set.
+ * @param {?Object<string, ?goog.html.SafeHtml.AttributeValue>=} opt_attributes
+ * Mapping from attribute names to their values. Only attribute names
+ * consisting of [a-zA-Z0-9-] are allowed. Value of null or undefined causes
+ * the attribute to be omitted.
+ * @param {!goog.html.SafeHtml.TextOrHtml_|
+ * !Array<!goog.html.SafeHtml.TextOrHtml_>=} opt_content Content to
+ * HTML-escape and put inside the tag. Array elements are concatenated.
+ * @return {!goog.html.SafeHtml} The SafeHtml content with the tag.
+ * @throws {Error} If invalid tag name, attribute name, or attribute value is
+ * provided. If opt_attributes contains the src or srcdoc attributes.
+ */
+goog.html.SafeHtml.createIframe = function(
+ opt_src, opt_srcdoc, opt_attributes, opt_content) {
+ if (opt_src) {
+ // Check whether this is really TrustedResourceUrl.
+ goog.html.TrustedResourceUrl.unwrap(opt_src);
+ }
+
+ var fixedAttributes = {};
+ fixedAttributes['src'] = opt_src || null;
+ fixedAttributes['srcdoc'] =
+ opt_srcdoc && goog.html.SafeHtml.unwrap(opt_srcdoc);
+ var defaultAttributes = {'sandbox': ''};
+ var attributes = goog.html.SafeHtml.combineAttributes(
+ fixedAttributes, defaultAttributes, opt_attributes);
+ return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse(
+ 'iframe', attributes, opt_content);
+};
+
+
+/**
+ * Creates a SafeHtml representing a sandboxed iframe tag.
+ *
+ * The sandbox attribute is enforced in its most restrictive mode, an empty
+ * string. Consequently, the security requirements for the src and srcdoc
+ * attributes are relaxed compared to SafeHtml.createIframe. This function
+ * will throw on browsers that do not support the sandbox attribute, as
+ * determined by SafeHtml.canUseSandboxIframe.
+ *
+ * The SafeHtml returned by this function can trigger downloads with no
+ * user interaction on Chrome (though only a few, further attempts are blocked).
+ * Firefox and IE will block all downloads from the sandbox.
+ *
+ * @see https://developer.mozilla.org/en/docs/Web/HTML/Element/iframe#attr-sandbox
+ * @see https://lists.w3.org/Archives/Public/public-whatwg-archive/2013Feb/0112.html
+ *
+ * @param {string|!goog.html.SafeUrl=} opt_src The value of the src
+ * attribute. If null or undefined src will not be set.
+ * @param {string=} opt_srcdoc The value of the srcdoc attribute.
+ * If null or undefined srcdoc will not be set. Will not be sanitized.
+ * @param {!Object<string, ?goog.html.SafeHtml.AttributeValue>=} opt_attributes
+ * Mapping from attribute names to their values. Only attribute names
+ * consisting of [a-zA-Z0-9-] are allowed. Value of null or undefined causes
+ * the attribute to be omitted.
+ * @param {!goog.html.SafeHtml.TextOrHtml_|
+ * !Array<!goog.html.SafeHtml.TextOrHtml_>=} opt_content Content to
+ * HTML-escape and put inside the tag. Array elements are concatenated.
+ * @return {!goog.html.SafeHtml} The SafeHtml content with the tag.
+ * @throws {Error} If invalid tag name, attribute name, or attribute value is
+ * provided. If opt_attributes contains the src, srcdoc or sandbox
+ * attributes. If browser does not support the sandbox attribute on iframe.
+ */
+goog.html.SafeHtml.createSandboxIframe = function(
+ opt_src, opt_srcdoc, opt_attributes, opt_content) {
+ if (!goog.html.SafeHtml.canUseSandboxIframe()) {
+ throw new Error('The browser does not support sandboxed iframes.');
+ }
+
+ var fixedAttributes = {};
+ if (opt_src) {
+ // Note that sanitize is a no-op on SafeUrl.
+ fixedAttributes['src'] =
+ goog.html.SafeUrl.unwrap(goog.html.SafeUrl.sanitize(opt_src));
+ } else {
+ fixedAttributes['src'] = null;
+ }
+ fixedAttributes['srcdoc'] = opt_srcdoc || null;
+ fixedAttributes['sandbox'] = '';
+ var attributes =
+ goog.html.SafeHtml.combineAttributes(fixedAttributes, {}, opt_attributes);
+ return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse(
+ 'iframe', attributes, opt_content);
+};
+
+
+/**
+ * Checks if the user agent supports sandboxed iframes.
+ * @return {boolean}
+ */
+goog.html.SafeHtml.canUseSandboxIframe = function() {
+ return goog.global['HTMLIFrameElement'] &&
+ ('sandbox' in goog.global['HTMLIFrameElement'].prototype);
+};
+
+
+/**
+ * Creates a SafeHtml representing a script tag with the src attribute.
+ * @param {!goog.html.TrustedResourceUrl} src The value of the src
+ * attribute.
+ * @param {?Object<string, ?goog.html.SafeHtml.AttributeValue>=}
+ * opt_attributes
+ * Mapping from attribute names to their values. Only attribute names
+ * consisting of [a-zA-Z0-9-] are allowed. Value of null or undefined
+ * causes the attribute to be omitted.
+ * @return {!goog.html.SafeHtml} The SafeHtml content with the tag.
+ * @throws {Error} If invalid attribute name or value is provided. If
+ * opt_attributes contains the src attribute.
+ */
+goog.html.SafeHtml.createScriptSrc = function(src, opt_attributes) {
+ // TODO(mlourenco): The charset attribute should probably be blocked. If
+ // its value is attacker controlled, the script contains attacker controlled
+ // sub-strings (even if properly escaped) and the server does not set charset
+ // then XSS is likely possible.
+ // https://html.spec.whatwg.org/multipage/scripting.html#dom-script-charset
+
+ // Check whether this is really TrustedResourceUrl.
+ goog.html.TrustedResourceUrl.unwrap(src);
+
+ var fixedAttributes = {'src': src};
+ var defaultAttributes = {};
+ var attributes = goog.html.SafeHtml.combineAttributes(
+ fixedAttributes, defaultAttributes, opt_attributes);
+ return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse(
+ 'script', attributes);
+};
+
+
+/**
+ * Creates a SafeHtml representing a script tag. Does not allow the language,
+ * src, text or type attributes to be set.
+ * @param {!goog.html.SafeScript|!Array<!goog.html.SafeScript>}
+ * script Content to put inside the tag. Array elements are
+ * concatenated.
+ * @param {?Object<string, ?goog.html.SafeHtml.AttributeValue>=} opt_attributes
+ * Mapping from attribute names to their values. Only attribute names
+ * consisting of [a-zA-Z0-9-] are allowed. Value of null or undefined causes
+ * the attribute to be omitted.
+ * @return {!goog.html.SafeHtml} The SafeHtml content with the tag.
+ * @throws {Error} If invalid attribute name or attribute value is provided. If
+ * opt_attributes contains the language, src, text or type attribute.
+ */
+goog.html.SafeHtml.createScript = function(script, opt_attributes) {
+ for (var attr in opt_attributes) {
+ var attrLower = attr.toLowerCase();
+ if (attrLower == 'language' || attrLower == 'src' || attrLower == 'text' ||
+ attrLower == 'type') {
+ throw new Error('Cannot set "' + attrLower + '" attribute');
+ }
+ }
+
+ var content = '';
+ script = goog.array.concat(script);
+ for (var i = 0; i < script.length; i++) {
+ content += goog.html.SafeScript.unwrap(script[i]);
+ }
+ // Convert to SafeHtml so that it's not HTML-escaped. This is safe because
+ // as part of its contract, SafeScript should have no dangerous '<'.
+ var htmlContent =
+ goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
+ content, goog.i18n.bidi.Dir.NEUTRAL);
+ return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse(
+ 'script', opt_attributes, htmlContent);
+};
+
+
+/**
+ * Creates a SafeHtml representing a style tag. The type attribute is set
+ * to "text/css".
+ * @param {!goog.html.SafeStyleSheet|!Array<!goog.html.SafeStyleSheet>}
+ * styleSheet Content to put inside the tag. Array elements are
+ * concatenated.
+ * @param {?Object<string, ?goog.html.SafeHtml.AttributeValue>=} opt_attributes
+ * Mapping from attribute names to their values. Only attribute names
+ * consisting of [a-zA-Z0-9-] are allowed. Value of null or undefined causes
+ * the attribute to be omitted.
+ * @return {!goog.html.SafeHtml} The SafeHtml content with the tag.
+ * @throws {Error} If invalid attribute name or attribute value is provided. If
+ * opt_attributes contains the type attribute.
+ */
+goog.html.SafeHtml.createStyle = function(styleSheet, opt_attributes) {
+ var fixedAttributes = {'type': 'text/css'};
+ var defaultAttributes = {};
+ var attributes = goog.html.SafeHtml.combineAttributes(
+ fixedAttributes, defaultAttributes, opt_attributes);
+
+ var content = '';
+ styleSheet = goog.array.concat(styleSheet);
+ for (var i = 0; i < styleSheet.length; i++) {
+ content += goog.html.SafeStyleSheet.unwrap(styleSheet[i]);
+ }
+ // Convert to SafeHtml so that it's not HTML-escaped. This is safe because
+ // as part of its contract, SafeStyleSheet should have no dangerous '<'.
+ var htmlContent =
+ goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
+ content, goog.i18n.bidi.Dir.NEUTRAL);
+ return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse(
+ 'style', attributes, htmlContent);
+};
+
+
+/**
+ * Creates a SafeHtml representing a meta refresh tag.
+ * @param {!goog.html.SafeUrl|string} url Where to redirect. If a string is
+ * passed, it will be sanitized with SafeUrl.sanitize().
+ * @param {number=} opt_secs Number of seconds until the page should be
+ * reloaded. Will be set to 0 if unspecified.
+ * @return {!goog.html.SafeHtml} The SafeHtml content with the tag.
+ */
+goog.html.SafeHtml.createMetaRefresh = function(url, opt_secs) {
+
+ // Note that sanitize is a no-op on SafeUrl.
+ var unwrappedUrl = goog.html.SafeUrl.unwrap(goog.html.SafeUrl.sanitize(url));
+
+ if (goog.labs.userAgent.browser.isIE() ||
+ goog.labs.userAgent.browser.isEdge()) {
+ // IE/EDGE can't parse the content attribute if the url contains a
+ // semicolon. We can fix this by adding quotes around the url, but then we
+ // can't parse quotes in the URL correctly. Also, it seems that IE/EDGE
+ // did not unescape semicolons in these URLs at some point in the past. We
+ // take a best-effort approach.
+ //
+ // If the URL has semicolons (which may happen in some cases, see
+ // http://www.w3.org/TR/1999/REC-html401-19991224/appendix/notes.html#h-B.2
+ // for instance), wrap it in single quotes to protect the semicolons.
+ // If the URL has semicolons and single quotes, url-encode the single quotes
+ // as well.
+ //
+ // This is imperfect. Notice that both ' and ; are reserved characters in
+ // URIs, so this could do the wrong thing, but at least it will do the wrong
+ // thing in only rare cases.
+ if (goog.string.contains(unwrappedUrl, ';')) {
+ unwrappedUrl = "'" + unwrappedUrl.replace(/'/g, '%27') + "'";
+ }
+ }
+ var attributes = {
+ 'http-equiv': 'refresh',
+ 'content': (opt_secs || 0) + '; url=' + unwrappedUrl
+ };
+
+ // This function will handle the HTML escaping for attributes.
+ return goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse(
+ 'meta', attributes);
+};
+
+
+/**
+ * @param {string} tagName The tag name.
+ * @param {string} name The attribute name.
+ * @param {!goog.html.SafeHtml.AttributeValue} value The attribute value.
+ * @return {string} A "name=value" string.
+ * @throws {Error} If attribute value is unsafe for the given tag and attribute.
+ * @private
+ */
+goog.html.SafeHtml.getAttrNameAndValue_ = function(tagName, name, value) {
+ // If it's goog.string.Const, allow any valid attribute name.
+ if (value instanceof goog.string.Const) {
+ value = goog.string.Const.unwrap(value);
+ } else if (name.toLowerCase() == 'style') {
+ value = goog.html.SafeHtml.getStyleValue_(value);
+ } else if (/^on/i.test(name)) {
+ // TODO(jakubvrana): Disallow more attributes with a special meaning.
+ throw new Error(
+ 'Attribute "' + name + '" requires goog.string.Const value, "' + value +
+ '" given.');
+ // URL attributes handled differently according to tag.
+ } else if (name.toLowerCase() in goog.html.SafeHtml.URL_ATTRIBUTES_) {
+ if (value instanceof goog.html.TrustedResourceUrl) {
+ value = goog.html.TrustedResourceUrl.unwrap(value);
+ } else if (value instanceof goog.html.SafeUrl) {
+ value = goog.html.SafeUrl.unwrap(value);
+ } else if (goog.isString(value)) {
+ value = goog.html.SafeUrl.sanitize(value).getTypedStringValue();
+ } else {
+ throw new Error(
+ 'Attribute "' + name + '" on tag "' + tagName +
+ '" requires goog.html.SafeUrl, goog.string.Const, or string,' +
+ ' value "' + value + '" given.');
+ }
+ }
+
+ // Accept SafeUrl, TrustedResourceUrl, etc. for attributes which only require
+ // HTML-escaping.
+ if (value.implementsGoogStringTypedString) {
+ // Ok to call getTypedStringValue() since there's no reliance on the type
+ // contract for security here.
+ value = value.getTypedStringValue();
+ }
+
+ goog.asserts.assert(
+ goog.isString(value) || goog.isNumber(value),
+ 'String or number value expected, got ' + (typeof value) +
+ ' with value: ' + value);
+ return name + '="' + goog.string.htmlEscape(String(value)) + '"';
+};
+
+
+/**
+ * Gets value allowed in "style" attribute.
+ * @param {!goog.html.SafeHtml.AttributeValue} value It could be SafeStyle or a
+ * map which will be passed to goog.html.SafeStyle.create.
+ * @return {string} Unwrapped value.
+ * @throws {Error} If string value is given.
+ * @private
+ */
+goog.html.SafeHtml.getStyleValue_ = function(value) {
+ if (!goog.isObject(value)) {
+ throw new Error(
+ 'The "style" attribute requires goog.html.SafeStyle or map ' +
+ 'of style properties, ' + (typeof value) + ' given: ' + value);
+ }
+ if (!(value instanceof goog.html.SafeStyle)) {
+ // Process the property bag into a style object.
+ value = goog.html.SafeStyle.create(value);
+ }
+ return goog.html.SafeStyle.unwrap(value);
+};
+
+
+/**
+ * Creates a SafeHtml content with known directionality consisting of a tag with
+ * optional attributes and optional content.
+ * @param {!goog.i18n.bidi.Dir} dir Directionality.
+ * @param {string} tagName
+ * @param {?Object<string, ?goog.html.SafeHtml.AttributeValue>=} opt_attributes
+ * @param {!goog.html.SafeHtml.TextOrHtml_|
+ * !Array<!goog.html.SafeHtml.TextOrHtml_>=} opt_content
+ * @return {!goog.html.SafeHtml} The SafeHtml content with the tag.
+ */
+goog.html.SafeHtml.createWithDir = function(
+ dir, tagName, opt_attributes, opt_content) {
+ var html = goog.html.SafeHtml.create(tagName, opt_attributes, opt_content);
+ html.dir_ = dir;
+ return html;
+};
+
+
+/**
+ * Creates a new SafeHtml object by concatenating values.
+ * @param {...(!goog.html.SafeHtml.TextOrHtml_|
+ * !Array<!goog.html.SafeHtml.TextOrHtml_>)} var_args Values to concatenate.
+ * @return {!goog.html.SafeHtml}
+ */
+goog.html.SafeHtml.concat = function(var_args) {
+ var dir = goog.i18n.bidi.Dir.NEUTRAL;
+ var content = '';
+
+ /**
+ * @param {!goog.html.SafeHtml.TextOrHtml_|
+ * !Array<!goog.html.SafeHtml.TextOrHtml_>} argument
+ */
+ var addArgument = function(argument) {
+ if (goog.isArray(argument)) {
+ goog.array.forEach(argument, addArgument);
+ } else {
+ var html = goog.html.SafeHtml.htmlEscape(argument);
+ content += goog.html.SafeHtml.unwrap(html);
+ var htmlDir = html.getDirection();
+ if (dir == goog.i18n.bidi.Dir.NEUTRAL) {
+ dir = htmlDir;
+ } else if (htmlDir != goog.i18n.bidi.Dir.NEUTRAL && dir != htmlDir) {
+ dir = null;
+ }
+ }
+ };
+
+ goog.array.forEach(arguments, addArgument);
+ return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
+ content, dir);
+};
+
+
+/**
+ * Creates a new SafeHtml object with known directionality by concatenating the
+ * values.
+ * @param {!goog.i18n.bidi.Dir} dir Directionality.
+ * @param {...(!goog.html.SafeHtml.TextOrHtml_|
+ * !Array<!goog.html.SafeHtml.TextOrHtml_>)} var_args Elements of array
+ * arguments would be processed recursively.
+ * @return {!goog.html.SafeHtml}
+ */
+goog.html.SafeHtml.concatWithDir = function(dir, var_args) {
+ var html = goog.html.SafeHtml.concat(goog.array.slice(arguments, 1));
+ html.dir_ = dir;
+ return html;
+};
+
+
+/**
+ * Type marker for the SafeHtml type, used to implement additional run-time
+ * type checking.
+ * @const {!Object}
+ * @private
+ */
+goog.html.SafeHtml.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = {};
+
+
+/**
+ * Package-internal utility method to create SafeHtml instances.
+ *
+ * @param {string} html The string to initialize the SafeHtml object with.
+ * @param {?goog.i18n.bidi.Dir} dir The directionality of the SafeHtml to be
+ * constructed, or null if unknown.
+ * @return {!goog.html.SafeHtml} The initialized SafeHtml object.
+ * @package
+ */
+goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse = function(
+ html, dir) {
+ return new goog.html.SafeHtml().initSecurityPrivateDoNotAccessOrElse_(
+ html, dir);
+};
+
+
+/**
+ * Called from createSafeHtmlSecurityPrivateDoNotAccessOrElse(). This
+ * method exists only so that the compiler can dead code eliminate static
+ * fields (like EMPTY) when they're not accessed.
+ * @param {string} html
+ * @param {?goog.i18n.bidi.Dir} dir
+ * @return {!goog.html.SafeHtml}
+ * @private
+ */
+goog.html.SafeHtml.prototype.initSecurityPrivateDoNotAccessOrElse_ = function(
+ html, dir) {
+ this.privateDoNotAccessOrElseSafeHtmlWrappedValue_ = html;
+ this.dir_ = dir;
+ return this;
+};
+
+
+/**
+ * Like create() but does not restrict which tags can be constructed.
+ *
+ * @param {string} tagName Tag name. Set or validated by caller.
+ * @param {?Object<string, ?goog.html.SafeHtml.AttributeValue>=} opt_attributes
+ * @param {(!goog.html.SafeHtml.TextOrHtml_|
+ * !Array<!goog.html.SafeHtml.TextOrHtml_>)=} opt_content
+ * @return {!goog.html.SafeHtml}
+ * @throws {Error} If invalid or unsafe attribute name or value is provided.
+ * @throws {goog.asserts.AssertionError} If content for void tag is provided.
+ * @package
+ */
+goog.html.SafeHtml.createSafeHtmlTagSecurityPrivateDoNotAccessOrElse = function(
+ tagName, opt_attributes, opt_content) {
+ var dir = null;
+ var result = '<' + tagName;
+ result += goog.html.SafeHtml.stringifyAttributes(tagName, opt_attributes);
+
+ var content = opt_content;
+ if (!goog.isDefAndNotNull(content)) {
+ content = [];
+ } else if (!goog.isArray(content)) {
+ content = [content];
+ }
+
+ if (goog.dom.tags.isVoidTag(tagName.toLowerCase())) {
+ goog.asserts.assert(
+ !content.length, 'Void tag <' + tagName + '> does not allow content.');
+ result += '>';
+ } else {
+ var html = goog.html.SafeHtml.concat(content);
+ result += '>' + goog.html.SafeHtml.unwrap(html) + '</' + tagName + '>';
+ dir = html.getDirection();
+ }
+
+ var dirAttribute = opt_attributes && opt_attributes['dir'];
+ if (dirAttribute) {
+ if (/^(ltr|rtl|auto)$/i.test(dirAttribute)) {
+ // If the tag has the "dir" attribute specified then its direction is
+ // neutral because it can be safely used in any context.
+ dir = goog.i18n.bidi.Dir.NEUTRAL;
+ } else {
+ dir = null;
+ }
+ }
+
+ return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
+ result, dir);
+};
+
+
+/**
+ * Creates a string with attributes to insert after tagName.
+ * @param {string} tagName
+ * @param {?Object<string, ?goog.html.SafeHtml.AttributeValue>=} opt_attributes
+ * @return {string} Returns an empty string if there are no attributes, returns
+ * a string starting with a space otherwise.
+ * @throws {Error} If attribute value is unsafe for the given tag and attribute.
+ * @package
+ */
+goog.html.SafeHtml.stringifyAttributes = function(tagName, opt_attributes) {
+ var result = '';
+ if (opt_attributes) {
+ for (var name in opt_attributes) {
+ if (!goog.html.SafeHtml.VALID_NAMES_IN_TAG_.test(name)) {
+ throw new Error('Invalid attribute name "' + name + '".');
+ }
+ var value = opt_attributes[name];
+ if (!goog.isDefAndNotNull(value)) {
+ continue;
+ }
+ result +=
+ ' ' + goog.html.SafeHtml.getAttrNameAndValue_(tagName, name, value);
+ }
+ }
+ return result;
+};
+
+
+/**
+ * @param {!Object<string, ?goog.html.SafeHtml.AttributeValue>} fixedAttributes
+ * @param {!Object<string, string>} defaultAttributes
+ * @param {?Object<string, ?goog.html.SafeHtml.AttributeValue>=} opt_attributes
+ * Optional attributes passed to create*().
+ * @return {!Object<string, ?goog.html.SafeHtml.AttributeValue>}
+ * @throws {Error} If opt_attributes contains an attribute with the same name
+ * as an attribute in fixedAttributes.
+ * @package
+ */
+goog.html.SafeHtml.combineAttributes = function(
+ fixedAttributes, defaultAttributes, opt_attributes) {
+ var combinedAttributes = {};
+ var name;
+
+ for (name in fixedAttributes) {
+ goog.asserts.assert(name.toLowerCase() == name, 'Must be lower case');
+ combinedAttributes[name] = fixedAttributes[name];
+ }
+ for (name in defaultAttributes) {
+ goog.asserts.assert(name.toLowerCase() == name, 'Must be lower case');
+ combinedAttributes[name] = defaultAttributes[name];
+ }
+
+ for (name in opt_attributes) {
+ var nameLower = name.toLowerCase();
+ if (nameLower in fixedAttributes) {
+ throw new Error(
+ 'Cannot override "' + nameLower + '" attribute, got "' + name +
+ '" with value "' + opt_attributes[name] + '"');
+ }
+ if (nameLower in defaultAttributes) {
+ delete combinedAttributes[nameLower];
+ }
+ combinedAttributes[name] = opt_attributes[name];
+ }
+
+ return combinedAttributes;
+};
+
+
+/**
+ * A SafeHtml instance corresponding to the HTML doctype: "<!DOCTYPE html>".
+ * @const {!goog.html.SafeHtml}
+ */
+goog.html.SafeHtml.DOCTYPE_HTML =
+ goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
+ '<!DOCTYPE html>', goog.i18n.bidi.Dir.NEUTRAL);
+
+
+/**
+ * A SafeHtml instance corresponding to the empty string.
+ * @const {!goog.html.SafeHtml}
+ */
+goog.html.SafeHtml.EMPTY =
+ goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
+ '', goog.i18n.bidi.Dir.NEUTRAL);
+
+
+/**
+ * A SafeHtml instance corresponding to the <br> tag.
+ * @const {!goog.html.SafeHtml}
+ */
+goog.html.SafeHtml.BR =
+ goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
+ '<br>', goog.i18n.bidi.Dir.NEUTRAL);
diff --git a/chromium/third_party/ink/closure/html/safescript.js b/chromium/third_party/ink/closure/html/safescript.js
new file mode 100644
index 00000000000..7a945eb9310
--- /dev/null
+++ b/chromium/third_party/ink/closure/html/safescript.js
@@ -0,0 +1,234 @@
+// Copyright 2014 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview The SafeScript type and its builders.
+ *
+ * TODO(xtof): Link to document stating type contract.
+ */
+
+goog.provide('goog.html.SafeScript');
+
+goog.require('goog.asserts');
+goog.require('goog.string.Const');
+goog.require('goog.string.TypedString');
+
+
+
+/**
+ * A string-like object which represents JavaScript code and that carries the
+ * security type contract that its value, as a string, will not cause execution
+ * of unconstrained attacker controlled code (XSS) when evaluated as JavaScript
+ * in a browser.
+ *
+ * Instances of this type must be created via the factory method
+ * {@code goog.html.SafeScript.fromConstant} and not by invoking its
+ * constructor. The constructor intentionally takes no parameters and the type
+ * is immutable; hence only a default instance corresponding to the empty string
+ * can be obtained via constructor invocation.
+ *
+ * A SafeScript's string representation can safely be interpolated as the
+ * content of a script element within HTML. The SafeScript string should not be
+ * escaped before interpolation.
+ *
+ * Note that the SafeScript might contain text that is attacker-controlled but
+ * that text should have been interpolated with appropriate escaping,
+ * sanitization and/or validation into the right location in the script, such
+ * that it is highly constrained in its effect (for example, it had to match a
+ * set of whitelisted words).
+ *
+ * A SafeScript can be constructed via security-reviewed unchecked
+ * conversions. In this case producers of SafeScript must ensure themselves that
+ * the SafeScript does not contain unsafe script. Note in particular that
+ * {@code &lt;} is dangerous, even when inside JavaScript strings, and so should
+ * always be forbidden or JavaScript escaped in user controlled input. For
+ * example, if {@code &lt;/script&gt;&lt;script&gt;evil&lt;/script&gt;"} were
+ * interpolated inside a JavaScript string, it would break out of the context
+ * of the original script element and {@code evil} would execute. Also note
+ * that within an HTML script (raw text) element, HTML character references,
+ * such as "&lt;" are not allowed. See
+ * http://www.w3.org/TR/html5/scripting-1.html#restrictions-for-contents-of-script-elements.
+ *
+ * @see goog.html.SafeScript#fromConstant
+ * @constructor
+ * @final
+ * @struct
+ * @implements {goog.string.TypedString}
+ */
+goog.html.SafeScript = function() {
+ /**
+ * The contained value of this SafeScript. The field has a purposely
+ * ugly name to make (non-compiled) code that attempts to directly access this
+ * field stand out.
+ * @private {string}
+ */
+ this.privateDoNotAccessOrElseSafeScriptWrappedValue_ = '';
+
+ /**
+ * A type marker used to implement additional run-time type checking.
+ * @see goog.html.SafeScript#unwrap
+ * @const {!Object}
+ * @private
+ */
+ this.SAFE_SCRIPT_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ =
+ goog.html.SafeScript.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_;
+};
+
+
+/**
+ * @override
+ * @const
+ */
+goog.html.SafeScript.prototype.implementsGoogStringTypedString = true;
+
+
+/**
+ * Type marker for the SafeScript type, used to implement additional
+ * run-time type checking.
+ * @const {!Object}
+ * @private
+ */
+goog.html.SafeScript.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = {};
+
+
+/**
+ * Creates a SafeScript object from a compile-time constant string.
+ *
+ * @param {!goog.string.Const} script A compile-time-constant string from which
+ * to create a SafeScript.
+ * @return {!goog.html.SafeScript} A SafeScript object initialized to
+ * {@code script}.
+ */
+goog.html.SafeScript.fromConstant = function(script) {
+ var scriptString = goog.string.Const.unwrap(script);
+ if (scriptString.length === 0) {
+ return goog.html.SafeScript.EMPTY;
+ }
+ return goog.html.SafeScript.createSafeScriptSecurityPrivateDoNotAccessOrElse(
+ scriptString);
+};
+
+
+/**
+ * Returns this SafeScript's value as a string.
+ *
+ * IMPORTANT: In code where it is security relevant that an object's type is
+ * indeed {@code SafeScript}, use {@code goog.html.SafeScript.unwrap} instead of
+ * this method. If in doubt, assume that it's security relevant. In particular,
+ * note that goog.html functions which return a goog.html type do not guarantee
+ * the returned instance is of the right type. For example:
+ *
+ * <pre>
+ * var fakeSafeHtml = new String('fake');
+ * fakeSafeHtml.__proto__ = goog.html.SafeHtml.prototype;
+ * var newSafeHtml = goog.html.SafeHtml.htmlEscape(fakeSafeHtml);
+ * // newSafeHtml is just an alias for fakeSafeHtml, it's passed through by
+ * // goog.html.SafeHtml.htmlEscape() as fakeSafeHtml
+ * // instanceof goog.html.SafeHtml.
+ * </pre>
+ *
+ * @see goog.html.SafeScript#unwrap
+ * @override
+ */
+goog.html.SafeScript.prototype.getTypedStringValue = function() {
+ return this.privateDoNotAccessOrElseSafeScriptWrappedValue_;
+};
+
+
+if (goog.DEBUG) {
+ /**
+ * Returns a debug string-representation of this value.
+ *
+ * To obtain the actual string value wrapped in a SafeScript, use
+ * {@code goog.html.SafeScript.unwrap}.
+ *
+ * @see goog.html.SafeScript#unwrap
+ * @override
+ */
+ goog.html.SafeScript.prototype.toString = function() {
+ return 'SafeScript{' +
+ this.privateDoNotAccessOrElseSafeScriptWrappedValue_ + '}';
+ };
+}
+
+
+/**
+ * Performs a runtime check that the provided object is indeed a
+ * SafeScript object, and returns its value.
+ *
+ * @param {!goog.html.SafeScript} safeScript The object to extract from.
+ * @return {string} The safeScript object's contained string, unless
+ * the run-time type check fails. In that case, {@code unwrap} returns an
+ * innocuous string, or, if assertions are enabled, throws
+ * {@code goog.asserts.AssertionError}.
+ */
+goog.html.SafeScript.unwrap = function(safeScript) {
+ // Perform additional Run-time type-checking to ensure that
+ // safeScript is indeed an instance of the expected type. This
+ // provides some additional protection against security bugs due to
+ // application code that disables type checks.
+ // Specifically, the following checks are performed:
+ // 1. The object is an instance of the expected type.
+ // 2. The object is not an instance of a subclass.
+ // 3. The object carries a type marker for the expected type. "Faking" an
+ // object requires a reference to the type marker, which has names intended
+ // to stand out in code reviews.
+ if (safeScript instanceof goog.html.SafeScript &&
+ safeScript.constructor === goog.html.SafeScript &&
+ safeScript.SAFE_SCRIPT_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ ===
+ goog.html.SafeScript.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_) {
+ return safeScript.privateDoNotAccessOrElseSafeScriptWrappedValue_;
+ } else {
+ goog.asserts.fail('expected object of type SafeScript, got \'' +
+ safeScript + '\' of type ' + goog.typeOf(safeScript));
+ return 'type_error:SafeScript';
+ }
+};
+
+
+/**
+ * Package-internal utility method to create SafeScript instances.
+ *
+ * @param {string} script The string to initialize the SafeScript object with.
+ * @return {!goog.html.SafeScript} The initialized SafeScript object.
+ * @package
+ */
+goog.html.SafeScript.createSafeScriptSecurityPrivateDoNotAccessOrElse =
+ function(script) {
+ return new goog.html.SafeScript().initSecurityPrivateDoNotAccessOrElse_(
+ script);
+};
+
+
+/**
+ * Called from createSafeScriptSecurityPrivateDoNotAccessOrElse(). This
+ * method exists only so that the compiler can dead code eliminate static
+ * fields (like EMPTY) when they're not accessed.
+ * @param {string} script
+ * @return {!goog.html.SafeScript}
+ * @private
+ */
+goog.html.SafeScript.prototype.initSecurityPrivateDoNotAccessOrElse_ = function(
+ script) {
+ this.privateDoNotAccessOrElseSafeScriptWrappedValue_ = script;
+ return this;
+};
+
+
+/**
+ * A SafeScript instance corresponding to the empty string.
+ * @const {!goog.html.SafeScript}
+ */
+goog.html.SafeScript.EMPTY =
+ goog.html.SafeScript.createSafeScriptSecurityPrivateDoNotAccessOrElse('');
diff --git a/chromium/third_party/ink/closure/html/safestyle.js b/chromium/third_party/ink/closure/html/safestyle.js
new file mode 100644
index 00000000000..95ff10c146a
--- /dev/null
+++ b/chromium/third_party/ink/closure/html/safestyle.js
@@ -0,0 +1,560 @@
+// Copyright 2014 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview The SafeStyle type and its builders.
+ *
+ * TODO(xtof): Link to document stating type contract.
+ */
+
+goog.provide('goog.html.SafeStyle');
+
+goog.require('goog.array');
+goog.require('goog.asserts');
+goog.require('goog.html.SafeUrl');
+goog.require('goog.string');
+goog.require('goog.string.Const');
+goog.require('goog.string.TypedString');
+
+
+
+/**
+ * A string-like object which represents a sequence of CSS declarations
+ * ({@code propertyName1: propertyvalue1; propertyName2: propertyValue2; ...})
+ * and that carries the security type contract that its value, as a string,
+ * will not cause untrusted script execution (XSS) when evaluated as CSS in a
+ * browser.
+ *
+ * Instances of this type must be created via the factory methods
+ * ({@code goog.html.SafeStyle.create} or
+ * {@code goog.html.SafeStyle.fromConstant}) and not by invoking its
+ * constructor. The constructor intentionally takes no parameters and the type
+ * is immutable; hence only a default instance corresponding to the empty string
+ * can be obtained via constructor invocation.
+ *
+ * SafeStyle's string representation can safely be:
+ * <ul>
+ * <li>Interpolated as the content of a *quoted* HTML style attribute.
+ * However, the SafeStyle string *must be HTML-attribute-escaped* before
+ * interpolation.
+ * <li>Interpolated as the content of a {}-wrapped block within a stylesheet.
+ * '<' characters in the SafeStyle string *must be CSS-escaped* before
+ * interpolation. The SafeStyle string is also guaranteed not to be able
+ * to introduce new properties or elide existing ones.
+ * <li>Interpolated as the content of a {}-wrapped block within an HTML
+ * <style> element. '<' characters in the SafeStyle string
+ * *must be CSS-escaped* before interpolation.
+ * <li>Assigned to the style property of a DOM node. The SafeStyle string
+ * should not be escaped before being assigned to the property.
+ * </ul>
+ *
+ * A SafeStyle may never contain literal angle brackets. Otherwise, it could
+ * be unsafe to place a SafeStyle into a &lt;style&gt; tag (where it can't
+ * be HTML escaped). For example, if the SafeStyle containing
+ * "{@code font: 'foo &lt;style/&gt;&lt;script&gt;evil&lt;/script&gt;'}" were
+ * interpolated within a &lt;style&gt; tag, this would then break out of the
+ * style context into HTML.
+ *
+ * A SafeStyle may contain literal single or double quotes, and as such the
+ * entire style string must be escaped when used in a style attribute (if
+ * this were not the case, the string could contain a matching quote that
+ * would escape from the style attribute).
+ *
+ * Values of this type must be composable, i.e. for any two values
+ * {@code style1} and {@code style2} of this type,
+ * {@code goog.html.SafeStyle.unwrap(style1) +
+ * goog.html.SafeStyle.unwrap(style2)} must itself be a value that satisfies
+ * the SafeStyle type constraint. This requirement implies that for any value
+ * {@code style} of this type, {@code goog.html.SafeStyle.unwrap(style)} must
+ * not end in a "property value" or "property name" context. For example,
+ * a value of {@code background:url("} or {@code font-} would not satisfy the
+ * SafeStyle contract. This is because concatenating such strings with a
+ * second value that itself does not contain unsafe CSS can result in an
+ * overall string that does. For example, if {@code javascript:evil())"} is
+ * appended to {@code background:url("}, the resulting string may result in
+ * the execution of a malicious script.
+ *
+ * TODO(mlourenco): Consider whether we should implement UTF-8 interchange
+ * validity checks and blacklisting of newlines (including Unicode ones) and
+ * other whitespace characters (\t, \f). Document here if so and also update
+ * SafeStyle.fromConstant().
+ *
+ * The following example values comply with this type's contract:
+ * <ul>
+ * <li><pre>width: 1em;</pre>
+ * <li><pre>height:1em;</pre>
+ * <li><pre>width: 1em;height: 1em;</pre>
+ * <li><pre>background:url('http://url');</pre>
+ * </ul>
+ * In addition, the empty string is safe for use in a CSS attribute.
+ *
+ * The following example values do NOT comply with this type's contract:
+ * <ul>
+ * <li><pre>background: red</pre> (missing a trailing semi-colon)
+ * <li><pre>background:</pre> (missing a value and a trailing semi-colon)
+ * <li><pre>1em</pre> (missing an attribute name, which provides context for
+ * the value)
+ * </ul>
+ *
+ * @see goog.html.SafeStyle#create
+ * @see goog.html.SafeStyle#fromConstant
+ * @see http://www.w3.org/TR/css3-syntax/
+ * @constructor
+ * @final
+ * @struct
+ * @implements {goog.string.TypedString}
+ */
+goog.html.SafeStyle = function() {
+ /**
+ * The contained value of this SafeStyle. The field has a purposely
+ * ugly name to make (non-compiled) code that attempts to directly access this
+ * field stand out.
+ * @private {string}
+ */
+ this.privateDoNotAccessOrElseSafeStyleWrappedValue_ = '';
+
+ /**
+ * A type marker used to implement additional run-time type checking.
+ * @see goog.html.SafeStyle#unwrap
+ * @const {!Object}
+ * @private
+ */
+ this.SAFE_STYLE_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ =
+ goog.html.SafeStyle.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_;
+};
+
+
+/**
+ * @override
+ * @const
+ */
+goog.html.SafeStyle.prototype.implementsGoogStringTypedString = true;
+
+
+/**
+ * Type marker for the SafeStyle type, used to implement additional
+ * run-time type checking.
+ * @const {!Object}
+ * @private
+ */
+goog.html.SafeStyle.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = {};
+
+
+/**
+ * Creates a SafeStyle object from a compile-time constant string.
+ *
+ * {@code style} should be in the format
+ * {@code name: value; [name: value; ...]} and must not have any < or >
+ * characters in it. This is so that SafeStyle's contract is preserved,
+ * allowing the SafeStyle to correctly be interpreted as a sequence of CSS
+ * declarations and without affecting the syntactic structure of any
+ * surrounding CSS and HTML.
+ *
+ * This method performs basic sanity checks on the format of {@code style}
+ * but does not constrain the format of {@code name} and {@code value}, except
+ * for disallowing tag characters.
+ *
+ * @param {!goog.string.Const} style A compile-time-constant string from which
+ * to create a SafeStyle.
+ * @return {!goog.html.SafeStyle} A SafeStyle object initialized to
+ * {@code style}.
+ */
+goog.html.SafeStyle.fromConstant = function(style) {
+ var styleString = goog.string.Const.unwrap(style);
+ if (styleString.length === 0) {
+ return goog.html.SafeStyle.EMPTY;
+ }
+ goog.html.SafeStyle.checkStyle_(styleString);
+ goog.asserts.assert(
+ goog.string.endsWith(styleString, ';'),
+ 'Last character of style string is not \';\': ' + styleString);
+ goog.asserts.assert(
+ goog.string.contains(styleString, ':'),
+ 'Style string must contain at least one \':\', to ' +
+ 'specify a "name: value" pair: ' + styleString);
+ return goog.html.SafeStyle.createSafeStyleSecurityPrivateDoNotAccessOrElse(
+ styleString);
+};
+
+
+/**
+ * Checks if the style definition is valid.
+ * @param {string} style
+ * @private
+ */
+goog.html.SafeStyle.checkStyle_ = function(style) {
+ goog.asserts.assert(
+ !/[<>]/.test(style), 'Forbidden characters in style string: ' + style);
+};
+
+
+/**
+ * Returns this SafeStyle's value as a string.
+ *
+ * IMPORTANT: In code where it is security relevant that an object's type is
+ * indeed {@code SafeStyle}, use {@code goog.html.SafeStyle.unwrap} instead of
+ * this method. If in doubt, assume that it's security relevant. In particular,
+ * note that goog.html functions which return a goog.html type do not guarantee
+ * the returned instance is of the right type. For example:
+ *
+ * <pre>
+ * var fakeSafeHtml = new String('fake');
+ * fakeSafeHtml.__proto__ = goog.html.SafeHtml.prototype;
+ * var newSafeHtml = goog.html.SafeHtml.htmlEscape(fakeSafeHtml);
+ * // newSafeHtml is just an alias for fakeSafeHtml, it's passed through by
+ * // goog.html.SafeHtml.htmlEscape() as fakeSafeHtml
+ * // instanceof goog.html.SafeHtml.
+ * </pre>
+ *
+ * @see goog.html.SafeStyle#unwrap
+ * @override
+ */
+goog.html.SafeStyle.prototype.getTypedStringValue = function() {
+ return this.privateDoNotAccessOrElseSafeStyleWrappedValue_;
+};
+
+
+if (goog.DEBUG) {
+ /**
+ * Returns a debug string-representation of this value.
+ *
+ * To obtain the actual string value wrapped in a SafeStyle, use
+ * {@code goog.html.SafeStyle.unwrap}.
+ *
+ * @see goog.html.SafeStyle#unwrap
+ * @override
+ */
+ goog.html.SafeStyle.prototype.toString = function() {
+ return 'SafeStyle{' + this.privateDoNotAccessOrElseSafeStyleWrappedValue_ +
+ '}';
+ };
+}
+
+
+/**
+ * Performs a runtime check that the provided object is indeed a
+ * SafeStyle object, and returns its value.
+ *
+ * @param {!goog.html.SafeStyle} safeStyle The object to extract from.
+ * @return {string} The safeStyle object's contained string, unless
+ * the run-time type check fails. In that case, {@code unwrap} returns an
+ * innocuous string, or, if assertions are enabled, throws
+ * {@code goog.asserts.AssertionError}.
+ */
+goog.html.SafeStyle.unwrap = function(safeStyle) {
+ // Perform additional Run-time type-checking to ensure that
+ // safeStyle is indeed an instance of the expected type. This
+ // provides some additional protection against security bugs due to
+ // application code that disables type checks.
+ // Specifically, the following checks are performed:
+ // 1. The object is an instance of the expected type.
+ // 2. The object is not an instance of a subclass.
+ // 3. The object carries a type marker for the expected type. "Faking" an
+ // object requires a reference to the type marker, which has names intended
+ // to stand out in code reviews.
+ if (safeStyle instanceof goog.html.SafeStyle &&
+ safeStyle.constructor === goog.html.SafeStyle &&
+ safeStyle.SAFE_STYLE_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ ===
+ goog.html.SafeStyle.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_) {
+ return safeStyle.privateDoNotAccessOrElseSafeStyleWrappedValue_;
+ } else {
+ goog.asserts.fail('expected object of type SafeStyle, got \'' +
+ safeStyle + '\' of type ' + goog.typeOf(safeStyle));
+ return 'type_error:SafeStyle';
+ }
+};
+
+
+/**
+ * Package-internal utility method to create SafeStyle instances.
+ *
+ * @param {string} style The string to initialize the SafeStyle object with.
+ * @return {!goog.html.SafeStyle} The initialized SafeStyle object.
+ * @package
+ */
+goog.html.SafeStyle.createSafeStyleSecurityPrivateDoNotAccessOrElse = function(
+ style) {
+ return new goog.html.SafeStyle().initSecurityPrivateDoNotAccessOrElse_(style);
+};
+
+
+/**
+ * Called from createSafeStyleSecurityPrivateDoNotAccessOrElse(). This
+ * method exists only so that the compiler can dead code eliminate static
+ * fields (like EMPTY) when they're not accessed.
+ * @param {string} style
+ * @return {!goog.html.SafeStyle}
+ * @private
+ */
+goog.html.SafeStyle.prototype.initSecurityPrivateDoNotAccessOrElse_ = function(
+ style) {
+ this.privateDoNotAccessOrElseSafeStyleWrappedValue_ = style;
+ return this;
+};
+
+
+/**
+ * A SafeStyle instance corresponding to the empty string.
+ * @const {!goog.html.SafeStyle}
+ */
+goog.html.SafeStyle.EMPTY =
+ goog.html.SafeStyle.createSafeStyleSecurityPrivateDoNotAccessOrElse('');
+
+
+/**
+ * The innocuous string generated by goog.html.SafeStyle.create when passed
+ * an unsafe value.
+ * @const {string}
+ */
+goog.html.SafeStyle.INNOCUOUS_STRING = 'zClosurez';
+
+
+/**
+ * A single property value.
+ * @typedef {string|!goog.string.Const|!goog.html.SafeUrl}
+ */
+goog.html.SafeStyle.PropertyValue;
+
+
+/**
+ * Mapping of property names to their values.
+ * We don't support numbers even though some values might be numbers (e.g.
+ * line-height or 0 for any length). The reason is that most numeric values need
+ * units (e.g. '1px') and allowing numbers could cause users forgetting about
+ * them.
+ * @typedef {!Object<string, ?goog.html.SafeStyle.PropertyValue|
+ * ?Array<!goog.html.SafeStyle.PropertyValue>>}
+ */
+goog.html.SafeStyle.PropertyMap;
+
+
+/**
+ * Creates a new SafeStyle object from the properties specified in the map.
+ * @param {goog.html.SafeStyle.PropertyMap} map Mapping of property names to
+ * their values, for example {'margin': '1px'}. Names must consist of
+ * [-_a-zA-Z0-9]. Values might be strings consisting of
+ * [-,.'"%_!# a-zA-Z0-9], where " and ' must be properly balanced. We also
+ * allow simple functions like rgb() and url() which sanitizes its contents.
+ * Other values must be wrapped in goog.string.Const. URLs might be passed
+ * as goog.html.SafeUrl which will be wrapped into url(""). We also support
+ * array whose elements are joined with ' '. Null value causes skipping the
+ * property.
+ * @return {!goog.html.SafeStyle}
+ * @throws {Error} If invalid name is provided.
+ * @throws {goog.asserts.AssertionError} If invalid value is provided. With
+ * disabled assertions, invalid value is replaced by
+ * goog.html.SafeStyle.INNOCUOUS_STRING.
+ */
+goog.html.SafeStyle.create = function(map) {
+ var style = '';
+ for (var name in map) {
+ if (!/^[-_a-zA-Z0-9]+$/.test(name)) {
+ throw new Error('Name allows only [-_a-zA-Z0-9], got: ' + name);
+ }
+ var value = map[name];
+ if (value == null) {
+ continue;
+ }
+ if (goog.isArray(value)) {
+ value = goog.array.map(value, goog.html.SafeStyle.sanitizePropertyValue_)
+ .join(' ');
+ } else {
+ value = goog.html.SafeStyle.sanitizePropertyValue_(value);
+ }
+ style += name + ':' + value + ';';
+ }
+ if (!style) {
+ return goog.html.SafeStyle.EMPTY;
+ }
+ goog.html.SafeStyle.checkStyle_(style);
+ return goog.html.SafeStyle.createSafeStyleSecurityPrivateDoNotAccessOrElse(
+ style);
+};
+
+
+/**
+ * Checks and converts value to string.
+ * @param {!goog.html.SafeStyle.PropertyValue} value
+ * @return {string}
+ * @private
+ */
+goog.html.SafeStyle.sanitizePropertyValue_ = function(value) {
+ if (value instanceof goog.html.SafeUrl) {
+ var url = goog.html.SafeUrl.unwrap(value);
+ return 'url("' + url.replace(/</g, '%3c').replace(/[\\"]/g, '\\$&') + '")';
+ }
+ var result = value instanceof goog.string.Const ?
+ goog.string.Const.unwrap(value) :
+ goog.html.SafeStyle.sanitizePropertyValueString_(String(value));
+ // These characters can be used to change context and we don't want that even
+ // with const values.
+ goog.asserts.assert(!/[{;}]/.test(result), 'Value does not allow [{;}].');
+ return result;
+};
+
+
+/**
+ * Checks string value.
+ * @param {string} value
+ * @return {string}
+ * @private
+ */
+goog.html.SafeStyle.sanitizePropertyValueString_ = function(value) {
+ var valueWithoutFunctions =
+ value.replace(goog.html.SafeUrl.FUNCTIONS_RE_, '$1')
+ .replace(goog.html.SafeUrl.URL_RE_, 'url');
+ if (!goog.html.SafeStyle.VALUE_RE_.test(valueWithoutFunctions)) {
+ goog.asserts.fail(
+ 'String value allows only ' + goog.html.SafeStyle.VALUE_ALLOWED_CHARS_ +
+ ' and simple functions, got: ' + value);
+ return goog.html.SafeStyle.INNOCUOUS_STRING;
+ } else if (!goog.html.SafeStyle.hasBalancedQuotes_(value)) {
+ goog.asserts.fail('String value requires balanced quotes, got: ' + value);
+ return goog.html.SafeStyle.INNOCUOUS_STRING;
+ }
+ return goog.html.SafeStyle.sanitizeUrl_(value);
+};
+
+
+/**
+ * Checks that quotes (" and ') are properly balanced inside a string. Assumes
+ * that neither escape (\) nor any other character that could result in
+ * breaking out of a string parsing context are allowed;
+ * see http://www.w3.org/TR/css3-syntax/#string-token-diagram.
+ * @param {string} value Untrusted CSS property value.
+ * @return {boolean} True if property value is safe with respect to quote
+ * balancedness.
+ * @private
+ */
+goog.html.SafeStyle.hasBalancedQuotes_ = function(value) {
+ var outsideSingle = true;
+ var outsideDouble = true;
+ for (var i = 0; i < value.length; i++) {
+ var c = value.charAt(i);
+ if (c == "'" && outsideDouble) {
+ outsideSingle = !outsideSingle;
+ } else if (c == '"' && outsideSingle) {
+ outsideDouble = !outsideDouble;
+ }
+ }
+ return outsideSingle && outsideDouble;
+};
+
+
+/**
+ * Characters allowed in goog.html.SafeStyle.VALUE_RE_.
+ * @private {string}
+ */
+goog.html.SafeStyle.VALUE_ALLOWED_CHARS_ = '[-,."\'%_!# a-zA-Z0-9]';
+
+
+/**
+ * Regular expression for safe values.
+ *
+ * Quotes (" and ') are allowed, but a check must be done elsewhere to ensure
+ * they're balanced.
+ *
+ * ',' allows multiple values to be assigned to the same property
+ * (e.g. background-attachment or font-family) and hence could allow
+ * multiple values to get injected, but that should pose no risk of XSS.
+ *
+ * The expression checks only for XSS safety, not for CSS validity.
+ * @const {!RegExp}
+ * @private
+ */
+goog.html.SafeStyle.VALUE_RE_ =
+ new RegExp('^' + goog.html.SafeStyle.VALUE_ALLOWED_CHARS_ + '+$');
+
+
+/**
+ * Regular expression for url(). We support URLs allowed by
+ * https://www.w3.org/TR/css-syntax-3/#url-token-diagram without using escape
+ * sequences. Use percent-encoding if you need to use special characters like
+ * backslash.
+ * @private @const {!RegExp}
+ */
+goog.html.SafeUrl.URL_RE_ = new RegExp(
+ '\\b(url\\([ \t\n]*)(' +
+ '\'[ -&(-\\[\\]-~]*\'' + // Printable characters except ' and \.
+ '|"[ !#-\\[\\]-~]*"' + // Printable characters except " and \.
+ '|[!#-&*-\\[\\]-~]*' + // Printable characters except [ "'()\\].
+ ')([ \t\n]*\\))',
+ 'g');
+
+
+/**
+ * Regular expression for simple functions.
+ * @private @const {!RegExp}
+ */
+goog.html.SafeUrl.FUNCTIONS_RE_ = new RegExp(
+ '\\b(hsl|hsla|rgb|rgba|(rotate|scale|translate)(X|Y|Z|3d)?)' +
+ '\\([-0-9a-z.%, ]+\\)',
+ 'g');
+
+
+/**
+ * Sanitize URLs inside url().
+ *
+ * NOTE: We could also consider using CSS.escape once that's available in the
+ * browsers. However, loosely matching URL e.g. with url\(.*\) and then escaping
+ * the contents would result in a slightly different language than CSS leading
+ * to confusion of users. E.g. url(")") is valid in CSS but it would be invalid
+ * as seen by our parser. On the other hand, url(\) is invalid in CSS but our
+ * parser would be fine with it.
+ *
+ * @param {string} value Untrusted CSS property value.
+ * @return {string}
+ * @private
+ */
+goog.html.SafeStyle.sanitizeUrl_ = function(value) {
+ return value.replace(
+ goog.html.SafeUrl.URL_RE_, function(match, before, url, after) {
+ var quote = '';
+ url = url.replace(/^(['"])(.*)\1$/, function(match, start, inside) {
+ quote = start;
+ return inside;
+ });
+ var sanitized = goog.html.SafeUrl.sanitize(url).getTypedStringValue();
+ return before + quote + sanitized + quote + after;
+ });
+};
+
+
+/**
+ * Creates a new SafeStyle object by concatenating the values.
+ * @param {...(!goog.html.SafeStyle|!Array<!goog.html.SafeStyle>)} var_args
+ * SafeStyles to concatenate.
+ * @return {!goog.html.SafeStyle}
+ */
+goog.html.SafeStyle.concat = function(var_args) {
+ var style = '';
+
+ /**
+ * @param {!goog.html.SafeStyle|!Array<!goog.html.SafeStyle>} argument
+ */
+ var addArgument = function(argument) {
+ if (goog.isArray(argument)) {
+ goog.array.forEach(argument, addArgument);
+ } else {
+ style += goog.html.SafeStyle.unwrap(argument);
+ }
+ };
+
+ goog.array.forEach(arguments, addArgument);
+ if (!style) {
+ return goog.html.SafeStyle.EMPTY;
+ }
+ return goog.html.SafeStyle.createSafeStyleSecurityPrivateDoNotAccessOrElse(
+ style);
+};
diff --git a/chromium/third_party/ink/closure/html/safestylesheet.js b/chromium/third_party/ink/closure/html/safestylesheet.js
new file mode 100644
index 00000000000..f690dbda471
--- /dev/null
+++ b/chromium/third_party/ink/closure/html/safestylesheet.js
@@ -0,0 +1,344 @@
+// Copyright 2014 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview The SafeStyleSheet type and its builders.
+ *
+ * TODO(xtof): Link to document stating type contract.
+ */
+
+goog.provide('goog.html.SafeStyleSheet');
+
+goog.require('goog.array');
+goog.require('goog.asserts');
+goog.require('goog.html.SafeStyle');
+goog.require('goog.object');
+goog.require('goog.string');
+goog.require('goog.string.Const');
+goog.require('goog.string.TypedString');
+
+
+
+/**
+ * A string-like object which represents a CSS style sheet and that carries the
+ * security type contract that its value, as a string, will not cause untrusted
+ * script execution (XSS) when evaluated as CSS in a browser.
+ *
+ * Instances of this type must be created via the factory method
+ * {@code goog.html.SafeStyleSheet.fromConstant} and not by invoking its
+ * constructor. The constructor intentionally takes no parameters and the type
+ * is immutable; hence only a default instance corresponding to the empty string
+ * can be obtained via constructor invocation.
+ *
+ * A SafeStyleSheet's string representation can safely be interpolated as the
+ * content of a style element within HTML. The SafeStyleSheet string should
+ * not be escaped before interpolation.
+ *
+ * Values of this type must be composable, i.e. for any two values
+ * {@code styleSheet1} and {@code styleSheet2} of this type,
+ * {@code goog.html.SafeStyleSheet.unwrap(styleSheet1) +
+ * goog.html.SafeStyleSheet.unwrap(styleSheet2)} must itself be a value that
+ * satisfies the SafeStyleSheet type constraint. This requirement implies that
+ * for any value {@code styleSheet} of this type,
+ * {@code goog.html.SafeStyleSheet.unwrap(styleSheet1)} must end in
+ * "beginning of rule" context.
+
+ * A SafeStyleSheet can be constructed via security-reviewed unchecked
+ * conversions. In this case producers of SafeStyleSheet must ensure themselves
+ * that the SafeStyleSheet does not contain unsafe script. Note in particular
+ * that {@code &lt;} is dangerous, even when inside CSS strings, and so should
+ * always be forbidden or CSS-escaped in user controlled input. For example, if
+ * {@code &lt;/style&gt;&lt;script&gt;evil&lt;/script&gt;"} were interpolated
+ * inside a CSS string, it would break out of the context of the original
+ * style element and {@code evil} would execute. Also note that within an HTML
+ * style (raw text) element, HTML character references, such as
+ * {@code &amp;lt;}, are not allowed. See
+ *
+ http://www.w3.org/TR/html5/scripting-1.html#restrictions-for-contents-of-script-elements
+ * (similar considerations apply to the style element).
+ *
+ * @see goog.html.SafeStyleSheet#fromConstant
+ * @constructor
+ * @final
+ * @struct
+ * @implements {goog.string.TypedString}
+ */
+goog.html.SafeStyleSheet = function() {
+ /**
+ * The contained value of this SafeStyleSheet. The field has a purposely
+ * ugly name to make (non-compiled) code that attempts to directly access this
+ * field stand out.
+ * @private {string}
+ */
+ this.privateDoNotAccessOrElseSafeStyleSheetWrappedValue_ = '';
+
+ /**
+ * A type marker used to implement additional run-time type checking.
+ * @see goog.html.SafeStyleSheet#unwrap
+ * @const {!Object}
+ * @private
+ */
+ this.SAFE_STYLE_SHEET_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ =
+ goog.html.SafeStyleSheet.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_;
+};
+
+
+/**
+ * @override
+ * @const
+ */
+goog.html.SafeStyleSheet.prototype.implementsGoogStringTypedString = true;
+
+
+/**
+ * Type marker for the SafeStyleSheet type, used to implement additional
+ * run-time type checking.
+ * @const {!Object}
+ * @private
+ */
+goog.html.SafeStyleSheet.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = {};
+
+
+/**
+ * Creates a style sheet consisting of one selector and one style definition.
+ * Use {@link goog.html.SafeStyleSheet.concat} to create longer style sheets.
+ * This function doesn't support @import, @media and similar constructs.
+ * @param {string} selector CSS selector, e.g. '#id' or 'tag .class, #id'. We
+ * support CSS3 selectors: https://w3.org/TR/css3-selectors/#selectors.
+ * @param {!goog.html.SafeStyle.PropertyMap|!goog.html.SafeStyle} style Style
+ * definition associated with the selector.
+ * @return {!goog.html.SafeStyleSheet}
+ * @throws {Error} If invalid selector is provided.
+ */
+goog.html.SafeStyleSheet.createRule = function(selector, style) {
+ if (goog.string.contains(selector, '<')) {
+ throw new Error('Selector does not allow \'<\', got: ' + selector);
+ }
+
+ // Remove strings.
+ var selectorToCheck =
+ selector.replace(/('|")((?!\1)[^\r\n\f\\]|\\[\s\S])*\1/g, '');
+
+ // Check characters allowed in CSS3 selectors.
+ if (!/^[-_a-zA-Z0-9#.:* ,>+~[\]()=^$|]+$/.test(selectorToCheck)) {
+ throw new Error(
+ 'Selector allows only [-_a-zA-Z0-9#.:* ,>+~[\\]()=^$|] and ' +
+ 'strings, got: ' + selector);
+ }
+
+ // Check balanced () and [].
+ if (!goog.html.SafeStyleSheet.hasBalancedBrackets_(selectorToCheck)) {
+ throw new Error('() and [] in selector must be balanced, got: ' + selector);
+ }
+
+ if (!(style instanceof goog.html.SafeStyle)) {
+ style = goog.html.SafeStyle.create(style);
+ }
+ var styleSheet = selector + '{' + goog.html.SafeStyle.unwrap(style) + '}';
+ return goog.html.SafeStyleSheet
+ .createSafeStyleSheetSecurityPrivateDoNotAccessOrElse(styleSheet);
+};
+
+
+/**
+ * Checks if a string has balanced () and [] brackets.
+ * @param {string} s String to check.
+ * @return {boolean}
+ * @private
+ */
+goog.html.SafeStyleSheet.hasBalancedBrackets_ = function(s) {
+ var brackets = {'(': ')', '[': ']'};
+ var expectedBrackets = [];
+ for (var i = 0; i < s.length; i++) {
+ var ch = s[i];
+ if (brackets[ch]) {
+ expectedBrackets.push(brackets[ch]);
+ } else if (goog.object.contains(brackets, ch)) {
+ if (expectedBrackets.pop() != ch) {
+ return false;
+ }
+ }
+ }
+ return expectedBrackets.length == 0;
+};
+
+
+/**
+ * Creates a new SafeStyleSheet object by concatenating values.
+ * @param {...(!goog.html.SafeStyleSheet|!Array<!goog.html.SafeStyleSheet>)}
+ * var_args Values to concatenate.
+ * @return {!goog.html.SafeStyleSheet}
+ */
+goog.html.SafeStyleSheet.concat = function(var_args) {
+ var result = '';
+
+ /**
+ * @param {!goog.html.SafeStyleSheet|!Array<!goog.html.SafeStyleSheet>}
+ * argument
+ */
+ var addArgument = function(argument) {
+ if (goog.isArray(argument)) {
+ goog.array.forEach(argument, addArgument);
+ } else {
+ result += goog.html.SafeStyleSheet.unwrap(argument);
+ }
+ };
+
+ goog.array.forEach(arguments, addArgument);
+ return goog.html.SafeStyleSheet
+ .createSafeStyleSheetSecurityPrivateDoNotAccessOrElse(result);
+};
+
+
+/**
+ * Creates a SafeStyleSheet object from a compile-time constant string.
+ *
+ * {@code styleSheet} must not have any &lt; characters in it, so that
+ * the syntactic structure of the surrounding HTML is not affected.
+ *
+ * @param {!goog.string.Const} styleSheet A compile-time-constant string from
+ * which to create a SafeStyleSheet.
+ * @return {!goog.html.SafeStyleSheet} A SafeStyleSheet object initialized to
+ * {@code styleSheet}.
+ */
+goog.html.SafeStyleSheet.fromConstant = function(styleSheet) {
+ var styleSheetString = goog.string.Const.unwrap(styleSheet);
+ if (styleSheetString.length === 0) {
+ return goog.html.SafeStyleSheet.EMPTY;
+ }
+ // > is a valid character in CSS selectors and there's no strict need to
+ // block it if we already block <.
+ goog.asserts.assert(
+ !goog.string.contains(styleSheetString, '<'),
+ "Forbidden '<' character in style sheet string: " + styleSheetString);
+ return goog.html.SafeStyleSheet
+ .createSafeStyleSheetSecurityPrivateDoNotAccessOrElse(styleSheetString);
+};
+
+
+/**
+ * Returns this SafeStyleSheet's value as a string.
+ *
+ * IMPORTANT: In code where it is security relevant that an object's type is
+ * indeed {@code SafeStyleSheet}, use {@code goog.html.SafeStyleSheet.unwrap}
+ * instead of this method. If in doubt, assume that it's security relevant. In
+ * particular, note that goog.html functions which return a goog.html type do
+ * not guarantee the returned instance is of the right type. For example:
+ *
+ * <pre>
+ * var fakeSafeHtml = new String('fake');
+ * fakeSafeHtml.__proto__ = goog.html.SafeHtml.prototype;
+ * var newSafeHtml = goog.html.SafeHtml.htmlEscape(fakeSafeHtml);
+ * // newSafeHtml is just an alias for fakeSafeHtml, it's passed through by
+ * // goog.html.SafeHtml.htmlEscape() as fakeSafeHtml
+ * // instanceof goog.html.SafeHtml.
+ * </pre>
+ *
+ * @see goog.html.SafeStyleSheet#unwrap
+ * @override
+ */
+goog.html.SafeStyleSheet.prototype.getTypedStringValue = function() {
+ return this.privateDoNotAccessOrElseSafeStyleSheetWrappedValue_;
+};
+
+
+if (goog.DEBUG) {
+ /**
+ * Returns a debug string-representation of this value.
+ *
+ * To obtain the actual string value wrapped in a SafeStyleSheet, use
+ * {@code goog.html.SafeStyleSheet.unwrap}.
+ *
+ * @see goog.html.SafeStyleSheet#unwrap
+ * @override
+ */
+ goog.html.SafeStyleSheet.prototype.toString = function() {
+ return 'SafeStyleSheet{' +
+ this.privateDoNotAccessOrElseSafeStyleSheetWrappedValue_ + '}';
+ };
+}
+
+
+/**
+ * Performs a runtime check that the provided object is indeed a
+ * SafeStyleSheet object, and returns its value.
+ *
+ * @param {!goog.html.SafeStyleSheet} safeStyleSheet The object to extract from.
+ * @return {string} The safeStyleSheet object's contained string, unless
+ * the run-time type check fails. In that case, {@code unwrap} returns an
+ * innocuous string, or, if assertions are enabled, throws
+ * {@code goog.asserts.AssertionError}.
+ */
+goog.html.SafeStyleSheet.unwrap = function(safeStyleSheet) {
+ // Perform additional Run-time type-checking to ensure that
+ // safeStyleSheet is indeed an instance of the expected type. This
+ // provides some additional protection against security bugs due to
+ // application code that disables type checks.
+ // Specifically, the following checks are performed:
+ // 1. The object is an instance of the expected type.
+ // 2. The object is not an instance of a subclass.
+ // 3. The object carries a type marker for the expected type. "Faking" an
+ // object requires a reference to the type marker, which has names intended
+ // to stand out in code reviews.
+ if (safeStyleSheet instanceof goog.html.SafeStyleSheet &&
+ safeStyleSheet.constructor === goog.html.SafeStyleSheet &&
+ safeStyleSheet
+ .SAFE_STYLE_SHEET_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ ===
+ goog.html.SafeStyleSheet.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_) {
+ return safeStyleSheet.privateDoNotAccessOrElseSafeStyleSheetWrappedValue_;
+ } else {
+ goog.asserts.fail('expected object of type SafeStyleSheet, got \'' +
+ safeStyleSheet + '\' of type ' + goog.typeOf(safeStyleSheet));
+ return 'type_error:SafeStyleSheet';
+ }
+};
+
+
+/**
+ * Package-internal utility method to create SafeStyleSheet instances.
+ *
+ * @param {string} styleSheet The string to initialize the SafeStyleSheet
+ * object with.
+ * @return {!goog.html.SafeStyleSheet} The initialized SafeStyleSheet object.
+ * @package
+ */
+goog.html.SafeStyleSheet.createSafeStyleSheetSecurityPrivateDoNotAccessOrElse =
+ function(styleSheet) {
+ return new goog.html.SafeStyleSheet().initSecurityPrivateDoNotAccessOrElse_(
+ styleSheet);
+};
+
+
+/**
+ * Called from createSafeStyleSheetSecurityPrivateDoNotAccessOrElse(). This
+ * method exists only so that the compiler can dead code eliminate static
+ * fields (like EMPTY) when they're not accessed.
+ * @param {string} styleSheet
+ * @return {!goog.html.SafeStyleSheet}
+ * @private
+ */
+goog.html.SafeStyleSheet.prototype.initSecurityPrivateDoNotAccessOrElse_ =
+ function(styleSheet) {
+ this.privateDoNotAccessOrElseSafeStyleSheetWrappedValue_ = styleSheet;
+ return this;
+};
+
+
+/**
+ * A SafeStyleSheet instance corresponding to the empty string.
+ * @const {!goog.html.SafeStyleSheet}
+ */
+goog.html.SafeStyleSheet.EMPTY =
+ goog.html.SafeStyleSheet
+ .createSafeStyleSheetSecurityPrivateDoNotAccessOrElse('');
diff --git a/chromium/third_party/ink/closure/html/safeurl.js b/chromium/third_party/ink/closure/html/safeurl.js
new file mode 100644
index 00000000000..3d1ee112550
--- /dev/null
+++ b/chromium/third_party/ink/closure/html/safeurl.js
@@ -0,0 +1,454 @@
+// Copyright 2013 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview The SafeUrl type and its builders.
+ *
+ * TODO(xtof): Link to document stating type contract.
+ */
+
+goog.provide('goog.html.SafeUrl');
+
+goog.require('goog.asserts');
+goog.require('goog.fs.url');
+goog.require('goog.html.TrustedResourceUrl');
+goog.require('goog.i18n.bidi.Dir');
+goog.require('goog.i18n.bidi.DirectionalString');
+goog.require('goog.string');
+goog.require('goog.string.Const');
+goog.require('goog.string.TypedString');
+
+
+
+/**
+ * A string that is safe to use in URL context in DOM APIs and HTML documents.
+ *
+ * A SafeUrl is a string-like object that carries the security type contract
+ * that its value as a string will not cause untrusted script execution
+ * when evaluated as a hyperlink URL in a browser.
+ *
+ * Values of this type are guaranteed to be safe to use in URL/hyperlink
+ * contexts, such as assignment to URL-valued DOM properties, in the sense that
+ * the use will not result in a Cross-Site-Scripting vulnerability. Similarly,
+ * SafeUrls can be interpolated into the URL context of an HTML template (e.g.,
+ * inside a href attribute). However, appropriate HTML-escaping must still be
+ * applied.
+ *
+ * Note that, as documented in {@code goog.html.SafeUrl.unwrap}, this type's
+ * contract does not guarantee that instances are safe to interpolate into HTML
+ * without appropriate escaping.
+ *
+ * Note also that this type's contract does not imply any guarantees regarding
+ * the resource the URL refers to. In particular, SafeUrls are <b>not</b>
+ * safe to use in a context where the referred-to resource is interpreted as
+ * trusted code, e.g., as the src of a script tag.
+ *
+ * Instances of this type must be created via the factory methods
+ * ({@code goog.html.SafeUrl.fromConstant}, {@code goog.html.SafeUrl.sanitize}),
+ * etc and not by invoking its constructor. The constructor intentionally
+ * takes no parameters and the type is immutable; hence only a default instance
+ * corresponding to the empty string can be obtained via constructor invocation.
+ *
+ * @see goog.html.SafeUrl#fromConstant
+ * @see goog.html.SafeUrl#from
+ * @see goog.html.SafeUrl#sanitize
+ * @constructor
+ * @final
+ * @struct
+ * @implements {goog.i18n.bidi.DirectionalString}
+ * @implements {goog.string.TypedString}
+ */
+goog.html.SafeUrl = function() {
+ /**
+ * The contained value of this SafeUrl. The field has a purposely ugly
+ * name to make (non-compiled) code that attempts to directly access this
+ * field stand out.
+ * @private {string}
+ */
+ this.privateDoNotAccessOrElseSafeHtmlWrappedValue_ = '';
+
+ /**
+ * A type marker used to implement additional run-time type checking.
+ * @see goog.html.SafeUrl#unwrap
+ * @const {!Object}
+ * @private
+ */
+ this.SAFE_URL_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ =
+ goog.html.SafeUrl.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_;
+};
+
+
+/**
+ * The innocuous string generated by goog.html.SafeUrl.sanitize when passed
+ * an unsafe URL.
+ *
+ * about:invalid is registered in
+ * http://www.w3.org/TR/css3-values/#about-invalid.
+ * http://tools.ietf.org/html/rfc6694#section-2.2.1 permits about URLs to
+ * contain a fragment, which is not to be considered when determining if an
+ * about URL is well-known.
+ *
+ * Using about:invalid seems preferable to using a fixed data URL, since
+ * browsers might choose to not report CSP violations on it, as legitimate
+ * CSS function calls to attr() can result in this URL being produced. It is
+ * also a standard URL which matches exactly the semantics we need:
+ * "The about:invalid URI references a non-existent document with a generic
+ * error condition. It can be used when a URI is necessary, but the default
+ * value shouldn't be resolveable as any type of document".
+ *
+ * @const {string}
+ */
+goog.html.SafeUrl.INNOCUOUS_STRING = 'about:invalid#zClosurez';
+
+
+/**
+ * @override
+ * @const
+ */
+goog.html.SafeUrl.prototype.implementsGoogStringTypedString = true;
+
+
+/**
+ * Returns this SafeUrl's value a string.
+ *
+ * IMPORTANT: In code where it is security relevant that an object's type is
+ * indeed {@code SafeUrl}, use {@code goog.html.SafeUrl.unwrap} instead of this
+ * method. If in doubt, assume that it's security relevant. In particular, note
+ * that goog.html functions which return a goog.html type do not guarantee that
+ * the returned instance is of the right type. For example:
+ *
+ * <pre>
+ * var fakeSafeHtml = new String('fake');
+ * fakeSafeHtml.__proto__ = goog.html.SafeHtml.prototype;
+ * var newSafeHtml = goog.html.SafeHtml.htmlEscape(fakeSafeHtml);
+ * // newSafeHtml is just an alias for fakeSafeHtml, it's passed through by
+ * // goog.html.SafeHtml.htmlEscape() as fakeSafeHtml instanceof
+ * // goog.html.SafeHtml.
+ * </pre>
+ *
+ * IMPORTANT: The guarantees of the SafeUrl type contract only extend to the
+ * behavior of browsers when interpreting URLs. Values of SafeUrl objects MUST
+ * be appropriately escaped before embedding in a HTML document. Note that the
+ * required escaping is context-sensitive (e.g. a different escaping is
+ * required for embedding a URL in a style property within a style
+ * attribute, as opposed to embedding in a href attribute).
+ *
+ * @see goog.html.SafeUrl#unwrap
+ * @override
+ */
+goog.html.SafeUrl.prototype.getTypedStringValue = function() {
+ return this.privateDoNotAccessOrElseSafeHtmlWrappedValue_;
+};
+
+
+/**
+ * @override
+ * @const
+ */
+goog.html.SafeUrl.prototype.implementsGoogI18nBidiDirectionalString = true;
+
+
+/**
+ * Returns this URLs directionality, which is always {@code LTR}.
+ * @override
+ */
+goog.html.SafeUrl.prototype.getDirection = function() {
+ return goog.i18n.bidi.Dir.LTR;
+};
+
+
+if (goog.DEBUG) {
+ /**
+ * Returns a debug string-representation of this value.
+ *
+ * To obtain the actual string value wrapped in a SafeUrl, use
+ * {@code goog.html.SafeUrl.unwrap}.
+ *
+ * @see goog.html.SafeUrl#unwrap
+ * @override
+ */
+ goog.html.SafeUrl.prototype.toString = function() {
+ return 'SafeUrl{' + this.privateDoNotAccessOrElseSafeHtmlWrappedValue_ +
+ '}';
+ };
+}
+
+
+/**
+ * Performs a runtime check that the provided object is indeed a SafeUrl
+ * object, and returns its value.
+ *
+ * IMPORTANT: The guarantees of the SafeUrl type contract only extend to the
+ * behavior of browsers when interpreting URLs. Values of SafeUrl objects MUST
+ * be appropriately escaped before embedding in a HTML document. Note that the
+ * required escaping is context-sensitive (e.g. a different escaping is
+ * required for embedding a URL in a style property within a style
+ * attribute, as opposed to embedding in a href attribute).
+ *
+ * @param {!goog.html.SafeUrl} safeUrl The object to extract from.
+ * @return {string} The SafeUrl object's contained string, unless the run-time
+ * type check fails. In that case, {@code unwrap} returns an innocuous
+ * string, or, if assertions are enabled, throws
+ * {@code goog.asserts.AssertionError}.
+ */
+goog.html.SafeUrl.unwrap = function(safeUrl) {
+ // Perform additional Run-time type-checking to ensure that safeUrl is indeed
+ // an instance of the expected type. This provides some additional protection
+ // against security bugs due to application code that disables type checks.
+ // Specifically, the following checks are performed:
+ // 1. The object is an instance of the expected type.
+ // 2. The object is not an instance of a subclass.
+ // 3. The object carries a type marker for the expected type. "Faking" an
+ // object requires a reference to the type marker, which has names intended
+ // to stand out in code reviews.
+ if (safeUrl instanceof goog.html.SafeUrl &&
+ safeUrl.constructor === goog.html.SafeUrl &&
+ safeUrl.SAFE_URL_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ ===
+ goog.html.SafeUrl.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_) {
+ return safeUrl.privateDoNotAccessOrElseSafeHtmlWrappedValue_;
+ } else {
+ goog.asserts.fail('expected object of type SafeUrl, got \'' +
+ safeUrl + '\' of type ' + goog.typeOf(safeUrl));
+ return 'type_error:SafeUrl';
+ }
+};
+
+
+/**
+ * Creates a SafeUrl object from a compile-time constant string.
+ *
+ * Compile-time constant strings are inherently program-controlled and hence
+ * trusted.
+ *
+ * @param {!goog.string.Const} url A compile-time-constant string from which to
+ * create a SafeUrl.
+ * @return {!goog.html.SafeUrl} A SafeUrl object initialized to {@code url}.
+ */
+goog.html.SafeUrl.fromConstant = function(url) {
+ return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(
+ goog.string.Const.unwrap(url));
+};
+
+
+/**
+ * A pattern that matches Blob or data types that can have SafeUrls created
+ * from URL.createObjectURL(blob) or via a data: URI.
+ * @const
+ * @private
+ */
+goog.html.SAFE_MIME_TYPE_PATTERN_ = new RegExp(
+ '^(?:audio/(?:3gpp|3gpp2|aac|midi|mp4|mpeg|ogg|x-m4a|x-wav|webm)|' +
+ 'image/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|' +
+ 'text/csv|' +
+ 'video/(?:mpeg|mp4|ogg|webm))$',
+ 'i');
+
+
+/**
+ * Creates a SafeUrl wrapping a blob URL for the given {@code blob}.
+ *
+ * The blob URL is created with {@code URL.createObjectURL}. If the MIME type
+ * for {@code blob} is not of a known safe audio, image or video MIME type,
+ * then the SafeUrl will wrap {@link #INNOCUOUS_STRING}.
+ *
+ * @see http://www.w3.org/TR/FileAPI/#url
+ * @param {!Blob} blob
+ * @return {!goog.html.SafeUrl} The blob URL, or an innocuous string wrapped
+ * as a SafeUrl.
+ */
+goog.html.SafeUrl.fromBlob = function(blob) {
+ var url = goog.html.SAFE_MIME_TYPE_PATTERN_.test(blob.type) ?
+ goog.fs.url.createObjectUrl(blob) :
+ goog.html.SafeUrl.INNOCUOUS_STRING;
+ return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url);
+};
+
+
+/**
+ * Matches a base-64 data URL, with the first match group being the MIME type.
+ * @const
+ * @private
+ */
+goog.html.DATA_URL_PATTERN_ = /^data:([^;,]*);base64,[a-z0-9+\/]+=*$/i;
+
+
+/**
+ * Creates a SafeUrl wrapping a data: URL, after validating it matches a
+ * known-safe audio, image or video MIME type.
+ *
+ * @param {string} dataUrl A valid base64 data URL with one of the whitelisted
+ * audio, image or video MIME types.
+ * @return {!goog.html.SafeUrl} A matching safe URL, or {@link INNOCUOUS_STRING}
+ * wrapped as a SafeUrl if it does not pass.
+ */
+goog.html.SafeUrl.fromDataUrl = function(dataUrl) {
+ // There's a slight risk here that a browser sniffs the content type if it
+ // doesn't know the MIME type and executes HTML within the data: URL. For this
+ // to cause XSS it would also have to execute the HTML in the same origin
+ // of the page with the link. It seems unlikely that both of these will
+ // happen, particularly in not really old IEs.
+ var match = dataUrl.match(goog.html.DATA_URL_PATTERN_);
+ var valid = match && goog.html.SAFE_MIME_TYPE_PATTERN_.test(match[1]);
+ return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(
+ valid ? dataUrl : goog.html.SafeUrl.INNOCUOUS_STRING);
+};
+
+
+/**
+ * Creates a SafeUrl wrapping a tel: URL.
+ *
+ * @param {string} telUrl A tel URL.
+ * @return {!goog.html.SafeUrl} A matching safe URL, or {@link INNOCUOUS_STRING}
+ * wrapped as a SafeUrl if it does not pass.
+ */
+goog.html.SafeUrl.fromTelUrl = function(telUrl) {
+ // There's a risk that a tel: URL could immediately place a call once
+ // clicked, without requiring user confirmation. For that reason it is
+ // handled in this separate function.
+ if (!goog.string.caseInsensitiveStartsWith(telUrl, 'tel:')) {
+ telUrl = goog.html.SafeUrl.INNOCUOUS_STRING;
+ }
+ return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(
+ telUrl);
+};
+
+
+/**
+ * Creates a SafeUrl from TrustedResourceUrl. This is safe because
+ * TrustedResourceUrl is more tightly restricted than SafeUrl.
+ *
+ * @param {!goog.html.TrustedResourceUrl} trustedResourceUrl
+ * @return {!goog.html.SafeUrl}
+ */
+goog.html.SafeUrl.fromTrustedResourceUrl = function(trustedResourceUrl) {
+ return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(
+ goog.html.TrustedResourceUrl.unwrap(trustedResourceUrl));
+};
+
+
+/**
+ * A pattern that recognizes a commonly useful subset of URLs that satisfy
+ * the SafeUrl contract.
+ *
+ * This regular expression matches a subset of URLs that will not cause script
+ * execution if used in URL context within a HTML document. Specifically, this
+ * regular expression matches if (comment from here on and regex copied from
+ * Soy's EscapingConventions):
+ * (1) Either a protocol in a whitelist (http, https, mailto or ftp).
+ * (2) or no protocol. A protocol must be followed by a colon. The below
+ * allows that by allowing colons only after one of the characters [/?#].
+ * A colon after a hash (#) must be in the fragment.
+ * Otherwise, a colon after a (?) must be in a query.
+ * Otherwise, a colon after a single solidus (/) must be in a path.
+ * Otherwise, a colon after a double solidus (//) must be in the authority
+ * (before port).
+ *
+ * @private
+ * @const {!RegExp}
+ */
+goog.html.SAFE_URL_PATTERN_ =
+ /^(?:(?:https?|mailto|ftp):|[^:/?#]*(?:[/?#]|$))/i;
+
+
+/**
+ * Creates a SafeUrl object from {@code url}. If {@code url} is a
+ * goog.html.SafeUrl then it is simply returned. Otherwise the input string is
+ * validated to match a pattern of commonly used safe URLs.
+ *
+ * {@code url} may be a URL with the http, https, mailto or ftp scheme,
+ * or a relative URL (i.e., a URL without a scheme; specifically, a
+ * scheme-relative, absolute-path-relative, or path-relative URL).
+ *
+ * @see http://url.spec.whatwg.org/#concept-relative-url
+ * @param {string|!goog.string.TypedString} url The URL to validate.
+ * @return {!goog.html.SafeUrl} The validated URL, wrapped as a SafeUrl.
+ */
+goog.html.SafeUrl.sanitize = function(url) {
+ if (url instanceof goog.html.SafeUrl) {
+ return url;
+ } else if (url.implementsGoogStringTypedString) {
+ url = url.getTypedStringValue();
+ } else {
+ url = String(url);
+ }
+ if (!goog.html.SAFE_URL_PATTERN_.test(url)) {
+ url = goog.html.SafeUrl.INNOCUOUS_STRING;
+ }
+ return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url);
+};
+
+/**
+ * Creates a SafeUrl object from {@code url}. If {@code url} is a
+ * goog.html.SafeUrl then it is simply returned. Otherwise the input string is
+ * validated to match a pattern of commonly used safe URLs.
+ *
+ * {@code url} may be a URL with the http, https, mailto or ftp scheme,
+ * or a relative URL (i.e., a URL without a scheme; specifically, a
+ * scheme-relative, absolute-path-relative, or path-relative URL).
+ *
+ * This function asserts (using goog.asserts) that the URL matches this pattern.
+ * If it does not, in addition to failing the assert, an innocous URL will be
+ * returned.
+ *
+ * @see http://url.spec.whatwg.org/#concept-relative-url
+ * @param {string|!goog.string.TypedString} url The URL to validate.
+ * @return {!goog.html.SafeUrl} The validated URL, wrapped as a SafeUrl.
+ */
+goog.html.SafeUrl.sanitizeAssertUnchanged = function(url) {
+ if (url instanceof goog.html.SafeUrl) {
+ return url;
+ } else if (url.implementsGoogStringTypedString) {
+ url = url.getTypedStringValue();
+ } else {
+ url = String(url);
+ }
+ if (!goog.asserts.assert(goog.html.SAFE_URL_PATTERN_.test(url))) {
+ url = goog.html.SafeUrl.INNOCUOUS_STRING;
+ }
+ return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url);
+};
+
+
+
+/**
+ * Type marker for the SafeUrl type, used to implement additional run-time
+ * type checking.
+ * @const {!Object}
+ * @private
+ */
+goog.html.SafeUrl.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = {};
+
+
+/**
+ * Package-internal utility method to create SafeUrl instances.
+ *
+ * @param {string} url The string to initialize the SafeUrl object with.
+ * @return {!goog.html.SafeUrl} The initialized SafeUrl object.
+ * @package
+ */
+goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse = function(
+ url) {
+ var safeUrl = new goog.html.SafeUrl();
+ safeUrl.privateDoNotAccessOrElseSafeHtmlWrappedValue_ = url;
+ return safeUrl;
+};
+
+
+/**
+ * A SafeUrl corresponding to the special about:blank url.
+ * @const {!goog.html.SafeUrl}
+ */
+goog.html.SafeUrl.ABOUT_BLANK =
+ goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(
+ 'about:blank');
diff --git a/chromium/third_party/ink/closure/html/trustedresourceurl.js b/chromium/third_party/ink/closure/html/trustedresourceurl.js
new file mode 100644
index 00000000000..b7a67bc41bb
--- /dev/null
+++ b/chromium/third_party/ink/closure/html/trustedresourceurl.js
@@ -0,0 +1,412 @@
+// Copyright 2013 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview The TrustedResourceUrl type and its builders.
+ *
+ * TODO(xtof): Link to document stating type contract.
+ */
+
+goog.provide('goog.html.TrustedResourceUrl');
+
+goog.require('goog.asserts');
+goog.require('goog.i18n.bidi.Dir');
+goog.require('goog.i18n.bidi.DirectionalString');
+goog.require('goog.string.Const');
+goog.require('goog.string.TypedString');
+
+
+
+/**
+ * A URL which is under application control and from which script, CSS, and
+ * other resources that represent executable code, can be fetched.
+ *
+ * Given that the URL can only be constructed from strings under application
+ * control and is used to load resources, bugs resulting in a malformed URL
+ * should not have a security impact and are likely to be easily detectable
+ * during testing. Given the wide number of non-RFC compliant URLs in use,
+ * stricter validation could prevent some applications from being able to use
+ * this type.
+ *
+ * Instances of this type must be created via the factory method,
+ * ({@code fromConstant}, {@code fromConstants}, {@code format} or {@code
+ * formatWithParams}), and not by invoking its constructor. The constructor
+ * intentionally takes no parameters and the type is immutable; hence only a
+ * default instance corresponding to the empty string can be obtained via
+ * constructor invocation.
+ *
+ * @see goog.html.TrustedResourceUrl#fromConstant
+ * @constructor
+ * @final
+ * @struct
+ * @implements {goog.i18n.bidi.DirectionalString}
+ * @implements {goog.string.TypedString}
+ */
+goog.html.TrustedResourceUrl = function() {
+ /**
+ * The contained value of this TrustedResourceUrl. The field has a purposely
+ * ugly name to make (non-compiled) code that attempts to directly access this
+ * field stand out.
+ * @private {string}
+ */
+ this.privateDoNotAccessOrElseTrustedResourceUrlWrappedValue_ = '';
+
+ /**
+ * A type marker used to implement additional run-time type checking.
+ * @see goog.html.TrustedResourceUrl#unwrap
+ * @const {!Object}
+ * @private
+ */
+ this.TRUSTED_RESOURCE_URL_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ =
+ goog.html.TrustedResourceUrl.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_;
+};
+
+
+/**
+ * @override
+ * @const
+ */
+goog.html.TrustedResourceUrl.prototype.implementsGoogStringTypedString = true;
+
+
+/**
+ * Returns this TrustedResourceUrl's value as a string.
+ *
+ * IMPORTANT: In code where it is security relevant that an object's type is
+ * indeed {@code TrustedResourceUrl}, use
+ * {@code goog.html.TrustedResourceUrl.unwrap} instead of this method. If in
+ * doubt, assume that it's security relevant. In particular, note that
+ * goog.html functions which return a goog.html type do not guarantee that
+ * the returned instance is of the right type. For example:
+ *
+ * <pre>
+ * var fakeSafeHtml = new String('fake');
+ * fakeSafeHtml.__proto__ = goog.html.SafeHtml.prototype;
+ * var newSafeHtml = goog.html.SafeHtml.htmlEscape(fakeSafeHtml);
+ * // newSafeHtml is just an alias for fakeSafeHtml, it's passed through by
+ * // goog.html.SafeHtml.htmlEscape() as fakeSafeHtml instanceof
+ * // goog.html.SafeHtml.
+ * </pre>
+ *
+ * @see goog.html.TrustedResourceUrl#unwrap
+ * @override
+ */
+goog.html.TrustedResourceUrl.prototype.getTypedStringValue = function() {
+ return this.privateDoNotAccessOrElseTrustedResourceUrlWrappedValue_;
+};
+
+
+/**
+ * @override
+ * @const
+ */
+goog.html.TrustedResourceUrl.prototype.implementsGoogI18nBidiDirectionalString =
+ true;
+
+
+/**
+ * Returns this URLs directionality, which is always {@code LTR}.
+ * @override
+ */
+goog.html.TrustedResourceUrl.prototype.getDirection = function() {
+ return goog.i18n.bidi.Dir.LTR;
+};
+
+
+if (goog.DEBUG) {
+ /**
+ * Returns a debug string-representation of this value.
+ *
+ * To obtain the actual string value wrapped in a TrustedResourceUrl, use
+ * {@code goog.html.TrustedResourceUrl.unwrap}.
+ *
+ * @see goog.html.TrustedResourceUrl#unwrap
+ * @override
+ */
+ goog.html.TrustedResourceUrl.prototype.toString = function() {
+ return 'TrustedResourceUrl{' +
+ this.privateDoNotAccessOrElseTrustedResourceUrlWrappedValue_ + '}';
+ };
+}
+
+
+/**
+ * Performs a runtime check that the provided object is indeed a
+ * TrustedResourceUrl object, and returns its value.
+ *
+ * @param {!goog.html.TrustedResourceUrl} trustedResourceUrl The object to
+ * extract from.
+ * @return {string} The trustedResourceUrl object's contained string, unless
+ * the run-time type check fails. In that case, {@code unwrap} returns an
+ * innocuous string, or, if assertions are enabled, throws
+ * {@code goog.asserts.AssertionError}.
+ */
+goog.html.TrustedResourceUrl.unwrap = function(trustedResourceUrl) {
+ // Perform additional Run-time type-checking to ensure that
+ // trustedResourceUrl is indeed an instance of the expected type. This
+ // provides some additional protection against security bugs due to
+ // application code that disables type checks.
+ // Specifically, the following checks are performed:
+ // 1. The object is an instance of the expected type.
+ // 2. The object is not an instance of a subclass.
+ // 3. The object carries a type marker for the expected type. "Faking" an
+ // object requires a reference to the type marker, which has names intended
+ // to stand out in code reviews.
+ if (trustedResourceUrl instanceof goog.html.TrustedResourceUrl &&
+ trustedResourceUrl.constructor === goog.html.TrustedResourceUrl &&
+ trustedResourceUrl
+ .TRUSTED_RESOURCE_URL_TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ ===
+ goog.html.TrustedResourceUrl
+ .TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_) {
+ return trustedResourceUrl
+ .privateDoNotAccessOrElseTrustedResourceUrlWrappedValue_;
+ } else {
+ goog.asserts.fail('expected object of type TrustedResourceUrl, got \'' +
+ trustedResourceUrl + '\' of type ' + goog.typeOf(trustedResourceUrl));
+ return 'type_error:TrustedResourceUrl';
+ }
+};
+
+
+/**
+ * Creates a TrustedResourceUrl from a format string and arguments.
+ *
+ * The arguments for interpolation into the format string map labels to values.
+ * Values of type `goog.string.Const` are interpolated without modifcation.
+ * Values of other types are cast to string and encoded with
+ * encodeURIComponent.
+ *
+ * `%{<label>}` markers are used in the format string to indicate locations
+ * to be interpolated with the valued mapped to the given label. `<label>`
+ * must contain only alphanumeric and `_` characters.
+ *
+ * The format string must start with one of the following:
+ * - `https://<origin>/`
+ * - `//<origin>/`
+ * - `/<pathStart>`
+ * - `about:blank`
+ *
+ * `<origin>` must contain only alphanumeric or any of the following: `-.:[]`.
+ * `<pathStart>` is any character except `/` and `\`.
+ *
+ * Example usage:
+ *
+ * var url = goog.html.TrustedResourceUrl.format(goog.string.Const.from(
+ * 'https://www.google.com/search?q=%{query}), {'query': searchTerm});
+ *
+ * var url = goog.html.TrustedResourceUrl.format(goog.string.Const.from(
+ * '//www.youtube.com/v/%{videoId}?hl=en&fs=1%{autoplay}'), {
+ * 'videoId': videoId,
+ * 'autoplay': opt_autoplay ?
+ * goog.string.Const.from('&autoplay=1') : goog.string.Const.EMPTY
+ * });
+ *
+ * While this function can be used to create a TrustedResourceUrl from only
+ * constants, fromConstant() and fromConstants() are generally preferable for
+ * that purpose.
+ *
+ * @param {!goog.string.Const} format The format string.
+ * @param {!Object<string, (string|number|!goog.string.Const)>} args Mapping
+ * of labels to values to be interpolated into the format string.
+ * goog.string.Const values are interpolated without encoding.
+ * @return {!goog.html.TrustedResourceUrl}
+ * @throws {!Error} On an invalid format string or if a label used in the
+ * the format string is not present in args.
+ */
+goog.html.TrustedResourceUrl.format = function(format, args) {
+ var result = goog.html.TrustedResourceUrl.format_(format, args);
+ return goog.html.TrustedResourceUrl
+ .createTrustedResourceUrlSecurityPrivateDoNotAccessOrElse(result);
+};
+
+
+/**
+ * String version of TrustedResourceUrl.format.
+ * @param {!goog.string.Const} format
+ * @param {!Object<string, (string|number|!goog.string.Const)>} args
+ * @return {string}
+ * @throws {!Error}
+ * @private
+ */
+goog.html.TrustedResourceUrl.format_ = function(format, args) {
+ var formatStr = goog.string.Const.unwrap(format);
+ if (!goog.html.TrustedResourceUrl.BASE_URL_.test(formatStr)) {
+ throw new Error('Invalid TrustedResourceUrl format: ' + formatStr);
+ }
+ return formatStr.replace(
+ goog.html.TrustedResourceUrl.FORMAT_MARKER_, function(match, id) {
+ if (!Object.prototype.hasOwnProperty.call(args, id)) {
+ throw new Error(
+ 'Found marker, "' + id + '", in format string, "' + formatStr +
+ '", but no valid label mapping found ' +
+ 'in args: ' + JSON.stringify(args));
+ }
+ var arg = args[id];
+ if (arg instanceof goog.string.Const) {
+ return goog.string.Const.unwrap(arg);
+ } else {
+ return encodeURIComponent(String(arg));
+ }
+ });
+};
+
+
+/**
+ * @private @const {!RegExp}
+ */
+goog.html.TrustedResourceUrl.FORMAT_MARKER_ = /%{(\w+)}/g;
+
+
+/**
+ * The URL must be absolute, scheme-relative or path-absolute. So it must
+ * start with:
+ * - https:// followed by allowed origin characters.
+ * - // followed by allowed origin characters.
+ * - / not followed by / or \. There will only be an absolute path.
+ *
+ * Based on
+ * https://url.spec.whatwg.org/commit-snapshots/56b74ce7cca8883eab62e9a12666e2fac665d03d/#url-parsing
+ * an initial / which is not followed by another / or \ will end up in the "path
+ * state" and from there it can only go to "fragment state" and "query state".
+ *
+ * We don't enforce a well-formed domain name. So '.' or '1.2' are valid.
+ * That's ok because the origin comes from a compile-time constant.
+ *
+ * A regular expression is used instead of goog.uri for several reasons:
+ * - Strictness. E.g. we don't want any userinfo component and we don't
+ * want '/./, nor \' in the first path component.
+ * - Small trusted base. goog.uri is generic and might need to change,
+ * reasoning about all the ways it can parse a URL now and in the future
+ * is error-prone.
+ * - Code size. We expect many calls to .format(), many of which might
+ * not be using goog.uri.
+ * - Simplicity. Using goog.uri would likely not result in simpler nor shorter
+ * code.
+ * @private @const {!RegExp}
+ */
+goog.html.TrustedResourceUrl.BASE_URL_ =
+ /^(?:https:)?\/\/[0-9a-z.:[\]-]+\/|^\/[^\/\\]|^about:blank(#|$)/i;
+
+
+/**
+ * Formats the URL same as TrustedResourceUrl.format and then adds extra URL
+ * parameters.
+ *
+ * Example usage:
+ *
+ * // Creates '//www.youtube.com/v/abc?autoplay=1' for videoId='abc' and
+ * // opt_autoplay=1. Creates '//www.youtube.com/v/abc' for videoId='abc'
+ * // and opt_autoplay=undefined.
+ * var url = goog.html.TrustedResourceUrl.formatWithParams(
+ * goog.string.Const.from('//www.youtube.com/v/%{videoId}'),
+ * {'videoId': videoId},
+ * {'autoplay': opt_autoplay});
+ *
+ * @param {!goog.string.Const} format The format string.
+ * @param {!Object<string, (string|number|!goog.string.Const)>} args Mapping
+ * of labels to values to be interpolated into the format string.
+ * goog.string.Const values are interpolated without encoding.
+ * @param {!Object<string, *>} params Parameters to add to URL. Parameters with
+ * value {@code null} or {@code undefined} are skipped. Both keys and values
+ * are encoded. If the value is an array then the same parameter is added
+ * for every element in the array. Note that JavaScript doesn't guarantee
+ * the order of values in an object which might result in non-deterministic
+ * order of the parameters. However, browsers currently preserve the order.
+ * @return {!goog.html.TrustedResourceUrl}
+ * @throws {!Error} On an invalid format string or if a label used in the
+ * the format string is not present in args.
+ */
+goog.html.TrustedResourceUrl.formatWithParams = function(format, args, params) {
+ var url = goog.html.TrustedResourceUrl.format_(format, args);
+ var separator = /\?/.test(url) ? '&' : '?';
+ for (var key in params) {
+ var values = goog.isArray(params[key]) ? params[key] : [params[key]];
+ for (var i = 0; i < values.length; i++) {
+ if (values[i] == null) {
+ continue;
+ }
+ url += separator + encodeURIComponent(key) + '=' +
+ encodeURIComponent(String(values[i]));
+ separator = '&';
+ }
+ }
+ return goog.html.TrustedResourceUrl
+ .createTrustedResourceUrlSecurityPrivateDoNotAccessOrElse(url);
+};
+
+
+/**
+ * Creates a TrustedResourceUrl object from a compile-time constant string.
+ *
+ * Compile-time constant strings are inherently program-controlled and hence
+ * trusted.
+ *
+ * @param {!goog.string.Const} url A compile-time-constant string from which to
+ * create a TrustedResourceUrl.
+ * @return {!goog.html.TrustedResourceUrl} A TrustedResourceUrl object
+ * initialized to {@code url}.
+ */
+goog.html.TrustedResourceUrl.fromConstant = function(url) {
+ return goog.html.TrustedResourceUrl
+ .createTrustedResourceUrlSecurityPrivateDoNotAccessOrElse(
+ goog.string.Const.unwrap(url));
+};
+
+
+/**
+ * Creates a TrustedResourceUrl object from a compile-time constant strings.
+ *
+ * Compile-time constant strings are inherently program-controlled and hence
+ * trusted.
+ *
+ * @param {!Array<!goog.string.Const>} parts Compile-time-constant strings from
+ * which to create a TrustedResourceUrl.
+ * @return {!goog.html.TrustedResourceUrl} A TrustedResourceUrl object
+ * initialized to concatenation of {@code parts}.
+ */
+goog.html.TrustedResourceUrl.fromConstants = function(parts) {
+ var unwrapped = '';
+ for (var i = 0; i < parts.length; i++) {
+ unwrapped += goog.string.Const.unwrap(parts[i]);
+ }
+ return goog.html.TrustedResourceUrl
+ .createTrustedResourceUrlSecurityPrivateDoNotAccessOrElse(unwrapped);
+};
+
+
+/**
+ * Type marker for the TrustedResourceUrl type, used to implement additional
+ * run-time type checking.
+ * @const {!Object}
+ * @private
+ */
+goog.html.TrustedResourceUrl.TYPE_MARKER_GOOG_HTML_SECURITY_PRIVATE_ = {};
+
+
+/**
+ * Package-internal utility method to create TrustedResourceUrl instances.
+ *
+ * @param {string} url The string to initialize the TrustedResourceUrl object
+ * with.
+ * @return {!goog.html.TrustedResourceUrl} The initialized TrustedResourceUrl
+ * object.
+ * @package
+ */
+goog.html.TrustedResourceUrl
+ .createTrustedResourceUrlSecurityPrivateDoNotAccessOrElse = function(url) {
+ var trustedResourceUrl = new goog.html.TrustedResourceUrl();
+ trustedResourceUrl.privateDoNotAccessOrElseTrustedResourceUrlWrappedValue_ =
+ url;
+ return trustedResourceUrl;
+};
diff --git a/chromium/third_party/ink/closure/html/uncheckedconversions.js b/chromium/third_party/ink/closure/html/uncheckedconversions.js
new file mode 100644
index 00000000000..e75dfa2ce2d
--- /dev/null
+++ b/chromium/third_party/ink/closure/html/uncheckedconversions.js
@@ -0,0 +1,254 @@
+// Copyright 2013 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Unchecked conversions to create values of goog.html types from
+ * plain strings. Use of these functions could potentially result in instances
+ * of goog.html types that violate their type contracts, and hence result in
+ * security vulnerabilties.
+ *
+ * Therefore, all uses of the methods herein must be carefully security
+ * reviewed. Avoid use of the methods in this file whenever possible; instead
+ * prefer to create instances of goog.html types using inherently safe builders
+ * or template systems.
+ *
+ * MOE:begin_intracomment_strip
+ * See http://go/safehtml-unchecked for guidelines on using these functions.
+ * MOE:end_intracomment_strip
+ *
+ * MOE:begin_intracomment_strip
+ * MAINTAINERS: Use of these functions is detected with a Tricorder analyzer.
+ * If adding functions here also add them to analyzer's list at
+ * j/c/g/devtools/staticanalysis/pipeline/analyzers/shared/SafeHtmlAnalyzers.java.
+ * MOE:end_intracomment_strip
+ *
+ * @visibility {//javascript/closure/html:approved_for_unchecked_conversion}
+ * @visibility {//javascript/closure/bin/sizetests:__pkg__}
+ */
+
+
+goog.provide('goog.html.uncheckedconversions');
+
+goog.require('goog.asserts');
+goog.require('goog.html.SafeHtml');
+goog.require('goog.html.SafeScript');
+goog.require('goog.html.SafeStyle');
+goog.require('goog.html.SafeStyleSheet');
+goog.require('goog.html.SafeUrl');
+goog.require('goog.html.TrustedResourceUrl');
+goog.require('goog.string');
+goog.require('goog.string.Const');
+
+
+/**
+ * Performs an "unchecked conversion" to SafeHtml from a plain string that is
+ * known to satisfy the SafeHtml type contract.
+ *
+ * IMPORTANT: Uses of this method must be carefully security-reviewed to ensure
+ * that the value of {@code html} satisfies the SafeHtml type contract in all
+ * possible program states.
+ *
+ * MOE:begin_intracomment_strip
+ * See http://go/safehtml-unchecked for guidelines on using these functions.
+ * MOE:end_intracomment_strip
+ *
+ * @param {!goog.string.Const} justification A constant string explaining why
+ * this use of this method is safe. May include a security review ticket
+ * number.
+ * @param {string} html A string that is claimed to adhere to the SafeHtml
+ * contract.
+ * @param {?goog.i18n.bidi.Dir=} opt_dir The optional directionality of the
+ * SafeHtml to be constructed. A null or undefined value signifies an
+ * unknown directionality.
+ * @return {!goog.html.SafeHtml} The value of html, wrapped in a SafeHtml
+ * object.
+ */
+goog.html.uncheckedconversions.safeHtmlFromStringKnownToSatisfyTypeContract =
+ function(justification, html, opt_dir) {
+ // unwrap() called inside an assert so that justification can be optimized
+ // away in production code.
+ goog.asserts.assertString(
+ goog.string.Const.unwrap(justification), 'must provide justification');
+ goog.asserts.assert(
+ !goog.string.isEmptyOrWhitespace(goog.string.Const.unwrap(justification)),
+ 'must provide non-empty justification');
+ return goog.html.SafeHtml.createSafeHtmlSecurityPrivateDoNotAccessOrElse(
+ html, opt_dir || null);
+};
+
+
+/**
+ * Performs an "unchecked conversion" to SafeScript from a plain string that is
+ * known to satisfy the SafeScript type contract.
+ *
+ * IMPORTANT: Uses of this method must be carefully security-reviewed to ensure
+ * that the value of {@code script} satisfies the SafeScript type contract in
+ * all possible program states.
+ *
+ * MOE:begin_intracomment_strip
+ * See http://go/safehtml-unchecked for guidelines on using these functions.
+ * MOE:end_intracomment_strip
+ *
+ * @param {!goog.string.Const} justification A constant string explaining why
+ * this use of this method is safe. May include a security review ticket
+ * number.
+ * @param {string} script The string to wrap as a SafeScript.
+ * @return {!goog.html.SafeScript} The value of {@code script}, wrapped in a
+ * SafeScript object.
+ */
+goog.html.uncheckedconversions.safeScriptFromStringKnownToSatisfyTypeContract =
+ function(justification, script) {
+ // unwrap() called inside an assert so that justification can be optimized
+ // away in production code.
+ goog.asserts.assertString(
+ goog.string.Const.unwrap(justification), 'must provide justification');
+ goog.asserts.assert(
+ !goog.string.isEmptyOrWhitespace(goog.string.Const.unwrap(justification)),
+ 'must provide non-empty justification');
+ return goog.html.SafeScript.createSafeScriptSecurityPrivateDoNotAccessOrElse(
+ script);
+};
+
+
+/**
+ * Performs an "unchecked conversion" to SafeStyle from a plain string that is
+ * known to satisfy the SafeStyle type contract.
+ *
+ * IMPORTANT: Uses of this method must be carefully security-reviewed to ensure
+ * that the value of {@code style} satisfies the SafeStyle type contract in all
+ * possible program states.
+ *
+ * MOE:begin_intracomment_strip
+ * See http://go/safehtml-unchecked for guidelines on using these functions.
+ * MOE:end_intracomment_strip
+ *
+ * @param {!goog.string.Const} justification A constant string explaining why
+ * this use of this method is safe. May include a security review ticket
+ * number.
+ * @param {string} style The string to wrap as a SafeStyle.
+ * @return {!goog.html.SafeStyle} The value of {@code style}, wrapped in a
+ * SafeStyle object.
+ */
+goog.html.uncheckedconversions.safeStyleFromStringKnownToSatisfyTypeContract =
+ function(justification, style) {
+ // unwrap() called inside an assert so that justification can be optimized
+ // away in production code.
+ goog.asserts.assertString(
+ goog.string.Const.unwrap(justification), 'must provide justification');
+ goog.asserts.assert(
+ !goog.string.isEmptyOrWhitespace(goog.string.Const.unwrap(justification)),
+ 'must provide non-empty justification');
+ return goog.html.SafeStyle.createSafeStyleSecurityPrivateDoNotAccessOrElse(
+ style);
+};
+
+
+/**
+ * Performs an "unchecked conversion" to SafeStyleSheet from a plain string
+ * that is known to satisfy the SafeStyleSheet type contract.
+ *
+ * IMPORTANT: Uses of this method must be carefully security-reviewed to ensure
+ * that the value of {@code styleSheet} satisfies the SafeStyleSheet type
+ * contract in all possible program states.
+ *
+ * MOE:begin_intracomment_strip
+ * See http://go/safehtml-unchecked for guidelines on using these functions.
+ * MOE:end_intracomment_strip
+ *
+ * @param {!goog.string.Const} justification A constant string explaining why
+ * this use of this method is safe. May include a security review ticket
+ * number.
+ * @param {string} styleSheet The string to wrap as a SafeStyleSheet.
+ * @return {!goog.html.SafeStyleSheet} The value of {@code styleSheet}, wrapped
+ * in a SafeStyleSheet object.
+ */
+goog.html.uncheckedconversions
+ .safeStyleSheetFromStringKnownToSatisfyTypeContract = function(
+ justification, styleSheet) {
+ // unwrap() called inside an assert so that justification can be optimized
+ // away in production code.
+ goog.asserts.assertString(
+ goog.string.Const.unwrap(justification), 'must provide justification');
+ goog.asserts.assert(
+ !goog.string.isEmptyOrWhitespace(goog.string.Const.unwrap(justification)),
+ 'must provide non-empty justification');
+ return goog.html.SafeStyleSheet
+ .createSafeStyleSheetSecurityPrivateDoNotAccessOrElse(styleSheet);
+};
+
+
+/**
+ * Performs an "unchecked conversion" to SafeUrl from a plain string that is
+ * known to satisfy the SafeUrl type contract.
+ *
+ * IMPORTANT: Uses of this method must be carefully security-reviewed to ensure
+ * that the value of {@code url} satisfies the SafeUrl type contract in all
+ * possible program states.
+ *
+ * MOE:begin_intracomment_strip
+ * See http://go/safehtml-unchecked for guidelines on using these functions.
+ * MOE:end_intracomment_strip
+ *
+ * @param {!goog.string.Const} justification A constant string explaining why
+ * this use of this method is safe. May include a security review ticket
+ * number.
+ * @param {string} url The string to wrap as a SafeUrl.
+ * @return {!goog.html.SafeUrl} The value of {@code url}, wrapped in a SafeUrl
+ * object.
+ */
+goog.html.uncheckedconversions.safeUrlFromStringKnownToSatisfyTypeContract =
+ function(justification, url) {
+ // unwrap() called inside an assert so that justification can be optimized
+ // away in production code.
+ goog.asserts.assertString(
+ goog.string.Const.unwrap(justification), 'must provide justification');
+ goog.asserts.assert(
+ !goog.string.isEmptyOrWhitespace(goog.string.Const.unwrap(justification)),
+ 'must provide non-empty justification');
+ return goog.html.SafeUrl.createSafeUrlSecurityPrivateDoNotAccessOrElse(url);
+};
+
+
+/**
+ * Performs an "unchecked conversion" to TrustedResourceUrl from a plain string
+ * that is known to satisfy the TrustedResourceUrl type contract.
+ *
+ * IMPORTANT: Uses of this method must be carefully security-reviewed to ensure
+ * that the value of {@code url} satisfies the TrustedResourceUrl type contract
+ * in all possible program states.
+ *
+ * MOE:begin_intracomment_strip
+ * See http://go/safehtml-unchecked for guidelines on using these functions.
+ * MOE:end_intracomment_strip
+ *
+ * @param {!goog.string.Const} justification A constant string explaining why
+ * this use of this method is safe. May include a security review ticket
+ * number.
+ * @param {string} url The string to wrap as a TrustedResourceUrl.
+ * @return {!goog.html.TrustedResourceUrl} The value of {@code url}, wrapped in
+ * a TrustedResourceUrl object.
+ */
+goog.html.uncheckedconversions
+ .trustedResourceUrlFromStringKnownToSatisfyTypeContract = function(
+ justification, url) {
+ // unwrap() called inside an assert so that justification can be optimized
+ // away in production code.
+ goog.asserts.assertString(
+ goog.string.Const.unwrap(justification), 'must provide justification');
+ goog.asserts.assert(
+ !goog.string.isEmptyOrWhitespace(goog.string.Const.unwrap(justification)),
+ 'must provide non-empty justification');
+ return goog.html.TrustedResourceUrl
+ .createTrustedResourceUrlSecurityPrivateDoNotAccessOrElse(url);
+};
diff --git a/chromium/third_party/ink/closure/i18n/bidi.js b/chromium/third_party/ink/closure/i18n/bidi.js
new file mode 100644
index 00000000000..fcf11201f9d
--- /dev/null
+++ b/chromium/third_party/ink/closure/i18n/bidi.js
@@ -0,0 +1,878 @@
+// Copyright 2007 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Utility functions for supporting Bidi issues.
+ * @author shanjian@google.com (Shanjian Li)
+ * @author dougfelt@google.com (Doug Felt)
+ */
+
+
+/**
+ * Namespace for bidi supporting functions.
+ */
+goog.provide('goog.i18n.bidi');
+goog.provide('goog.i18n.bidi.Dir');
+goog.provide('goog.i18n.bidi.DirectionalString');
+goog.provide('goog.i18n.bidi.Format');
+
+
+/**
+ * @define {boolean} FORCE_RTL forces the {@link goog.i18n.bidi.IS_RTL} constant
+ * to say that the current locale is a RTL locale. This should only be used
+ * if you want to override the default behavior for deciding whether the
+ * current locale is RTL or not.
+ *
+ * {@see goog.i18n.bidi.IS_RTL}
+ */
+goog.define('goog.i18n.bidi.FORCE_RTL', false);
+
+
+/**
+ * Constant that defines whether or not the current locale is a RTL locale.
+ * If {@link goog.i18n.bidi.FORCE_RTL} is not true, this constant will default
+ * to check that {@link goog.LOCALE} is one of a few major RTL locales.
+ *
+ * <p>This is designed to be a maximally efficient compile-time constant. For
+ * example, for the default goog.LOCALE, compiling
+ * "if (goog.i18n.bidi.IS_RTL) alert('rtl') else {}" should produce no code. It
+ * is this design consideration that limits the implementation to only
+ * supporting a few major RTL locales, as opposed to the broader repertoire of
+ * something like goog.i18n.bidi.isRtlLanguage.
+ *
+ * <p>Since this constant refers to the directionality of the locale, it is up
+ * to the caller to determine if this constant should also be used for the
+ * direction of the UI.
+ *
+ * {@see goog.LOCALE}
+ *
+ * @type {boolean}
+ *
+ * TODO(aharon): write a test that checks that this is a compile-time constant.
+ */
+goog.i18n.bidi.IS_RTL = goog.i18n.bidi.FORCE_RTL ||
+ ((goog.LOCALE.substring(0, 2).toLowerCase() == 'ar' ||
+ goog.LOCALE.substring(0, 2).toLowerCase() == 'fa' ||
+ goog.LOCALE.substring(0, 2).toLowerCase() == 'he' ||
+ goog.LOCALE.substring(0, 2).toLowerCase() == 'iw' ||
+ goog.LOCALE.substring(0, 2).toLowerCase() == 'ps' ||
+ goog.LOCALE.substring(0, 2).toLowerCase() == 'sd' ||
+ goog.LOCALE.substring(0, 2).toLowerCase() == 'ug' ||
+ goog.LOCALE.substring(0, 2).toLowerCase() == 'ur' ||
+ goog.LOCALE.substring(0, 2).toLowerCase() == 'yi') &&
+ (goog.LOCALE.length == 2 || goog.LOCALE.substring(2, 3) == '-' ||
+ goog.LOCALE.substring(2, 3) == '_')) ||
+ (goog.LOCALE.length >= 3 &&
+ goog.LOCALE.substring(0, 3).toLowerCase() == 'ckb' &&
+ (goog.LOCALE.length == 3 || goog.LOCALE.substring(3, 4) == '-' ||
+ goog.LOCALE.substring(3, 4) == '_'));
+
+
+/**
+ * Unicode formatting characters and directionality string constants.
+ * @enum {string}
+ */
+goog.i18n.bidi.Format = {
+ /** Unicode "Left-To-Right Embedding" (LRE) character. */
+ LRE: '\u202A',
+ /** Unicode "Right-To-Left Embedding" (RLE) character. */
+ RLE: '\u202B',
+ /** Unicode "Pop Directional Formatting" (PDF) character. */
+ PDF: '\u202C',
+ /** Unicode "Left-To-Right Mark" (LRM) character. */
+ LRM: '\u200E',
+ /** Unicode "Right-To-Left Mark" (RLM) character. */
+ RLM: '\u200F'
+};
+
+
+/**
+ * Directionality enum.
+ * @enum {number}
+ */
+goog.i18n.bidi.Dir = {
+ /**
+ * Left-to-right.
+ */
+ LTR: 1,
+
+ /**
+ * Right-to-left.
+ */
+ RTL: -1,
+
+ /**
+ * Neither left-to-right nor right-to-left.
+ */
+ NEUTRAL: 0
+};
+
+
+/**
+ * 'right' string constant.
+ * @type {string}
+ */
+goog.i18n.bidi.RIGHT = 'right';
+
+
+/**
+ * 'left' string constant.
+ * @type {string}
+ */
+goog.i18n.bidi.LEFT = 'left';
+
+
+/**
+ * 'left' if locale is RTL, 'right' if not.
+ * @type {string}
+ */
+goog.i18n.bidi.I18N_RIGHT =
+ goog.i18n.bidi.IS_RTL ? goog.i18n.bidi.LEFT : goog.i18n.bidi.RIGHT;
+
+
+/**
+ * 'right' if locale is RTL, 'left' if not.
+ * @type {string}
+ */
+goog.i18n.bidi.I18N_LEFT =
+ goog.i18n.bidi.IS_RTL ? goog.i18n.bidi.RIGHT : goog.i18n.bidi.LEFT;
+
+
+/**
+ * Convert a directionality given in various formats to a goog.i18n.bidi.Dir
+ * constant. Useful for interaction with different standards of directionality
+ * representation.
+ *
+ * @param {goog.i18n.bidi.Dir|number|boolean|null} givenDir Directionality given
+ * in one of the following formats:
+ * 1. A goog.i18n.bidi.Dir constant.
+ * 2. A number (positive = LTR, negative = RTL, 0 = neutral).
+ * 3. A boolean (true = RTL, false = LTR).
+ * 4. A null for unknown directionality.
+ * @param {boolean=} opt_noNeutral Whether a givenDir of zero or
+ * goog.i18n.bidi.Dir.NEUTRAL should be treated as null, i.e. unknown, in
+ * order to preserve legacy behavior.
+ * @return {?goog.i18n.bidi.Dir} A goog.i18n.bidi.Dir constant matching the
+ * given directionality. If given null, returns null (i.e. unknown).
+ */
+goog.i18n.bidi.toDir = function(givenDir, opt_noNeutral) {
+ if (typeof givenDir == 'number') {
+ // This includes the non-null goog.i18n.bidi.Dir case.
+ return givenDir > 0 ? goog.i18n.bidi.Dir.LTR : givenDir < 0 ?
+ goog.i18n.bidi.Dir.RTL :
+ opt_noNeutral ? null : goog.i18n.bidi.Dir.NEUTRAL;
+ } else if (givenDir == null) {
+ return null;
+ } else {
+ // Must be typeof givenDir == 'boolean'.
+ return givenDir ? goog.i18n.bidi.Dir.RTL : goog.i18n.bidi.Dir.LTR;
+ }
+};
+
+
+/**
+ * A practical pattern to identify strong LTR characters. This pattern is not
+ * theoretically correct according to the Unicode standard. It is simplified for
+ * performance and small code size.
+ * @type {string}
+ * @private
+ */
+goog.i18n.bidi.ltrChars_ =
+ 'A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02B8\u0300-\u0590\u0800-\u1FFF' +
+ '\u200E\u2C00-\uFB1C\uFE00-\uFE6F\uFEFD-\uFFFF';
+
+
+/**
+ * A practical pattern to identify strong RTL character. This pattern is not
+ * theoretically correct according to the Unicode standard. It is simplified
+ * for performance and small code size.
+ * @type {string}
+ * @private
+ */
+goog.i18n.bidi.rtlChars_ =
+ '\u0591-\u06EF\u06FA-\u07FF\u200F\uFB1D-\uFDFF\uFE70-\uFEFC';
+
+
+/**
+ * Simplified regular expression for an HTML tag (opening or closing) or an HTML
+ * escape. We might want to skip over such expressions when estimating the text
+ * directionality.
+ * @type {RegExp}
+ * @private
+ */
+goog.i18n.bidi.htmlSkipReg_ = /<[^>]*>|&[^;]+;/g;
+
+
+/**
+ * Returns the input text with spaces instead of HTML tags or HTML escapes, if
+ * opt_isStripNeeded is true. Else returns the input as is.
+ * Useful for text directionality estimation.
+ * Note: the function should not be used in other contexts; it is not 100%
+ * correct, but rather a good-enough implementation for directionality
+ * estimation purposes.
+ * @param {string} str The given string.
+ * @param {boolean=} opt_isStripNeeded Whether to perform the stripping.
+ * Default: false (to retain consistency with calling functions).
+ * @return {string} The given string cleaned of HTML tags / escapes.
+ * @private
+ */
+goog.i18n.bidi.stripHtmlIfNeeded_ = function(str, opt_isStripNeeded) {
+ return opt_isStripNeeded ? str.replace(goog.i18n.bidi.htmlSkipReg_, '') : str;
+};
+
+
+/**
+ * Regular expression to check for RTL characters.
+ * @type {RegExp}
+ * @private
+ */
+goog.i18n.bidi.rtlCharReg_ = new RegExp('[' + goog.i18n.bidi.rtlChars_ + ']');
+
+
+/**
+ * Regular expression to check for LTR characters.
+ * @type {RegExp}
+ * @private
+ */
+goog.i18n.bidi.ltrCharReg_ = new RegExp('[' + goog.i18n.bidi.ltrChars_ + ']');
+
+
+/**
+ * Test whether the given string has any RTL characters in it.
+ * @param {string} str The given string that need to be tested.
+ * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
+ * Default: false.
+ * @return {boolean} Whether the string contains RTL characters.
+ */
+goog.i18n.bidi.hasAnyRtl = function(str, opt_isHtml) {
+ return goog.i18n.bidi.rtlCharReg_.test(
+ goog.i18n.bidi.stripHtmlIfNeeded_(str, opt_isHtml));
+};
+
+
+/**
+ * Test whether the given string has any RTL characters in it.
+ * @param {string} str The given string that need to be tested.
+ * @return {boolean} Whether the string contains RTL characters.
+ * @deprecated Use hasAnyRtl.
+ */
+goog.i18n.bidi.hasRtlChar = goog.i18n.bidi.hasAnyRtl;
+
+
+/**
+ * Test whether the given string has any LTR characters in it.
+ * @param {string} str The given string that need to be tested.
+ * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
+ * Default: false.
+ * @return {boolean} Whether the string contains LTR characters.
+ */
+goog.i18n.bidi.hasAnyLtr = function(str, opt_isHtml) {
+ return goog.i18n.bidi.ltrCharReg_.test(
+ goog.i18n.bidi.stripHtmlIfNeeded_(str, opt_isHtml));
+};
+
+
+/**
+ * Regular expression pattern to check if the first character in the string
+ * is LTR.
+ * @type {RegExp}
+ * @private
+ */
+goog.i18n.bidi.ltrRe_ = new RegExp('^[' + goog.i18n.bidi.ltrChars_ + ']');
+
+
+/**
+ * Regular expression pattern to check if the first character in the string
+ * is RTL.
+ * @type {RegExp}
+ * @private
+ */
+goog.i18n.bidi.rtlRe_ = new RegExp('^[' + goog.i18n.bidi.rtlChars_ + ']');
+
+
+/**
+ * Check if the first character in the string is RTL or not.
+ * @param {string} str The given string that need to be tested.
+ * @return {boolean} Whether the first character in str is an RTL char.
+ */
+goog.i18n.bidi.isRtlChar = function(str) {
+ return goog.i18n.bidi.rtlRe_.test(str);
+};
+
+
+/**
+ * Check if the first character in the string is LTR or not.
+ * @param {string} str The given string that need to be tested.
+ * @return {boolean} Whether the first character in str is an LTR char.
+ */
+goog.i18n.bidi.isLtrChar = function(str) {
+ return goog.i18n.bidi.ltrRe_.test(str);
+};
+
+
+/**
+ * Check if the first character in the string is neutral or not.
+ * @param {string} str The given string that need to be tested.
+ * @return {boolean} Whether the first character in str is a neutral char.
+ */
+goog.i18n.bidi.isNeutralChar = function(str) {
+ return !goog.i18n.bidi.isLtrChar(str) && !goog.i18n.bidi.isRtlChar(str);
+};
+
+
+/**
+ * Regular expressions to check if a piece of text is of LTR directionality
+ * on first character with strong directionality.
+ * @type {RegExp}
+ * @private
+ */
+goog.i18n.bidi.ltrDirCheckRe_ = new RegExp(
+ '^[^' + goog.i18n.bidi.rtlChars_ + ']*[' + goog.i18n.bidi.ltrChars_ + ']');
+
+
+/**
+ * Regular expressions to check if a piece of text is of RTL directionality
+ * on first character with strong directionality.
+ * @type {RegExp}
+ * @private
+ */
+goog.i18n.bidi.rtlDirCheckRe_ = new RegExp(
+ '^[^' + goog.i18n.bidi.ltrChars_ + ']*[' + goog.i18n.bidi.rtlChars_ + ']');
+
+
+/**
+ * Check whether the first strongly directional character (if any) is RTL.
+ * @param {string} str String being checked.
+ * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
+ * Default: false.
+ * @return {boolean} Whether RTL directionality is detected using the first
+ * strongly-directional character method.
+ */
+goog.i18n.bidi.startsWithRtl = function(str, opt_isHtml) {
+ return goog.i18n.bidi.rtlDirCheckRe_.test(
+ goog.i18n.bidi.stripHtmlIfNeeded_(str, opt_isHtml));
+};
+
+
+/**
+ * Check whether the first strongly directional character (if any) is RTL.
+ * @param {string} str String being checked.
+ * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
+ * Default: false.
+ * @return {boolean} Whether RTL directionality is detected using the first
+ * strongly-directional character method.
+ * @deprecated Use startsWithRtl.
+ */
+goog.i18n.bidi.isRtlText = goog.i18n.bidi.startsWithRtl;
+
+
+/**
+ * Check whether the first strongly directional character (if any) is LTR.
+ * @param {string} str String being checked.
+ * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
+ * Default: false.
+ * @return {boolean} Whether LTR directionality is detected using the first
+ * strongly-directional character method.
+ */
+goog.i18n.bidi.startsWithLtr = function(str, opt_isHtml) {
+ return goog.i18n.bidi.ltrDirCheckRe_.test(
+ goog.i18n.bidi.stripHtmlIfNeeded_(str, opt_isHtml));
+};
+
+
+/**
+ * Check whether the first strongly directional character (if any) is LTR.
+ * @param {string} str String being checked.
+ * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
+ * Default: false.
+ * @return {boolean} Whether LTR directionality is detected using the first
+ * strongly-directional character method.
+ * @deprecated Use startsWithLtr.
+ */
+goog.i18n.bidi.isLtrText = goog.i18n.bidi.startsWithLtr;
+
+
+/**
+ * Regular expression to check if a string looks like something that must
+ * always be LTR even in RTL text, e.g. a URL. When estimating the
+ * directionality of text containing these, we treat these as weakly LTR,
+ * like numbers.
+ * @type {RegExp}
+ * @private
+ */
+goog.i18n.bidi.isRequiredLtrRe_ = /^http:\/\/.*/;
+
+
+/**
+ * Check whether the input string either contains no strongly directional
+ * characters or looks like a url.
+ * @param {string} str String being checked.
+ * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
+ * Default: false.
+ * @return {boolean} Whether neutral directionality is detected.
+ */
+goog.i18n.bidi.isNeutralText = function(str, opt_isHtml) {
+ str = goog.i18n.bidi.stripHtmlIfNeeded_(str, opt_isHtml);
+ return goog.i18n.bidi.isRequiredLtrRe_.test(str) ||
+ !goog.i18n.bidi.hasAnyLtr(str) && !goog.i18n.bidi.hasAnyRtl(str);
+};
+
+
+/**
+ * Regular expressions to check if the last strongly-directional character in a
+ * piece of text is LTR.
+ * @type {RegExp}
+ * @private
+ */
+goog.i18n.bidi.ltrExitDirCheckRe_ = new RegExp(
+ '[' + goog.i18n.bidi.ltrChars_ + '][^' + goog.i18n.bidi.rtlChars_ + ']*$');
+
+
+/**
+ * Regular expressions to check if the last strongly-directional character in a
+ * piece of text is RTL.
+ * @type {RegExp}
+ * @private
+ */
+goog.i18n.bidi.rtlExitDirCheckRe_ = new RegExp(
+ '[' + goog.i18n.bidi.rtlChars_ + '][^' + goog.i18n.bidi.ltrChars_ + ']*$');
+
+
+/**
+ * Check if the exit directionality a piece of text is LTR, i.e. if the last
+ * strongly-directional character in the string is LTR.
+ * @param {string} str String being checked.
+ * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
+ * Default: false.
+ * @return {boolean} Whether LTR exit directionality was detected.
+ */
+goog.i18n.bidi.endsWithLtr = function(str, opt_isHtml) {
+ return goog.i18n.bidi.ltrExitDirCheckRe_.test(
+ goog.i18n.bidi.stripHtmlIfNeeded_(str, opt_isHtml));
+};
+
+
+/**
+ * Check if the exit directionality a piece of text is LTR, i.e. if the last
+ * strongly-directional character in the string is LTR.
+ * @param {string} str String being checked.
+ * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
+ * Default: false.
+ * @return {boolean} Whether LTR exit directionality was detected.
+ * @deprecated Use endsWithLtr.
+ */
+goog.i18n.bidi.isLtrExitText = goog.i18n.bidi.endsWithLtr;
+
+
+/**
+ * Check if the exit directionality a piece of text is RTL, i.e. if the last
+ * strongly-directional character in the string is RTL.
+ * @param {string} str String being checked.
+ * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
+ * Default: false.
+ * @return {boolean} Whether RTL exit directionality was detected.
+ */
+goog.i18n.bidi.endsWithRtl = function(str, opt_isHtml) {
+ return goog.i18n.bidi.rtlExitDirCheckRe_.test(
+ goog.i18n.bidi.stripHtmlIfNeeded_(str, opt_isHtml));
+};
+
+
+/**
+ * Check if the exit directionality a piece of text is RTL, i.e. if the last
+ * strongly-directional character in the string is RTL.
+ * @param {string} str String being checked.
+ * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
+ * Default: false.
+ * @return {boolean} Whether RTL exit directionality was detected.
+ * @deprecated Use endsWithRtl.
+ */
+goog.i18n.bidi.isRtlExitText = goog.i18n.bidi.endsWithRtl;
+
+
+/**
+ * A regular expression for matching right-to-left language codes.
+ * See {@link #isRtlLanguage} for the design.
+ * @type {RegExp}
+ * @private
+ */
+goog.i18n.bidi.rtlLocalesRe_ = new RegExp(
+ '^(ar|ckb|dv|he|iw|fa|nqo|ps|sd|ug|ur|yi|' +
+ '.*[-_](Arab|Hebr|Thaa|Nkoo|Tfng))' +
+ '(?!.*[-_](Latn|Cyrl)($|-|_))($|-|_)',
+ 'i');
+
+
+/**
+ * Check if a BCP 47 / III language code indicates an RTL language, i.e. either:
+ * - a language code explicitly specifying one of the right-to-left scripts,
+ * e.g. "az-Arab", or<p>
+ * - a language code specifying one of the languages normally written in a
+ * right-to-left script, e.g. "fa" (Farsi), except ones explicitly specifying
+ * Latin or Cyrillic script (which are the usual LTR alternatives).<p>
+ * The list of right-to-left scripts appears in the 100-199 range in
+ * http://www.unicode.org/iso15924/iso15924-num.html, of which Arabic and
+ * Hebrew are by far the most widely used. We also recognize Thaana, N'Ko, and
+ * Tifinagh, which also have significant modern usage. The rest (Syriac,
+ * Samaritan, Mandaic, etc.) seem to have extremely limited or no modern usage
+ * and are not recognized to save on code size.
+ * The languages usually written in a right-to-left script are taken as those
+ * with Suppress-Script: Hebr|Arab|Thaa|Nkoo|Tfng in
+ * http://www.iana.org/assignments/language-subtag-registry,
+ * as well as Central (or Sorani) Kurdish (ckb), Sindhi (sd) and Uyghur (ug).
+ * Other subtags of the language code, e.g. regions like EG (Egypt), are
+ * ignored.
+ * @param {string} lang BCP 47 (a.k.a III) language code.
+ * @return {boolean} Whether the language code is an RTL language.
+ */
+goog.i18n.bidi.isRtlLanguage = function(lang) {
+ return goog.i18n.bidi.rtlLocalesRe_.test(lang);
+};
+
+
+/**
+ * Regular expression for bracket guard replacement in text.
+ * @type {RegExp}
+ * @private
+ */
+goog.i18n.bidi.bracketGuardTextRe_ =
+ /(\(.*?\)+)|(\[.*?\]+)|(\{.*?\}+)|(<.*?>+)/g;
+
+
+/**
+ * Apply bracket guard using LRM and RLM. This is to address the problem of
+ * messy bracket display frequently happens in RTL layout.
+ * This function works for plain text, not for HTML. In HTML, the opening
+ * bracket might be in a different context than the closing bracket (such as
+ * an attribute value).
+ * @param {string} s The string that need to be processed.
+ * @param {boolean=} opt_isRtlContext specifies default direction (usually
+ * direction of the UI).
+ * @return {string} The processed string, with all bracket guarded.
+ */
+goog.i18n.bidi.guardBracketInText = function(s, opt_isRtlContext) {
+ var useRtl = opt_isRtlContext === undefined ? goog.i18n.bidi.hasAnyRtl(s) :
+ opt_isRtlContext;
+ var mark = useRtl ? goog.i18n.bidi.Format.RLM : goog.i18n.bidi.Format.LRM;
+ return s.replace(goog.i18n.bidi.bracketGuardTextRe_, mark + '$&' + mark);
+};
+
+
+/**
+ * Enforce the html snippet in RTL directionality regardless overall context.
+ * If the html piece was enclosed by tag, dir will be applied to existing
+ * tag, otherwise a span tag will be added as wrapper. For this reason, if
+ * html snippet start with with tag, this tag must enclose the whole piece. If
+ * the tag already has a dir specified, this new one will override existing
+ * one in behavior (tested on FF and IE).
+ * @param {string} html The string that need to be processed.
+ * @return {string} The processed string, with directionality enforced to RTL.
+ */
+goog.i18n.bidi.enforceRtlInHtml = function(html) {
+ if (html.charAt(0) == '<') {
+ return html.replace(/<\w+/, '$& dir=rtl');
+ }
+ // '\n' is important for FF so that it won't incorrectly merge span groups
+ return '\n<span dir=rtl>' + html + '</span>';
+};
+
+
+/**
+ * Enforce RTL on both end of the given text piece using unicode BiDi formatting
+ * characters RLE and PDF.
+ * @param {string} text The piece of text that need to be wrapped.
+ * @return {string} The wrapped string after process.
+ */
+goog.i18n.bidi.enforceRtlInText = function(text) {
+ return goog.i18n.bidi.Format.RLE + text + goog.i18n.bidi.Format.PDF;
+};
+
+
+/**
+ * Enforce the html snippet in RTL directionality regardless overall context.
+ * If the html piece was enclosed by tag, dir will be applied to existing
+ * tag, otherwise a span tag will be added as wrapper. For this reason, if
+ * html snippet start with with tag, this tag must enclose the whole piece. If
+ * the tag already has a dir specified, this new one will override existing
+ * one in behavior (tested on FF and IE).
+ * @param {string} html The string that need to be processed.
+ * @return {string} The processed string, with directionality enforced to RTL.
+ */
+goog.i18n.bidi.enforceLtrInHtml = function(html) {
+ if (html.charAt(0) == '<') {
+ return html.replace(/<\w+/, '$& dir=ltr');
+ }
+ // '\n' is important for FF so that it won't incorrectly merge span groups
+ return '\n<span dir=ltr>' + html + '</span>';
+};
+
+
+/**
+ * Enforce LTR on both end of the given text piece using unicode BiDi formatting
+ * characters LRE and PDF.
+ * @param {string} text The piece of text that need to be wrapped.
+ * @return {string} The wrapped string after process.
+ */
+goog.i18n.bidi.enforceLtrInText = function(text) {
+ return goog.i18n.bidi.Format.LRE + text + goog.i18n.bidi.Format.PDF;
+};
+
+
+/**
+ * Regular expression to find dimensions such as "padding: .3 0.4ex 5px 6;"
+ * @type {RegExp}
+ * @private
+ */
+goog.i18n.bidi.dimensionsRe_ =
+ /:\s*([.\d][.\w]*)\s+([.\d][.\w]*)\s+([.\d][.\w]*)\s+([.\d][.\w]*)/g;
+
+
+/**
+ * Regular expression for left.
+ * @type {RegExp}
+ * @private
+ */
+goog.i18n.bidi.leftRe_ = /left/gi;
+
+
+/**
+ * Regular expression for right.
+ * @type {RegExp}
+ * @private
+ */
+goog.i18n.bidi.rightRe_ = /right/gi;
+
+
+/**
+ * Placeholder regular expression for swapping.
+ * @type {RegExp}
+ * @private
+ */
+goog.i18n.bidi.tempRe_ = /%%%%/g;
+
+
+/**
+ * Swap location parameters and 'left'/'right' in CSS specification. The
+ * processed string will be suited for RTL layout. Though this function can
+ * cover most cases, there are always exceptions. It is suggested to put
+ * those exceptions in separate group of CSS string.
+ * @param {string} cssStr CSS spefication string.
+ * @return {string} Processed CSS specification string.
+ */
+goog.i18n.bidi.mirrorCSS = function(cssStr) {
+ return cssStr
+ .
+ // reverse dimensions
+ replace(goog.i18n.bidi.dimensionsRe_, ':$1 $4 $3 $2')
+ .replace(goog.i18n.bidi.leftRe_, '%%%%')
+ . // swap left and right
+ replace(goog.i18n.bidi.rightRe_, goog.i18n.bidi.LEFT)
+ .replace(goog.i18n.bidi.tempRe_, goog.i18n.bidi.RIGHT);
+};
+
+
+/**
+ * Regular expression for hebrew double quote substitution, finding quote
+ * directly after hebrew characters.
+ * @type {RegExp}
+ * @private
+ */
+goog.i18n.bidi.doubleQuoteSubstituteRe_ = /([\u0591-\u05f2])"/g;
+
+
+/**
+ * Regular expression for hebrew single quote substitution, finding quote
+ * directly after hebrew characters.
+ * @type {RegExp}
+ * @private
+ */
+goog.i18n.bidi.singleQuoteSubstituteRe_ = /([\u0591-\u05f2])'/g;
+
+
+/**
+ * Replace the double and single quote directly after a Hebrew character with
+ * GERESH and GERSHAYIM. In such case, most likely that's user intention.
+ * @param {string} str String that need to be processed.
+ * @return {string} Processed string with double/single quote replaced.
+ */
+goog.i18n.bidi.normalizeHebrewQuote = function(str) {
+ return str.replace(goog.i18n.bidi.doubleQuoteSubstituteRe_, '$1\u05f4')
+ .replace(goog.i18n.bidi.singleQuoteSubstituteRe_, '$1\u05f3');
+};
+
+
+/**
+ * Regular expression to split a string into "words" for directionality
+ * estimation based on relative word counts.
+ * @type {RegExp}
+ * @private
+ */
+goog.i18n.bidi.wordSeparatorRe_ = /\s+/;
+
+
+/**
+ * Regular expression to check if a string contains any numerals. Used to
+ * differentiate between completely neutral strings and those containing
+ * numbers, which are weakly LTR.
+ *
+ * Native Arabic digits (\u0660 - \u0669) are not included because although they
+ * do flow left-to-right inside a number, this is the case even if the overall
+ * directionality is RTL, and a mathematical expression using these digits is
+ * supposed to flow right-to-left overall, including unary plus and minus
+ * appearing to the right of a number, and this does depend on the overall
+ * directionality being RTL. The digits used in Farsi (\u06F0 - \u06F9), on the
+ * other hand, are included, since Farsi math (including unary plus and minus)
+ * does flow left-to-right.
+ *
+ * @type {RegExp}
+ * @private
+ */
+goog.i18n.bidi.hasNumeralsRe_ = /[\d\u06f0-\u06f9]/;
+
+
+/**
+ * This constant controls threshold of RTL directionality.
+ * @type {number}
+ * @private
+ */
+goog.i18n.bidi.rtlDetectionThreshold_ = 0.40;
+
+
+/**
+ * Estimates the directionality of a string based on relative word counts.
+ * If the number of RTL words is above a certain percentage of the total number
+ * of strongly directional words, returns RTL.
+ * Otherwise, if any words are strongly or weakly LTR, returns LTR.
+ * Otherwise, returns UNKNOWN, which is used to mean "neutral".
+ * Numbers are counted as weakly LTR.
+ * @param {string} str The string to be checked.
+ * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
+ * Default: false.
+ * @return {goog.i18n.bidi.Dir} Estimated overall directionality of {@code str}.
+ */
+goog.i18n.bidi.estimateDirection = function(str, opt_isHtml) {
+ var rtlCount = 0;
+ var totalCount = 0;
+ var hasWeaklyLtr = false;
+ var tokens = goog.i18n.bidi.stripHtmlIfNeeded_(str, opt_isHtml)
+ .split(goog.i18n.bidi.wordSeparatorRe_);
+ for (var i = 0; i < tokens.length; i++) {
+ var token = tokens[i];
+ if (goog.i18n.bidi.startsWithRtl(token)) {
+ rtlCount++;
+ totalCount++;
+ } else if (goog.i18n.bidi.isRequiredLtrRe_.test(token)) {
+ hasWeaklyLtr = true;
+ } else if (goog.i18n.bidi.hasAnyLtr(token)) {
+ totalCount++;
+ } else if (goog.i18n.bidi.hasNumeralsRe_.test(token)) {
+ hasWeaklyLtr = true;
+ }
+ }
+
+ return totalCount == 0 ?
+ (hasWeaklyLtr ? goog.i18n.bidi.Dir.LTR : goog.i18n.bidi.Dir.NEUTRAL) :
+ (rtlCount / totalCount > goog.i18n.bidi.rtlDetectionThreshold_ ?
+ goog.i18n.bidi.Dir.RTL :
+ goog.i18n.bidi.Dir.LTR);
+};
+
+
+/**
+ * Check the directionality of a piece of text, return true if the piece of
+ * text should be laid out in RTL direction.
+ * @param {string} str The piece of text that need to be detected.
+ * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
+ * Default: false.
+ * @return {boolean} Whether this piece of text should be laid out in RTL.
+ */
+goog.i18n.bidi.detectRtlDirectionality = function(str, opt_isHtml) {
+ return goog.i18n.bidi.estimateDirection(str, opt_isHtml) ==
+ goog.i18n.bidi.Dir.RTL;
+};
+
+
+/**
+ * Sets text input element's directionality and text alignment based on a
+ * given directionality. Does nothing if the given directionality is unknown or
+ * neutral.
+ * @param {Element} element Input field element to set directionality to.
+ * @param {goog.i18n.bidi.Dir|number|boolean|null} dir Desired directionality,
+ * given in one of the following formats:
+ * 1. A goog.i18n.bidi.Dir constant.
+ * 2. A number (positive = LRT, negative = RTL, 0 = neutral).
+ * 3. A boolean (true = RTL, false = LTR).
+ * 4. A null for unknown directionality.
+ */
+goog.i18n.bidi.setElementDirAndAlign = function(element, dir) {
+ if (element) {
+ dir = goog.i18n.bidi.toDir(dir);
+ if (dir) {
+ element.style.textAlign = dir == goog.i18n.bidi.Dir.RTL ?
+ goog.i18n.bidi.RIGHT :
+ goog.i18n.bidi.LEFT;
+ element.dir = dir == goog.i18n.bidi.Dir.RTL ? 'rtl' : 'ltr';
+ }
+ }
+};
+
+
+/**
+ * Sets element dir based on estimated directionality of the given text.
+ * @param {!Element} element
+ * @param {string} text
+ */
+goog.i18n.bidi.setElementDirByTextDirectionality = function(element, text) {
+ switch (goog.i18n.bidi.estimateDirection(text)) {
+ case (goog.i18n.bidi.Dir.LTR):
+ element.dir = 'ltr';
+ break;
+ case (goog.i18n.bidi.Dir.RTL):
+ element.dir = 'rtl';
+ break;
+ default:
+ // Default for no direction, inherit from document.
+ element.removeAttribute('dir');
+ }
+};
+
+
+
+/**
+ * Strings that have an (optional) known direction.
+ *
+ * Implementations of this interface are string-like objects that carry an
+ * attached direction, if known.
+ * @interface
+ */
+goog.i18n.bidi.DirectionalString = function() {};
+
+
+/**
+ * Interface marker of the DirectionalString interface.
+ *
+ * This property can be used to determine at runtime whether or not an object
+ * implements this interface. All implementations of this interface set this
+ * property to {@code true}.
+ * @type {boolean}
+ */
+goog.i18n.bidi.DirectionalString.prototype
+ .implementsGoogI18nBidiDirectionalString;
+
+
+/**
+ * Retrieves this object's known direction (if any).
+ * @return {?goog.i18n.bidi.Dir} The known direction. Null if unknown.
+ */
+goog.i18n.bidi.DirectionalString.prototype.getDirection;
diff --git a/chromium/third_party/ink/closure/i18n/bidiformatter.js b/chromium/third_party/ink/closure/i18n/bidiformatter.js
new file mode 100644
index 00000000000..19bb2dde756
--- /dev/null
+++ b/chromium/third_party/ink/closure/i18n/bidiformatter.js
@@ -0,0 +1,556 @@
+// Copyright 2009 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Utility for formatting text for display in a potentially
+ * opposite-directionality context without garbling.
+ * Mostly a port of http://go/formatter.cc.
+ * @author tomerigo@google.com (Tomer Greenberg)
+ */
+
+
+goog.provide('goog.i18n.BidiFormatter');
+
+goog.require('goog.html.SafeHtml');
+goog.require('goog.i18n.bidi');
+goog.require('goog.i18n.bidi.Dir');
+goog.require('goog.i18n.bidi.Format');
+
+
+
+/**
+ * Utility class for formatting text for display in a potentially
+ * opposite-directionality context without garbling. Provides the following
+ * functionality:
+ *
+ * 1. BiDi Wrapping
+ * When text in one language is mixed into a document in another, opposite-
+ * directionality language, e.g. when an English business name is embedded in a
+ * Hebrew web page, both the inserted string and the text following it may be
+ * displayed incorrectly unless the inserted string is explicitly separated
+ * from the surrounding text in a "wrapper" that declares its directionality at
+ * the start and then resets it back at the end. This wrapping can be done in
+ * HTML mark-up (e.g. a 'span dir="rtl"' tag) or - only in contexts where
+ * mark-up can not be used - in Unicode BiDi formatting codes (LRE|RLE and PDF).
+ * Providing such wrapping services is the basic purpose of the BiDi formatter.
+ *
+ * 2. Directionality estimation
+ * How does one know whether a string about to be inserted into surrounding
+ * text has the same directionality? Well, in many cases, one knows that this
+ * must be the case when writing the code doing the insertion, e.g. when a
+ * localized message is inserted into a localized page. In such cases there is
+ * no need to involve the BiDi formatter at all. In the remaining cases, e.g.
+ * when the string is user-entered or comes from a database, the language of
+ * the string (and thus its directionality) is not known a priori, and must be
+ * estimated at run-time. The BiDi formatter does this automatically.
+ *
+ * 3. Escaping
+ * When wrapping plain text - i.e. text that is not already HTML or HTML-
+ * escaped - in HTML mark-up, the text must first be HTML-escaped to prevent XSS
+ * attacks and other nasty business. This of course is always true, but the
+ * escaping can not be done after the string has already been wrapped in
+ * mark-up, so the BiDi formatter also serves as a last chance and includes
+ * escaping services.
+ *
+ * Thus, in a single call, the formatter will escape the input string as
+ * specified, determine its directionality, and wrap it as necessary. It is
+ * then up to the caller to insert the return value in the output.
+ *
+ * See http://wiki/Main/TemplatesAndBiDi for more information.
+ *
+ * @param {goog.i18n.bidi.Dir|number|boolean|null} contextDir The context
+ * directionality, in one of the following formats:
+ * 1. A goog.i18n.bidi.Dir constant. NEUTRAL is treated the same as null,
+ * i.e. unknown, for backward compatibility with legacy calls.
+ * 2. A number (positive = LTR, negative = RTL, 0 = unknown).
+ * 3. A boolean (true = RTL, false = LTR).
+ * 4. A null for unknown directionality.
+ * @param {boolean=} opt_alwaysSpan Whether {@link #spanWrap} should always
+ * use a 'span' tag, even when the input directionality is neutral or
+ * matches the context, so that the DOM structure of the output does not
+ * depend on the combination of directionalities. Default: false.
+ * @constructor
+ * @final
+ */
+goog.i18n.BidiFormatter = function(contextDir, opt_alwaysSpan) {
+ /**
+ * The overall directionality of the context in which the formatter is being
+ * used.
+ * @type {?goog.i18n.bidi.Dir}
+ * @private
+ */
+ this.contextDir_ = goog.i18n.bidi.toDir(contextDir, true /* opt_noNeutral */);
+
+ /**
+ * Whether {@link #spanWrap} and similar methods should always use the same
+ * span structure, regardless of the combination of directionalities, for a
+ * stable DOM structure.
+ * @type {boolean}
+ * @private
+ */
+ this.alwaysSpan_ = !!opt_alwaysSpan;
+};
+
+
+/**
+ * @return {?goog.i18n.bidi.Dir} The context directionality.
+ */
+goog.i18n.BidiFormatter.prototype.getContextDir = function() {
+ return this.contextDir_;
+};
+
+
+/**
+ * @return {boolean} Whether alwaysSpan is set.
+ */
+goog.i18n.BidiFormatter.prototype.getAlwaysSpan = function() {
+ return this.alwaysSpan_;
+};
+
+
+/**
+ * @param {goog.i18n.bidi.Dir|number|boolean|null} contextDir The context
+ * directionality, in one of the following formats:
+ * 1. A goog.i18n.bidi.Dir constant. NEUTRAL is treated the same as null,
+ * i.e. unknown.
+ * 2. A number (positive = LTR, negative = RTL, 0 = unknown).
+ * 3. A boolean (true = RTL, false = LTR).
+ * 4. A null for unknown directionality.
+ */
+goog.i18n.BidiFormatter.prototype.setContextDir = function(contextDir) {
+ this.contextDir_ = goog.i18n.bidi.toDir(contextDir, true /* opt_noNeutral */);
+};
+
+
+/**
+ * @param {boolean} alwaysSpan Whether {@link #spanWrap} should always use a
+ * 'span' tag, even when the input directionality is neutral or matches the
+ * context, so that the DOM structure of the output does not depend on the
+ * combination of directionalities.
+ */
+goog.i18n.BidiFormatter.prototype.setAlwaysSpan = function(alwaysSpan) {
+ this.alwaysSpan_ = alwaysSpan;
+};
+
+
+/**
+ * Returns the directionality of input argument {@code str}.
+ * Identical to {@link goog.i18n.bidi.estimateDirection}.
+ *
+ * @param {string} str The input text.
+ * @param {boolean=} opt_isHtml Whether {@code str} is HTML / HTML-escaped.
+ * Default: false.
+ * @return {goog.i18n.bidi.Dir} Estimated overall directionality of {@code str}.
+ */
+goog.i18n.BidiFormatter.prototype.estimateDirection =
+ goog.i18n.bidi.estimateDirection;
+
+
+/**
+ * Returns true if two given directionalities are opposite.
+ * Note: the implementation is based on the numeric values of the Dir enum.
+ *
+ * @param {?goog.i18n.bidi.Dir} dir1 1st directionality.
+ * @param {?goog.i18n.bidi.Dir} dir2 2nd directionality.
+ * @return {boolean} Whether the directionalities are opposite.
+ * @private
+ */
+goog.i18n.BidiFormatter.prototype.areDirectionalitiesOpposite_ = function(
+ dir1, dir2) {
+ return Number(dir1) * Number(dir2) < 0;
+};
+
+
+/**
+ * Returns a unicode BiDi mark matching the context directionality (LRM or
+ * RLM) if {@code opt_dirReset}, and if either the directionality or the exit
+ * directionality of {@code str} is opposite to the context directionality.
+ * Otherwise returns the empty string.
+ *
+ * @param {string} str The input text.
+ * @param {goog.i18n.bidi.Dir} dir {@code str}'s overall directionality.
+ * @param {boolean=} opt_isHtml Whether {@code str} is HTML / HTML-escaped.
+ * Default: false.
+ * @param {boolean=} opt_dirReset Whether to perform the reset. Default: false.
+ * @return {string} A unicode BiDi mark or the empty string.
+ * @private
+ */
+goog.i18n.BidiFormatter.prototype.dirResetIfNeeded_ = function(
+ str, dir, opt_isHtml, opt_dirReset) {
+ // endsWithRtl and endsWithLtr are called only if needed (short-circuit).
+ if (opt_dirReset &&
+ (this.areDirectionalitiesOpposite_(dir, this.contextDir_) ||
+ (this.contextDir_ == goog.i18n.bidi.Dir.LTR &&
+ goog.i18n.bidi.endsWithRtl(str, opt_isHtml)) ||
+ (this.contextDir_ == goog.i18n.bidi.Dir.RTL &&
+ goog.i18n.bidi.endsWithLtr(str, opt_isHtml)))) {
+ return this.contextDir_ == goog.i18n.bidi.Dir.LTR ?
+ goog.i18n.bidi.Format.LRM :
+ goog.i18n.bidi.Format.RLM;
+ } else {
+ return '';
+ }
+};
+
+
+/**
+ * Returns "rtl" if {@code str}'s estimated directionality is RTL, and "ltr" if
+ * it is LTR. In case it's NEUTRAL, returns "rtl" if the context directionality
+ * is RTL, and "ltr" otherwise.
+ * Needed for GXP, which can't handle dirAttr.
+ * Example use case:
+ * &lt;td expr:dir='bidiFormatter.dirAttrValue(foo)'&gt;
+ * &lt;gxp:eval expr='foo'&gt;
+ * &lt;/td&gt;
+ *
+ * @param {string} str Text whose directionality is to be estimated.
+ * @param {boolean=} opt_isHtml Whether {@code str} is HTML / HTML-escaped.
+ * Default: false.
+ * @return {string} "rtl" or "ltr", according to the logic described above.
+ */
+goog.i18n.BidiFormatter.prototype.dirAttrValue = function(str, opt_isHtml) {
+ return this.knownDirAttrValue(this.estimateDirection(str, opt_isHtml));
+};
+
+
+/**
+ * Returns "rtl" if the given directionality is RTL, and "ltr" if it is LTR. In
+ * case it's NEUTRAL, returns "rtl" if the context directionality is RTL, and
+ * "ltr" otherwise.
+ *
+ * @param {goog.i18n.bidi.Dir} dir A directionality.
+ * @return {string} "rtl" or "ltr", according to the logic described above.
+ */
+goog.i18n.BidiFormatter.prototype.knownDirAttrValue = function(dir) {
+ var resolvedDir = dir == goog.i18n.bidi.Dir.NEUTRAL ? this.contextDir_ : dir;
+ return resolvedDir == goog.i18n.bidi.Dir.RTL ? 'rtl' : 'ltr';
+};
+
+
+/**
+ * Returns 'dir="ltr"' or 'dir="rtl"', depending on {@code str}'s estimated
+ * directionality, if it is not the same as the context directionality.
+ * Otherwise, returns the empty string.
+ *
+ * @param {string} str Text whose directionality is to be estimated.
+ * @param {boolean=} opt_isHtml Whether {@code str} is HTML / HTML-escaped.
+ * Default: false.
+ * @return {string} 'dir="rtl"' for RTL text in non-RTL context; 'dir="ltr"' for
+ * LTR text in non-LTR context; else, the empty string.
+ */
+goog.i18n.BidiFormatter.prototype.dirAttr = function(str, opt_isHtml) {
+ return this.knownDirAttr(this.estimateDirection(str, opt_isHtml));
+};
+
+
+/**
+ * Returns 'dir="ltr"' or 'dir="rtl"', depending on the given directionality, if
+ * it is not the same as the context directionality. Otherwise, returns the
+ * empty string.
+ *
+ * @param {goog.i18n.bidi.Dir} dir A directionality.
+ * @return {string} 'dir="rtl"' for RTL text in non-RTL context; 'dir="ltr"' for
+ * LTR text in non-LTR context; else, the empty string.
+ */
+goog.i18n.BidiFormatter.prototype.knownDirAttr = function(dir) {
+ if (dir != this.contextDir_) {
+ return dir == goog.i18n.bidi.Dir.RTL ?
+ 'dir="rtl"' :
+ dir == goog.i18n.bidi.Dir.LTR ? 'dir="ltr"' : '';
+ }
+ return '';
+};
+
+
+/**
+ * Formats a string of unknown directionality for use in HTML output of the
+ * context directionality, so an opposite-directionality string is neither
+ * garbled nor garbles what follows it.
+ * The algorithm: estimates the directionality of input argument {@code html}.
+ * In case its directionality doesn't match the context directionality, wraps it
+ * with a 'span' tag and adds a "dir" attribute (either 'dir="rtl"' or
+ * 'dir="ltr"'). If setAlwaysSpan(true) was used, the input is always wrapped
+ * with 'span', skipping just the dir attribute when it's not needed.
+ *
+ * If {@code opt_dirReset}, and if the overall directionality or the exit
+ * directionality of {@code str} are opposite to the context directionality, a
+ * trailing unicode BiDi mark matching the context directionality is appened
+ * (LRM or RLM).
+ *
+ * @param {!goog.html.SafeHtml} html The input HTML.
+ * @param {boolean=} opt_dirReset Whether to append a trailing unicode bidi mark
+ * matching the context directionality, when needed, to prevent the possible
+ * garbling of whatever may follow {@code html}. Default: true.
+ * @return {!goog.html.SafeHtml} Input text after applying the processing.
+ */
+goog.i18n.BidiFormatter.prototype.spanWrapSafeHtml = function(
+ html, opt_dirReset) {
+ return this.spanWrapSafeHtmlWithKnownDir(null, html, opt_dirReset);
+};
+
+
+/**
+ * Formats a string of given directionality for use in HTML output of the
+ * context directionality, so an opposite-directionality string is neither
+ * garbled nor garbles what follows it.
+ * The algorithm: If {@code dir} doesn't match the context directionality, wraps
+ * {@code html} with a 'span' tag and adds a "dir" attribute (either 'dir="rtl"'
+ * or 'dir="ltr"'). If setAlwaysSpan(true) was used, the input is always wrapped
+ * with 'span', skipping just the dir attribute when it's not needed.
+ *
+ * If {@code opt_dirReset}, and if {@code dir} or the exit directionality of
+ * {@code html} are opposite to the context directionality, a trailing unicode
+ * BiDi mark matching the context directionality is appened (LRM or RLM).
+ *
+ * @param {?goog.i18n.bidi.Dir} dir {@code html}'s overall directionality, or
+ * null if unknown and needs to be estimated.
+ * @param {!goog.html.SafeHtml} html The input HTML.
+ * @param {boolean=} opt_dirReset Whether to append a trailing unicode bidi mark
+ * matching the context directionality, when needed, to prevent the possible
+ * garbling of whatever may follow {@code html}. Default: true.
+ * @return {!goog.html.SafeHtml} Input text after applying the processing.
+ */
+goog.i18n.BidiFormatter.prototype.spanWrapSafeHtmlWithKnownDir = function(
+ dir, html, opt_dirReset) {
+ if (dir == null) {
+ dir = this.estimateDirection(goog.html.SafeHtml.unwrap(html), true);
+ }
+ return this.spanWrapWithKnownDir_(dir, html, opt_dirReset);
+};
+
+
+/**
+ * The internal implementation of spanWrapSafeHtmlWithKnownDir for non-null dir,
+ * to help the compiler optimize.
+ *
+ * @param {goog.i18n.bidi.Dir} dir {@code str}'s overall directionality.
+ * @param {!goog.html.SafeHtml} html The input HTML.
+ * @param {boolean=} opt_dirReset Whether to append a trailing unicode bidi mark
+ * matching the context directionality, when needed, to prevent the possible
+ * garbling of whatever may follow {@code str}. Default: true.
+ * @return {!goog.html.SafeHtml} Input text after applying the above processing.
+ * @private
+ */
+goog.i18n.BidiFormatter.prototype.spanWrapWithKnownDir_ = function(
+ dir, html, opt_dirReset) {
+ opt_dirReset = opt_dirReset || (opt_dirReset == undefined);
+
+ var result;
+ // Whether to add the "dir" attribute.
+ var dirCondition =
+ dir != goog.i18n.bidi.Dir.NEUTRAL && dir != this.contextDir_;
+ if (this.alwaysSpan_ || dirCondition) { // Wrap is needed
+ var dirAttribute;
+ if (dirCondition) {
+ dirAttribute = dir == goog.i18n.bidi.Dir.RTL ? 'rtl' : 'ltr';
+ }
+ result = goog.html.SafeHtml.create('span', {'dir': dirAttribute}, html);
+ } else {
+ result = html;
+ }
+ var str = goog.html.SafeHtml.unwrap(html);
+ result = goog.html.SafeHtml.concatWithDir(
+ goog.i18n.bidi.Dir.NEUTRAL, result,
+ this.dirResetIfNeeded_(str, dir, true, opt_dirReset));
+ return result;
+};
+
+
+/**
+ * Formats a string of unknown directionality for use in plain-text output of
+ * the context directionality, so an opposite-directionality string is neither
+ * garbled nor garbles what follows it.
+ * As opposed to {@link #spanWrap}, this makes use of unicode BiDi formatting
+ * characters. In HTML, its *only* valid use is inside of elements that do not
+ * allow mark-up, e.g. an 'option' tag.
+ * The algorithm: estimates the directionality of input argument {@code str}.
+ * In case it doesn't match the context directionality, wraps it with Unicode
+ * BiDi formatting characters: RLE{@code str}PDF for RTL text, and
+ * LRE{@code str}PDF for LTR text.
+ *
+ * If {@code opt_dirReset}, and if the overall directionality or the exit
+ * directionality of {@code str} are opposite to the context directionality, a
+ * trailing unicode BiDi mark matching the context directionality is appended
+ * (LRM or RLM).
+ *
+ * Does *not* do HTML-escaping regardless of the value of {@code opt_isHtml}.
+ * The return value can be HTML-escaped as necessary.
+ *
+ * @param {string} str The input text.
+ * @param {boolean=} opt_isHtml Whether {@code str} is HTML / HTML-escaped.
+ * Default: false.
+ * @param {boolean=} opt_dirReset Whether to append a trailing unicode bidi mark
+ * matching the context directionality, when needed, to prevent the possible
+ * garbling of whatever may follow {@code str}. Default: true.
+ * @return {string} Input text after applying the above processing.
+ */
+goog.i18n.BidiFormatter.prototype.unicodeWrap = function(
+ str, opt_isHtml, opt_dirReset) {
+ return this.unicodeWrapWithKnownDir(null, str, opt_isHtml, opt_dirReset);
+};
+
+
+/**
+ * Formats a string of given directionality for use in plain-text output of the
+ * context directionality, so an opposite-directionality string is neither
+ * garbled nor garbles what follows it.
+ * As opposed to {@link #spanWrapWithKnownDir}, makes use of unicode BiDi
+ * formatting characters. In HTML, its *only* valid use is inside of elements
+ * that do not allow mark-up, e.g. an 'option' tag.
+ * The algorithm: If {@code dir} doesn't match the context directionality, wraps
+ * {@code str} with Unicode BiDi formatting characters: RLE{@code str}PDF for
+ * RTL text, and LRE{@code str}PDF for LTR text.
+ *
+ * If {@code opt_dirReset}, and if the overall directionality or the exit
+ * directionality of {@code str} are opposite to the context directionality, a
+ * trailing unicode BiDi mark matching the context directionality is appended
+ * (LRM or RLM).
+ *
+ * Does *not* do HTML-escaping regardless of the value of {@code opt_isHtml}.
+ * The return value can be HTML-escaped as necessary.
+ *
+ * @param {?goog.i18n.bidi.Dir} dir {@code str}'s overall directionality, or
+ * null if unknown and needs to be estimated.
+ * @param {string} str The input text.
+ * @param {boolean=} opt_isHtml Whether {@code str} is HTML / HTML-escaped.
+ * Default: false.
+ * @param {boolean=} opt_dirReset Whether to append a trailing unicode bidi mark
+ * matching the context directionality, when needed, to prevent the possible
+ * garbling of whatever may follow {@code str}. Default: true.
+ * @return {string} Input text after applying the above processing.
+ */
+goog.i18n.BidiFormatter.prototype.unicodeWrapWithKnownDir = function(
+ dir, str, opt_isHtml, opt_dirReset) {
+ if (dir == null) {
+ dir = this.estimateDirection(str, opt_isHtml);
+ }
+ return this.unicodeWrapWithKnownDir_(dir, str, opt_isHtml, opt_dirReset);
+};
+
+
+/**
+ * The internal implementation of unicodeWrapWithKnownDir for non-null dir, to
+ * help the compiler optimize.
+ *
+ * @param {goog.i18n.bidi.Dir} dir {@code str}'s overall directionality.
+ * @param {string} str The input text.
+ * @param {boolean=} opt_isHtml Whether {@code str} is HTML / HTML-escaped.
+ * Default: false.
+ * @param {boolean=} opt_dirReset Whether to append a trailing unicode bidi mark
+ * matching the context directionality, when needed, to prevent the possible
+ * garbling of whatever may follow {@code str}. Default: true.
+ * @return {string} Input text after applying the above processing.
+ * @private
+ */
+goog.i18n.BidiFormatter.prototype.unicodeWrapWithKnownDir_ = function(
+ dir, str, opt_isHtml, opt_dirReset) {
+ opt_dirReset = opt_dirReset || (opt_dirReset == undefined);
+ var result = [];
+ if (dir != goog.i18n.bidi.Dir.NEUTRAL && dir != this.contextDir_) {
+ result.push(
+ dir == goog.i18n.bidi.Dir.RTL ? goog.i18n.bidi.Format.RLE :
+ goog.i18n.bidi.Format.LRE);
+ result.push(str);
+ result.push(goog.i18n.bidi.Format.PDF);
+ } else {
+ result.push(str);
+ }
+
+ result.push(this.dirResetIfNeeded_(str, dir, opt_isHtml, opt_dirReset));
+ return result.join('');
+};
+
+
+/**
+ * Returns a Unicode BiDi mark matching the context directionality (LRM or RLM)
+ * if the directionality or the exit directionality of {@code str} are opposite
+ * to the context directionality. Otherwise returns the empty string.
+ *
+ * @param {string} str The input text.
+ * @param {boolean=} opt_isHtml Whether {@code str} is HTML / HTML-escaped.
+ * Default: false.
+ * @return {string} A Unicode bidi mark matching the global directionality or
+ * the empty string.
+ */
+goog.i18n.BidiFormatter.prototype.markAfter = function(str, opt_isHtml) {
+ return this.markAfterKnownDir(null, str, opt_isHtml);
+};
+
+
+/**
+ * Returns a Unicode BiDi mark matching the context directionality (LRM or RLM)
+ * if the given directionality or the exit directionality of {@code str} are
+ * opposite to the context directionality. Otherwise returns the empty string.
+ *
+ * @param {?goog.i18n.bidi.Dir} dir {@code str}'s overall directionality, or
+ * null if unknown and needs to be estimated.
+ * @param {string} str The input text.
+ * @param {boolean=} opt_isHtml Whether {@code str} is HTML / HTML-escaped.
+ * Default: false.
+ * @return {string} A Unicode bidi mark matching the global directionality or
+ * the empty string.
+ */
+goog.i18n.BidiFormatter.prototype.markAfterKnownDir = function(
+ dir, str, opt_isHtml) {
+ if (dir == null) {
+ dir = this.estimateDirection(str, opt_isHtml);
+ }
+ return this.dirResetIfNeeded_(str, dir, opt_isHtml, true);
+};
+
+
+/**
+ * Returns the Unicode BiDi mark matching the context directionality (LRM for
+ * LTR context directionality, RLM for RTL context directionality), or the
+ * empty string for neutral / unknown context directionality.
+ *
+ * @return {string} LRM for LTR context directionality and RLM for RTL context
+ * directionality.
+ */
+goog.i18n.BidiFormatter.prototype.mark = function() {
+ switch (this.contextDir_) {
+ case (goog.i18n.bidi.Dir.LTR):
+ return goog.i18n.bidi.Format.LRM;
+ case (goog.i18n.bidi.Dir.RTL):
+ return goog.i18n.bidi.Format.RLM;
+ default:
+ return '';
+ }
+};
+
+
+/**
+ * Returns 'right' for RTL context directionality. Otherwise (LTR or neutral /
+ * unknown context directionality) returns 'left'.
+ *
+ * @return {string} 'right' for RTL context directionality and 'left' for other
+ * context directionality.
+ */
+goog.i18n.BidiFormatter.prototype.startEdge = function() {
+ return this.contextDir_ == goog.i18n.bidi.Dir.RTL ? goog.i18n.bidi.RIGHT :
+ goog.i18n.bidi.LEFT;
+};
+
+
+/**
+ * Returns 'left' for RTL context directionality. Otherwise (LTR or neutral /
+ * unknown context directionality) returns 'right'.
+ *
+ * @return {string} 'left' for RTL context directionality and 'right' for other
+ * context directionality.
+ */
+goog.i18n.BidiFormatter.prototype.endEdge = function() {
+ return this.contextDir_ == goog.i18n.bidi.Dir.RTL ? goog.i18n.bidi.LEFT :
+ goog.i18n.bidi.RIGHT;
+};
diff --git a/chromium/third_party/ink/closure/i18n/graphemebreak.js b/chromium/third_party/ink/closure/i18n/graphemebreak.js
new file mode 100644
index 00000000000..ba85f94c6d6
--- /dev/null
+++ b/chromium/third_party/ink/closure/i18n/graphemebreak.js
@@ -0,0 +1,451 @@
+// Copyright 2006 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Detect Grapheme Cluster Break in a pair of codepoints. Follows
+ * Unicode 10 UAX#29. Tailoring for Virama × Indic Letters is used.
+ *
+ * Reference: http://unicode.org/reports/tr29
+ *
+ * @author cibu@google.com (Cibu Johny)
+ * @author fabalbon@google.com (Felipe Balbontin)
+ */
+
+goog.provide('goog.i18n.GraphemeBreak');
+
+goog.require('goog.asserts');
+goog.require('goog.i18n.uChar');
+goog.require('goog.structs.InversionMap');
+
+/**
+ * Enum for all Grapheme Cluster Break properties.
+ * These enums directly corresponds to Grapheme_Cluster_Break property values
+ * mentioned in http://unicode.org/reports/tr29 table 2. VIRAMA and
+ * INDIC_LETTER are for the Virama × Base tailoring mentioned in the notes.
+ *
+ * @protected @enum {number}
+ */
+goog.i18n.GraphemeBreak.property = {
+ OTHER: 0,
+ CONTROL: 1,
+ EXTEND: 2,
+ PREPEND: 3,
+ SPACING_MARK: 4,
+ INDIC_LETTER: 5,
+ VIRAMA: 6,
+ L: 7,
+ V: 8,
+ T: 9,
+ LV: 10,
+ LVT: 11,
+ CR: 12,
+ LF: 13,
+ REGIONAL_INDICATOR: 14,
+ ZWJ: 15,
+ E_BASE: 16,
+ GLUE_AFTER_ZWJ: 17,
+ E_MODIFIER: 18,
+ E_BASE_GAZ: 19
+};
+
+
+/**
+ * Grapheme Cluster Break property values for all codepoints as inversion map.
+ * Constructed lazily.
+ *
+ * @private {?goog.structs.InversionMap}
+ */
+goog.i18n.GraphemeBreak.inversions_ = null;
+
+
+/**
+ * Indicates if a and b form a grapheme cluster.
+ *
+ * This implements the rules in:
+ * http://www.unicode.org/reports/tr29/#Grapheme_Cluster_Boundary_Rules
+ *
+ * @param {number|string} a Code point or string with the first side of
+ * grapheme cluster.
+ * @param {number|string} b Code point or string with the second side of
+ * grapheme cluster.
+ * @param {boolean} extended If true, indicates extended grapheme cluster;
+ * If false, indicates legacy cluster.
+ * @return {boolean} True if a & b do not form a cluster; False otherwise.
+ * @private
+ */
+goog.i18n.GraphemeBreak.applyBreakRules_ = function(a, b, extended) {
+ var prop = goog.i18n.GraphemeBreak.property;
+
+ var aCode = goog.isString(a) ?
+ goog.i18n.GraphemeBreak.getCodePoint_(a, a.length - 1) :
+ a;
+ var bCode =
+ goog.isString(b) ? goog.i18n.GraphemeBreak.getCodePoint_(b, 0) : b;
+
+ var aProp = goog.i18n.GraphemeBreak.getBreakProp_(aCode);
+ var bProp = goog.i18n.GraphemeBreak.getBreakProp_(bCode);
+
+ var isString = goog.isString(a);
+
+ // GB3.
+ if (aProp === prop.CR && bProp === prop.LF) {
+ return false;
+ }
+
+ // GB4.
+ if (aProp === prop.CONTROL || aProp === prop.CR || aProp === prop.LF) {
+ return true;
+ }
+
+ // GB5.
+ if (bProp === prop.CONTROL || bProp === prop.CR || bProp === prop.LF) {
+ return true;
+ }
+
+ // GB6.
+ if (aProp === prop.L &&
+ (bProp === prop.L || bProp === prop.V || bProp === prop.LV ||
+ bProp === prop.LVT)) {
+ return false;
+ }
+
+ // GB7.
+ if ((aProp === prop.LV || aProp === prop.V) &&
+ (bProp === prop.V || bProp === prop.T)) {
+ return false;
+ }
+
+ // GB8.
+ if ((aProp === prop.LVT || aProp === prop.T) && bProp === prop.T) {
+ return false;
+ }
+
+ // GB9.
+ if (bProp === prop.EXTEND || bProp === prop.ZWJ || bProp === prop.VIRAMA) {
+ return false;
+ }
+
+ // GB9a, GB9b.
+ if (extended && (aProp === prop.PREPEND || bProp === prop.SPACING_MARK)) {
+ return false;
+ }
+
+ // Tailorings for basic aksara support.
+ if (extended && aProp === prop.VIRAMA && bProp === prop.INDIC_LETTER) {
+ return false;
+ }
+
+ var aStr, index, codePoint, codePointProp;
+
+ // GB10.
+ if (isString) {
+ if (bProp === prop.E_MODIFIER) {
+ // If using new API, consume the string's code points starting from the
+ // end and test the left side of: (E_Base | EBG) Extend* × E_Modifier.
+ aStr = /** @type {string} */ (a);
+ index = aStr.length - 1;
+ codePoint = aCode;
+ codePointProp = aProp;
+ while (index > 0 && codePointProp === prop.EXTEND) {
+ index -= goog.i18n.uChar.charCount(codePoint);
+ codePoint = goog.i18n.GraphemeBreak.getCodePoint_(aStr, index);
+ codePointProp = goog.i18n.GraphemeBreak.getBreakProp_(codePoint);
+ }
+ if (codePointProp === prop.E_BASE || codePointProp === prop.E_BASE_GAZ) {
+ return false;
+ }
+ }
+ } else {
+ // If using legacy API, return best effort by testing:
+ // (E_Base | EBG) × E_Modifier.
+ if ((aProp === prop.E_BASE || aProp === prop.E_BASE_GAZ) &&
+ bProp === prop.E_MODIFIER) {
+ return false;
+ }
+ }
+
+ // GB11.
+ if (aProp === prop.ZWJ &&
+ (bProp === prop.GLUE_AFTER_ZWJ || bProp === prop.E_BASE_GAZ)) {
+ return false;
+ }
+
+ // GB12, GB13.
+ if (isString) {
+ if (bProp === prop.REGIONAL_INDICATOR) {
+ // If using new API, consume the string's code points starting from the
+ // end and test the left side of these rules:
+ // - sot (RI RI)* RI × RI
+ // - [^RI] (RI RI)* RI × RI.
+ var numberOfRi = 0;
+ aStr = /** @type {string} */ (a);
+ index = aStr.length - 1;
+ codePoint = aCode;
+ codePointProp = aProp;
+ while (index > 0 && codePointProp === prop.REGIONAL_INDICATOR) {
+ numberOfRi++;
+ index -= goog.i18n.uChar.charCount(codePoint);
+ codePoint = goog.i18n.GraphemeBreak.getCodePoint_(aStr, index);
+ codePointProp = goog.i18n.GraphemeBreak.getBreakProp_(codePoint);
+ }
+ if (codePointProp === prop.REGIONAL_INDICATOR) {
+ numberOfRi++;
+ }
+ if (numberOfRi % 2 === 1) {
+ return false;
+ }
+ }
+ } else {
+ // If using legacy API, return best effort by testing: RI × RI.
+ if (aProp === prop.REGIONAL_INDICATOR &&
+ bProp === prop.REGIONAL_INDICATOR) {
+ return false;
+ }
+ }
+
+ // GB999.
+ return true;
+};
+
+
+/**
+ * Method to return property enum value of the code point. If it is Hangul LV or
+ * LVT, then it is computed; for the rest it is picked from the inversion map.
+ *
+ * @param {number} codePoint The code point value of the character.
+ * @return {number} Property enum value of code point.
+ * @private
+ */
+goog.i18n.GraphemeBreak.getBreakProp_ = function(codePoint) {
+ if (0xAC00 <= codePoint && codePoint <= 0xD7A3) {
+ var prop = goog.i18n.GraphemeBreak.property;
+ if (codePoint % 0x1C === 0x10) {
+ return prop.LV;
+ }
+ return prop.LVT;
+ } else {
+ if (!goog.i18n.GraphemeBreak.inversions_) {
+ goog.i18n.GraphemeBreak.inversions_ = new goog.structs.InversionMap(
+ [
+ 0, 10, 1, 2, 1, 18, 95, 33, 13, 1,
+ 594, 112, 275, 7, 263, 45, 1, 1, 1, 2,
+ 1, 2, 1, 1, 56, 6, 10, 11, 1, 1,
+ 46, 21, 16, 1, 101, 7, 1, 1, 6, 2,
+ 2, 1, 4, 33, 1, 1, 1, 30, 27, 91,
+ 11, 58, 9, 34, 4, 1, 9, 1, 3, 1,
+ 5, 43, 3, 120, 14, 1, 32, 1, 17, 37,
+ 1, 1, 1, 1, 3, 8, 4, 1, 2, 1,
+ 7, 8, 2, 2, 21, 7, 1, 1, 2, 17,
+ 39, 1, 1, 1, 2, 6, 6, 1, 9, 5,
+ 4, 2, 2, 12, 2, 15, 2, 1, 17, 39,
+ 2, 3, 12, 4, 8, 6, 17, 2, 3, 14,
+ 1, 17, 39, 1, 1, 3, 8, 4, 1, 20,
+ 2, 29, 1, 2, 17, 39, 1, 1, 2, 1,
+ 6, 6, 9, 6, 4, 2, 2, 13, 1, 16,
+ 1, 18, 41, 1, 1, 1, 12, 1, 9, 1,
+ 40, 1, 3, 17, 31, 1, 5, 4, 3, 5,
+ 7, 8, 3, 2, 8, 2, 29, 1, 2, 17,
+ 39, 1, 1, 1, 1, 2, 1, 3, 1, 5,
+ 1, 8, 9, 1, 3, 2, 29, 1, 2, 17,
+ 38, 3, 1, 2, 5, 7, 1, 1, 8, 1,
+ 10, 2, 30, 2, 22, 48, 5, 1, 2, 6,
+ 7, 1, 18, 2, 13, 46, 2, 1, 1, 1,
+ 6, 1, 12, 8, 50, 46, 2, 1, 1, 1,
+ 9, 11, 6, 14, 2, 58, 2, 27, 1, 1,
+ 1, 1, 1, 4, 2, 49, 14, 1, 4, 1,
+ 1, 2, 5, 48, 9, 1, 57, 33, 12, 4,
+ 1, 6, 1, 2, 2, 2, 1, 16, 2, 4,
+ 2, 2, 4, 3, 1, 3, 2, 7, 3, 4,
+ 13, 1, 1, 1, 2, 6, 1, 1, 14, 1,
+ 98, 96, 72, 88, 349, 3, 931, 15, 2, 1,
+ 14, 15, 2, 1, 14, 15, 2, 15, 15, 14,
+ 35, 17, 2, 1, 7, 8, 1, 2, 9, 1,
+ 1, 9, 1, 45, 3, 1, 118, 2, 34, 1,
+ 87, 28, 3, 3, 4, 2, 9, 1, 6, 3,
+ 20, 19, 29, 44, 84, 23, 2, 2, 1, 4,
+ 45, 6, 2, 1, 1, 1, 8, 1, 1, 1,
+ 2, 8, 6, 13, 48, 84, 1, 14, 33, 1,
+ 1, 5, 1, 1, 5, 1, 1, 1, 7, 31,
+ 9, 12, 2, 1, 7, 23, 1, 4, 2, 2,
+ 2, 2, 2, 11, 3, 2, 36, 2, 1, 1,
+ 2, 3, 1, 1, 3, 2, 12, 36, 8, 8,
+ 2, 2, 21, 3, 128, 3, 1, 13, 1, 7,
+ 4, 1, 4, 2, 1, 3, 2, 198, 64, 523,
+ 1, 1, 1, 2, 24, 7, 49, 16, 96, 33,
+ 1324, 1, 34, 1, 1, 1, 82, 2, 98, 1,
+ 14, 1, 1, 4, 86, 1, 1418, 3, 141, 1,
+ 96, 32, 554, 6, 105, 2, 30164, 4, 1, 10,
+ 32, 2, 80, 2, 272, 1, 3, 1, 4, 1,
+ 23, 2, 2, 1, 24, 30, 4, 4, 3, 8,
+ 1, 1, 13, 2, 16, 34, 16, 1, 1, 26,
+ 18, 24, 24, 4, 8, 2, 23, 11, 1, 1,
+ 12, 32, 3, 1, 5, 3, 3, 36, 1, 2,
+ 4, 2, 1, 3, 1, 36, 1, 32, 35, 6,
+ 2, 2, 2, 2, 12, 1, 8, 1, 1, 18,
+ 16, 1, 3, 6, 1, 1, 1, 3, 48, 1,
+ 1, 3, 2, 2, 5, 2, 1, 1, 32, 9,
+ 1, 2, 2, 5, 1, 1, 201, 14, 2, 1,
+ 1, 9, 8, 2, 1, 2, 1, 2, 1, 1,
+ 1, 18, 11184, 27, 49, 1028, 1024, 6942, 1, 737,
+ 16, 16, 16, 207, 1, 158, 2, 89, 3, 513,
+ 1, 226, 1, 149, 5, 1670, 15, 40, 7, 1,
+ 165, 2, 1305, 1, 1, 1, 53, 14, 1, 56,
+ 1, 2, 1, 45, 3, 4, 2, 1, 1, 2,
+ 1, 66, 3, 36, 5, 1, 6, 2, 62, 1,
+ 12, 2, 1, 48, 3, 9, 1, 1, 1, 2,
+ 6, 3, 95, 3, 3, 2, 1, 1, 2, 6,
+ 1, 160, 1, 3, 7, 1, 21, 2, 2, 56,
+ 1, 1, 1, 1, 1, 12, 1, 9, 1, 10,
+ 4, 15, 192, 3, 8, 2, 1, 2, 1, 1,
+ 105, 1, 2, 6, 1, 1, 2, 1, 1, 2,
+ 1, 1, 1, 235, 1, 2, 6, 4, 2, 1,
+ 1, 1, 27, 2, 82, 3, 8, 2, 1, 1,
+ 1, 1, 106, 1, 1, 1, 2, 6, 1, 1,
+ 101, 3, 2, 4, 1, 4, 1, 1283, 1, 14,
+ 1, 1, 82, 23, 1, 7, 1, 2, 1, 2,
+ 20025, 5, 59, 7, 1050, 62, 4, 19722, 2, 1,
+ 4, 5313, 1, 1, 3, 3, 1, 5, 8, 8,
+ 2, 7, 30, 4, 148, 3, 1979, 55, 4, 50,
+ 8, 1, 14, 1, 22, 1424, 2213, 7, 109, 7,
+ 2203, 26, 264, 1, 53, 1, 52, 1, 17, 1,
+ 13, 1, 16, 1, 3, 1, 25, 3, 2, 1,
+ 2, 3, 30, 1, 1, 1, 13, 5, 66, 2,
+ 2, 11, 21, 4, 4, 1, 1, 9, 3, 1,
+ 4, 3, 1, 3, 3, 1, 30, 1, 16, 2,
+ 106, 1, 4, 1, 71, 2, 4, 1, 21, 1,
+ 4, 2, 81, 1, 92, 3, 3, 5, 48, 1,
+ 17, 1, 16, 1, 16, 3, 9, 1, 11, 1,
+ 587, 5, 1, 1, 7, 1, 9, 10, 3, 2,
+ 788162, 31
+ ],
+ [
+ 1, 13, 1, 12, 1, 0, 1, 0, 1, 0, 2, 0, 2, 0, 2, 0, 2, 0,
+ 2, 0, 2, 0, 2, 0, 3, 0, 2, 0, 1, 0, 2, 0, 2, 0, 2, 3,
+ 0, 2, 0, 2, 0, 2, 0, 3, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2,
+ 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 3, 2, 4, 0, 5, 2, 4, 2,
+ 0, 4, 2, 4, 6, 4, 0, 2, 5, 0, 2, 0, 5, 0, 2, 4, 0, 5,
+ 2, 0, 2, 4, 2, 4, 6, 0, 2, 5, 0, 2, 0, 5, 0, 2, 4, 0,
+ 5, 2, 4, 2, 6, 2, 5, 0, 2, 0, 2, 4, 0, 5, 2, 0, 4, 2,
+ 4, 6, 0, 2, 0, 2, 4, 0, 5, 2, 0, 2, 4, 2, 4, 6, 2, 5,
+ 0, 2, 0, 5, 0, 2, 0, 5, 2, 4, 2, 4, 6, 0, 2, 0, 2, 4,
+ 0, 5, 0, 5, 0, 2, 4, 2, 6, 2, 5, 0, 2, 0, 2, 4, 0, 5,
+ 2, 0, 4, 2, 4, 2, 4, 2, 4, 2, 6, 2, 5, 0, 2, 0, 2, 4,
+ 0, 5, 0, 2, 4, 2, 4, 6, 3, 0, 2, 0, 2, 0, 4, 0, 5, 6,
+ 2, 4, 2, 4, 2, 0, 4, 0, 5, 0, 2, 0, 4, 2, 6, 0, 2, 0,
+ 5, 0, 2, 0, 4, 2, 0, 2, 0, 5, 0, 2, 0, 2, 0, 2, 0, 2,
+ 0, 4, 5, 2, 4, 2, 6, 0, 2, 0, 2, 0, 2, 0, 5, 0, 2, 4,
+ 2, 0, 6, 4, 2, 5, 0, 5, 0, 4, 2, 5, 2, 5, 0, 5, 0, 5,
+ 2, 5, 2, 0, 4, 2, 0, 2, 5, 0, 2, 0, 7, 8, 9, 0, 2, 0,
+ 5, 2, 6, 0, 5, 2, 6, 0, 5, 2, 0, 5, 2, 5, 0, 2, 4, 2,
+ 4, 2, 4, 2, 6, 2, 0, 2, 0, 2, 1, 0, 2, 0, 2, 0, 5, 0,
+ 2, 4, 2, 4, 2, 4, 2, 0, 5, 0, 5, 0, 5, 2, 4, 2, 0, 5,
+ 0, 5, 4, 2, 4, 2, 6, 0, 2, 0, 2, 4, 2, 0, 2, 4, 0, 5,
+ 2, 4, 2, 4, 2, 4, 2, 4, 6, 5, 0, 2, 0, 2, 4, 0, 5, 4,
+ 2, 4, 2, 6, 2, 5, 0, 5, 0, 5, 0, 2, 4, 2, 4, 2, 4, 2,
+ 6, 0, 5, 4, 2, 4, 2, 0, 5, 0, 2, 0, 2, 4, 2, 0, 2, 0,
+ 4, 2, 0, 2, 0, 2, 0, 1, 2, 15, 1, 0, 1, 0, 1, 0, 2, 0,
+ 16, 0, 17, 0, 17, 0, 17, 0, 16, 0, 17, 0, 16, 0, 17, 0, 2, 0,
+ 6, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0,
+ 6, 5, 2, 5, 4, 2, 4, 0, 5, 0, 5, 0, 5, 0, 5, 0, 4, 0,
+ 5, 4, 6, 2, 0, 2, 0, 5, 0, 2, 0, 5, 2, 4, 6, 0, 7, 2,
+ 4, 0, 5, 0, 5, 2, 4, 2, 4, 2, 4, 6, 0, 2, 0, 5, 2, 4,
+ 2, 4, 2, 0, 2, 0, 2, 4, 0, 5, 0, 5, 0, 5, 0, 2, 0, 5,
+ 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 5, 4, 2, 4, 0, 4, 6, 0,
+ 5, 0, 5, 0, 5, 0, 4, 2, 4, 2, 4, 0, 4, 6, 0, 11, 8, 9,
+ 0, 2, 0, 2, 0, 2, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 2,
+ 0, 2, 0, 2, 0, 2, 6, 0, 2, 0, 4, 2, 4, 0, 2, 6, 0, 6,
+ 2, 4, 0, 4, 2, 4, 6, 2, 0, 3, 0, 2, 0, 2, 4, 2, 6, 0,
+ 2, 0, 2, 4, 0, 4, 2, 4, 6, 0, 3, 0, 2, 0, 4, 2, 4, 2,
+ 6, 2, 0, 2, 0, 2, 4, 2, 6, 0, 2, 4, 0, 2, 0, 2, 4, 2,
+ 4, 6, 0, 2, 0, 4, 2, 0, 4, 2, 4, 6, 2, 4, 2, 0, 2, 4,
+ 2, 4, 2, 4, 2, 4, 2, 4, 6, 2, 0, 2, 4, 2, 4, 2, 4, 6,
+ 2, 0, 2, 0, 4, 2, 4, 2, 4, 6, 2, 0, 2, 4, 2, 4, 2, 6,
+ 2, 0, 2, 4, 2, 4, 2, 6, 0, 4, 2, 4, 6, 0, 2, 4, 2, 4,
+ 2, 4, 2, 0, 2, 0, 2, 0, 4, 2, 0, 2, 0, 1, 0, 2, 4, 2,
+ 0, 4, 2, 1, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0,
+ 2, 0, 2, 0, 2, 0, 2, 0, 14, 0, 17, 0, 17, 0, 17, 0, 16, 0,
+ 17, 0, 17, 0, 17, 0, 16, 0, 16, 0, 16, 0, 17, 0, 17, 0, 18, 0,
+ 16, 0, 16, 0, 19, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0, 17, 0,
+ 16, 0, 17, 0, 17, 0, 17, 0, 16, 0, 16, 0, 16, 0, 16, 0, 17, 0,
+ 16, 0, 16, 0, 17, 0, 17, 0, 16, 0, 16, 0, 16, 0, 16, 0, 16, 0,
+ 16, 0, 16, 0, 16, 0, 16, 0, 1, 2
+ ],
+ true);
+ }
+ return /** @type {number} */ (
+ goog.i18n.GraphemeBreak.inversions_.at(codePoint));
+ }
+};
+
+/**
+ * Extracts a code point from a string at the specified index.
+ *
+ * @param {string} str
+ * @param {number} index
+ * @return {number} Extracted code point.
+ * @private
+ */
+goog.i18n.GraphemeBreak.getCodePoint_ = function(str, index) {
+ var codePoint = goog.i18n.uChar.getCodePointAround(str, index);
+ return (codePoint < 0) ? -codePoint : codePoint;
+};
+
+/**
+ * Indicates if there is a grapheme cluster boundary between a and b.
+ *
+ * Legacy function. Does not cover cases where a sequence of code points is
+ * required in order to decide if there is a grapheme cluster boundary, such as
+ * emoji modifier sequences and emoji flag sequences. To cover all cases please
+ * use {@code hasGraphemeBreakStrings}.
+ *
+ * There are two kinds of grapheme clusters: 1) Legacy 2) Extended. This method
+ * is to check for both using a boolean flag to switch between them. If no flag
+ * is provided rules for the extended clusters will be used by default.
+ *
+ * @param {number} a The code point value of the first character.
+ * @param {number} b The code point value of the second character.
+ * @param {boolean=} opt_extended If true, indicates extended grapheme cluster;
+ * If false, indicates legacy cluster. Default value is true.
+ * @return {boolean} True if there is a grapheme cluster boundary between
+ * a and b; False otherwise.
+ */
+goog.i18n.GraphemeBreak.hasGraphemeBreak = function(a, b, opt_extended) {
+ return goog.i18n.GraphemeBreak.applyBreakRules_(a, b, opt_extended !== false);
+};
+
+/**
+ * Indicates if there is a grapheme cluster boundary between a and b.
+ *
+ * There are two kinds of grapheme clusters: 1) Legacy 2) Extended. This method
+ * is to check for both using a boolean flag to switch between them. If no flag
+ * is provided rules for the extended clusters will be used by default.
+ *
+ * @param {string} a String with the first sequence of characters.
+ * @param {string} b String with the second sequence of characters.
+ * @param {boolean=} opt_extended If true, indicates extended grapheme cluster;
+ * If false, indicates legacy cluster. Default value is true.
+ * @return {boolean} True if there is a grapheme cluster boundary between
+ * a and b; False otherwise.
+ */
+goog.i18n.GraphemeBreak.hasGraphemeBreakStrings = function(a, b, opt_extended) {
+ goog.asserts.assert(goog.isDef(a), 'First string should be defined.');
+ goog.asserts.assert(goog.isDef(b), 'Second string should be defined.');
+
+ // Break if any of the strings is empty.
+ if (a.length === 0 || b.length === 0) {
+ return true;
+ }
+
+ return goog.i18n.GraphemeBreak.applyBreakRules_(a, b, opt_extended !== false);
+};
diff --git a/chromium/third_party/ink/closure/i18n/uchar.js b/chromium/third_party/ink/closure/i18n/uchar.js
new file mode 100644
index 00000000000..6c22c402fc9
--- /dev/null
+++ b/chromium/third_party/ink/closure/i18n/uchar.js
@@ -0,0 +1,294 @@
+// Copyright 2009 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Collection of utility functions for Unicode character.
+ *
+ * @author cibu@google.com (Cibu Johny)
+ * @author burakov@google.com (Danny Burakov)
+ */
+
+goog.provide('goog.i18n.uChar');
+
+
+// Constants for handling Unicode supplementary characters (surrogate pairs).
+
+
+/**
+ * The minimum value for Supplementary code points.
+ * @type {number}
+ * @private
+ */
+goog.i18n.uChar.SUPPLEMENTARY_CODE_POINT_MIN_VALUE_ = 0x10000;
+
+
+/**
+ * The highest Unicode code point value (scalar value) according to the Unicode
+ * Standard.
+ * @type {number}
+ * @private
+ */
+goog.i18n.uChar.CODE_POINT_MAX_VALUE_ = 0x10FFFF;
+
+
+/**
+ * Lead surrogate minimum value.
+ * @type {number}
+ * @private
+ */
+goog.i18n.uChar.LEAD_SURROGATE_MIN_VALUE_ = 0xD800;
+
+
+/**
+ * Lead surrogate maximum value.
+ * @type {number}
+ * @private
+ */
+goog.i18n.uChar.LEAD_SURROGATE_MAX_VALUE_ = 0xDBFF;
+
+
+/**
+ * Trail surrogate minimum value.
+ * @type {number}
+ * @private
+ */
+goog.i18n.uChar.TRAIL_SURROGATE_MIN_VALUE_ = 0xDC00;
+
+
+/**
+ * Trail surrogate maximum value.
+ * @type {number}
+ * @private
+ */
+goog.i18n.uChar.TRAIL_SURROGATE_MAX_VALUE_ = 0xDFFF;
+
+
+/**
+ * The number of least significant bits of a supplementary code point that in
+ * UTF-16 become the least significant bits of the trail surrogate. The rest of
+ * the in-use bits of the supplementary code point become the least significant
+ * bits of the lead surrogate.
+ * @type {number}
+ * @private
+ */
+goog.i18n.uChar.TRAIL_SURROGATE_BIT_COUNT_ = 10;
+
+
+/**
+ * Gets the U+ notation string of a Unicode character. Ex: 'U+0041' for 'A'.
+ * @param {string} ch The given character.
+ * @return {string} The U+ notation of the given character.
+ */
+goog.i18n.uChar.toHexString = function(ch) {
+ var chCode = goog.i18n.uChar.toCharCode(ch);
+ var chCodeStr = 'U+' +
+ goog.i18n.uChar.padString_(chCode.toString(16).toUpperCase(), 4, '0');
+
+ return chCodeStr;
+};
+
+
+/**
+ * Gets a string padded with given character to get given size.
+ * @param {string} str The given string to be padded.
+ * @param {number} length The target size of the string.
+ * @param {string} ch The character to be padded with.
+ * @return {string} The padded string.
+ * @private
+ */
+goog.i18n.uChar.padString_ = function(str, length, ch) {
+ while (str.length < length) {
+ str = ch + str;
+ }
+ return str;
+};
+
+
+/**
+ * Gets Unicode value of the given character.
+ * @param {string} ch The given character, which in the case of a supplementary
+ * character is actually a surrogate pair. The remainder of the string is
+ * ignored.
+ * @return {number} The Unicode value of the character.
+ */
+goog.i18n.uChar.toCharCode = function(ch) {
+ return goog.i18n.uChar.getCodePointAround(ch, 0);
+};
+
+
+/**
+ * Gets a character from the given Unicode value. If the given code point is not
+ * a valid Unicode code point, null is returned.
+ * @param {number} code The Unicode value of the character.
+ * @return {?string} The character corresponding to the given Unicode value.
+ */
+goog.i18n.uChar.fromCharCode = function(code) {
+ if (!goog.isDefAndNotNull(code) ||
+ !(code >= 0 && code <= goog.i18n.uChar.CODE_POINT_MAX_VALUE_)) {
+ return null;
+ }
+ if (goog.i18n.uChar.isSupplementaryCodePoint(code)) {
+ // First, we split the code point into the trail surrogate part (the
+ // TRAIL_SURROGATE_BIT_COUNT_ least significant bits) and the lead surrogate
+ // part (the rest of the bits, shifted down; note that for now this includes
+ // the supplementary offset, also shifted down, to be subtracted off below).
+ var leadBits = code >> goog.i18n.uChar.TRAIL_SURROGATE_BIT_COUNT_;
+ var trailBits = code &
+ // A bit-mask to get the TRAIL_SURROGATE_BIT_COUNT_ (i.e. 10) least
+ // significant bits. 1 << 10 = 0x0400. 0x0400 - 1 = 0x03FF.
+ ((1 << goog.i18n.uChar.TRAIL_SURROGATE_BIT_COUNT_) - 1);
+
+ // Now we calculate the code point of each surrogate by adding each offset
+ // to the corresponding base code point.
+ var leadCodePoint = leadBits +
+ (goog.i18n.uChar.LEAD_SURROGATE_MIN_VALUE_ -
+ // Subtract off the supplementary offset, which had been shifted down
+ // with the rest of leadBits. We do this here instead of before the
+ // shift in order to save a separate subtraction step.
+ (goog.i18n.uChar.SUPPLEMENTARY_CODE_POINT_MIN_VALUE_ >>
+ goog.i18n.uChar.TRAIL_SURROGATE_BIT_COUNT_));
+ var trailCodePoint = trailBits + goog.i18n.uChar.TRAIL_SURROGATE_MIN_VALUE_;
+
+ // Convert the code points into a 2-character long string.
+ return String.fromCharCode(leadCodePoint) +
+ String.fromCharCode(trailCodePoint);
+ }
+ return String.fromCharCode(code);
+};
+
+
+/**
+ * Returns the Unicode code point at the specified index.
+ *
+ * If the char value specified at the given index is in the leading-surrogate
+ * range, and the following index is less than the length of {@code string}, and
+ * the char value at the following index is in the trailing-surrogate range,
+ * then the supplementary code point corresponding to this surrogate pair is
+ * returned.
+ *
+ * If the char value specified at the given index is in the trailing-surrogate
+ * range, and the preceding index is not before the start of {@code string}, and
+ * the char value at the preceding index is in the leading-surrogate range, then
+ * the negated supplementary code point corresponding to this surrogate pair is
+ * returned.
+ *
+ * The negation allows the caller to differentiate between the case where the
+ * given index is at the leading surrogate and the one where it is at the
+ * trailing surrogate, and thus deduce where the next character starts and
+ * preceding character ends.
+ *
+ * Otherwise, the char value at the given index is returned. Thus, a leading
+ * surrogate is returned when it is not followed by a trailing surrogate, and a
+ * trailing surrogate is returned when it is not preceded by a leading
+ * surrogate.
+ *
+ * @param {string} string The string.
+ * @param {number} index The index from which the code point is to be retrieved.
+ * @return {number} The code point at the given index. If the given index is
+ * that of the start (i.e. lead surrogate) of a surrogate pair, returns the code
+ * point encoded by the pair. If the given index is that of the end (i.e. trail
+ * surrogate) of a surrogate pair, returns the negated code pointed encoded by
+ * the pair.
+ */
+goog.i18n.uChar.getCodePointAround = function(string, index) {
+ var charCode = string.charCodeAt(index);
+ if (goog.i18n.uChar.isLeadSurrogateCodePoint(charCode) &&
+ index + 1 < string.length) {
+ var trail = string.charCodeAt(index + 1);
+ if (goog.i18n.uChar.isTrailSurrogateCodePoint(trail)) {
+ // Part of a surrogate pair.
+ return /** @type {number} */ (
+ goog.i18n.uChar.buildSupplementaryCodePoint(charCode, trail));
+ }
+ } else if (goog.i18n.uChar.isTrailSurrogateCodePoint(charCode) && index > 0) {
+ var lead = string.charCodeAt(index - 1);
+ if (goog.i18n.uChar.isLeadSurrogateCodePoint(lead)) {
+ // Part of a surrogate pair.
+ return /** @type {number} */ (
+ -goog.i18n.uChar.buildSupplementaryCodePoint(lead, charCode));
+ }
+ }
+ return charCode;
+};
+
+
+/**
+ * Determines the length of the string needed to represent the specified
+ * Unicode code point.
+ * @param {number} codePoint
+ * @return {number} 2 if codePoint is a supplementary character, 1 otherwise.
+ */
+goog.i18n.uChar.charCount = function(codePoint) {
+ return goog.i18n.uChar.isSupplementaryCodePoint(codePoint) ? 2 : 1;
+};
+
+
+/**
+ * Determines whether the specified Unicode code point is in the supplementary
+ * Unicode characters range.
+ * @param {number} codePoint
+ * @return {boolean} Whether then given code point is a supplementary character.
+ */
+goog.i18n.uChar.isSupplementaryCodePoint = function(codePoint) {
+ return codePoint >= goog.i18n.uChar.SUPPLEMENTARY_CODE_POINT_MIN_VALUE_ &&
+ codePoint <= goog.i18n.uChar.CODE_POINT_MAX_VALUE_;
+};
+
+
+/**
+ * Gets whether the given code point is a leading surrogate character.
+ * @param {number} codePoint
+ * @return {boolean} Whether the given code point is a leading surrogate
+ * character.
+ */
+goog.i18n.uChar.isLeadSurrogateCodePoint = function(codePoint) {
+ return codePoint >= goog.i18n.uChar.LEAD_SURROGATE_MIN_VALUE_ &&
+ codePoint <= goog.i18n.uChar.LEAD_SURROGATE_MAX_VALUE_;
+};
+
+
+/**
+ * Gets whether the given code point is a trailing surrogate character.
+ * @param {number} codePoint
+ * @return {boolean} Whether the given code point is a trailing surrogate
+ * character.
+ */
+goog.i18n.uChar.isTrailSurrogateCodePoint = function(codePoint) {
+ return codePoint >= goog.i18n.uChar.TRAIL_SURROGATE_MIN_VALUE_ &&
+ codePoint <= goog.i18n.uChar.TRAIL_SURROGATE_MAX_VALUE_;
+};
+
+
+/**
+ * Composes a supplementary Unicode code point from the given UTF-16 surrogate
+ * pair. If leadSurrogate isn't a leading surrogate code point or trailSurrogate
+ * isn't a trailing surrogate code point, null is returned.
+ * @param {number} lead The leading surrogate code point.
+ * @param {number} trail The trailing surrogate code point.
+ * @return {?number} The supplementary Unicode code point obtained by decoding
+ * the given UTF-16 surrogate pair.
+ */
+goog.i18n.uChar.buildSupplementaryCodePoint = function(lead, trail) {
+ if (goog.i18n.uChar.isLeadSurrogateCodePoint(lead) &&
+ goog.i18n.uChar.isTrailSurrogateCodePoint(trail)) {
+ var shiftedLeadOffset =
+ (lead << goog.i18n.uChar.TRAIL_SURROGATE_BIT_COUNT_) -
+ (goog.i18n.uChar.LEAD_SURROGATE_MIN_VALUE_
+ << goog.i18n.uChar.TRAIL_SURROGATE_BIT_COUNT_);
+ var trailOffset = trail - goog.i18n.uChar.TRAIL_SURROGATE_MIN_VALUE_ +
+ goog.i18n.uChar.SUPPLEMENTARY_CODE_POINT_MIN_VALUE_;
+ return shiftedLeadOffset + trailOffset;
+ }
+ return null;
+};
diff --git a/chromium/third_party/ink/closure/iter/iter.js b/chromium/third_party/ink/closure/iter/iter.js
new file mode 100644
index 00000000000..534d24adc4e
--- /dev/null
+++ b/chromium/third_party/ink/closure/iter/iter.js
@@ -0,0 +1,1284 @@
+// Copyright 2007 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Python style iteration utilities.
+ * @author arv@google.com (Erik Arvidsson)
+ */
+
+
+goog.provide('goog.iter');
+goog.provide('goog.iter.Iterable');
+goog.provide('goog.iter.Iterator');
+goog.provide('goog.iter.StopIteration');
+
+goog.require('goog.array');
+goog.require('goog.asserts');
+goog.require('goog.functions');
+goog.require('goog.math');
+
+
+/**
+ * @typedef {goog.iter.Iterator|{length:number}|{__iterator__}}
+ */
+goog.iter.Iterable;
+
+
+/**
+ * Singleton Error object that is used to terminate iterations.
+ * @const {!Error}
+ */
+goog.iter.StopIteration = ('StopIteration' in goog.global) ?
+ // For script engines that support legacy iterators.
+ goog.global['StopIteration'] :
+ {message: 'StopIteration', stack: ''};
+
+
+
+/**
+ * Class/interface for iterators. An iterator needs to implement a {@code next}
+ * method and it needs to throw a {@code goog.iter.StopIteration} when the
+ * iteration passes beyond the end. Iterators have no {@code hasNext} method.
+ * It is recommended to always use the helper functions to iterate over the
+ * iterator or in case you are only targeting JavaScript 1.7 for in loops.
+ * @constructor
+ * @template VALUE
+ */
+goog.iter.Iterator = function() {};
+
+
+/**
+ * Returns the next value of the iteration. This will throw the object
+ * {@see goog.iter#StopIteration} when the iteration passes the end.
+ * @return {VALUE} Any object or value.
+ */
+goog.iter.Iterator.prototype.next = function() {
+ throw goog.iter.StopIteration;
+};
+
+
+/**
+ * Returns the {@code Iterator} object itself. This is used to implement
+ * the iterator protocol in JavaScript 1.7
+ * @param {boolean=} opt_keys Whether to return the keys or values. Default is
+ * to only return the values. This is being used by the for-in loop (true)
+ * and the for-each-in loop (false). Even though the param gives a hint
+ * about what the iterator will return there is no guarantee that it will
+ * return the keys when true is passed.
+ * @return {!goog.iter.Iterator<VALUE>} The object itself.
+ */
+goog.iter.Iterator.prototype.__iterator__ = function(opt_keys) {
+ return this;
+};
+
+
+/**
+ * Returns an iterator that knows how to iterate over the values in the object.
+ * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable If the
+ * object is an iterator it will be returned as is. If the object has an
+ * {@code __iterator__} method that will be called to get the value
+ * iterator. If the object is an array-like object we create an iterator
+ * for that.
+ * @return {!goog.iter.Iterator<VALUE>} An iterator that knows how to iterate
+ * over the values in {@code iterable}.
+ * @template VALUE
+ */
+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;
+ }
+ // Don't include deleted elements.
+ if (!(i in iterable)) {
+ i++;
+ continue;
+ }
+ return iterable[i++];
+ }
+ };
+ return newIter;
+ }
+
+
+ // TODO(arv): Should we fall back on goog.structs.getValues()?
+ throw new Error('Not implemented');
+};
+
+
+/**
+ * Calls a function for each element in the iterator with the element of the
+ * iterator passed as argument.
+ *
+ * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator
+ * to iterate over. If the iterable is an object {@code toIterator} will be
+ * called on it.
+ * @param {function(this:THIS,VALUE,?,!goog.iter.Iterator<VALUE>)} f
+ * The function to call for every element. This function takes 3 arguments
+ * (the element, undefined, and the iterator) and the return value is
+ * irrelevant. The reason for passing undefined as the second argument is
+ * so that the same function can be used in {@see goog.array#forEach} as
+ * well as others. The third parameter is of type "number" for
+ * arraylike objects, undefined, otherwise.
+ * @param {THIS=} opt_obj The object to be used as the value of 'this' within
+ * {@code f}.
+ * @template THIS, VALUE
+ */
+goog.iter.forEach = function(iterable, f, opt_obj) {
+ if (goog.isArrayLike(iterable)) {
+
+ try {
+ // NOTES: this passes the index number to the second parameter
+ // of the callback contrary to the documentation above.
+ goog.array.forEach(
+ /** @type {IArrayLike<?>} */ (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;
+ }
+ }
+ }
+};
+
+
+/**
+ * Calls a function for every element in the iterator, and if the function
+ * returns true adds the element to a new iterator.
+ *
+ * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator
+ * to iterate over.
+ * @param {
+ * function(this:THIS,VALUE,undefined,!goog.iter.Iterator<VALUE>):boolean} f
+ * The function to call for every element. This function takes 3 arguments
+ * (the element, undefined, and the iterator) and should return a boolean.
+ * If the return value is true the element will be included in the returned
+ * iterator. If it is false the element is not included.
+ * @param {THIS=} opt_obj The object to be used as the value of 'this' within
+ * {@code f}.
+ * @return {!goog.iter.Iterator<VALUE>} A new iterator in which only elements
+ * that passed the test are present.
+ * @template THIS, VALUE
+ */
+goog.iter.filter = function(iterable, f, opt_obj) {
+ var iterator = goog.iter.toIterator(iterable);
+ var newIter = new goog.iter.Iterator;
+ newIter.next = function() {
+ while (true) {
+ var val = iterator.next();
+ if (f.call(opt_obj, val, undefined, iterator)) {
+ return val;
+ }
+ }
+ };
+ return newIter;
+};
+
+
+/**
+ * Calls a function for every element in the iterator, and if the function
+ * returns false adds the element to a new iterator.
+ *
+ * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator
+ * to iterate over.
+ * @param {
+ * function(this:THIS,VALUE,undefined,!goog.iter.Iterator<VALUE>):boolean} f
+ * The function to call for every element. This function takes 3 arguments
+ * (the element, undefined, and the iterator) and should return a boolean.
+ * If the return value is false the element will be included in the returned
+ * iterator. If it is true the element is not included.
+ * @param {THIS=} opt_obj The object to be used as the value of 'this' within
+ * {@code f}.
+ * @return {!goog.iter.Iterator<VALUE>} A new iterator in which only elements
+ * that did not pass the test are present.
+ * @template THIS, VALUE
+ */
+goog.iter.filterFalse = function(iterable, f, opt_obj) {
+ return goog.iter.filter(iterable, goog.functions.not(f), opt_obj);
+};
+
+
+/**
+ * Creates a new iterator that returns the values in a range. This function
+ * can take 1, 2 or 3 arguments:
+ * <pre>
+ * range(5) same as range(0, 5, 1)
+ * range(2, 5) same as range(2, 5, 1)
+ * </pre>
+ *
+ * @param {number} startOrStop The stop value if only one argument is provided.
+ * The start value if 2 or more arguments are provided. If only one
+ * argument is used the start value is 0.
+ * @param {number=} opt_stop The stop value. If left out then the first
+ * argument is used as the stop value.
+ * @param {number=} opt_step The number to increment with between each call to
+ * next. This can be negative.
+ * @return {!goog.iter.Iterator<number>} A new iterator that returns the values
+ * in the range.
+ */
+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 new 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;
+};
+
+
+/**
+ * Joins the values in a iterator with a delimiter.
+ * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator
+ * to get the values from.
+ * @param {string} deliminator The text to put between the values.
+ * @return {string} The joined value string.
+ * @template VALUE
+ */
+goog.iter.join = function(iterable, deliminator) {
+ return goog.iter.toArray(iterable).join(deliminator);
+};
+
+
+/**
+ * For every element in the iterator call a function and return a new iterator
+ * with that value.
+ *
+ * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
+ * iterator to iterate over.
+ * @param {
+ * function(this:THIS,VALUE,undefined,!goog.iter.Iterator<VALUE>):RESULT} f
+ * The function to call for every element. This function takes 3 arguments
+ * (the element, undefined, and the iterator) and should return a new value.
+ * @param {THIS=} opt_obj The object to be used as the value of 'this' within
+ * {@code f}.
+ * @return {!goog.iter.Iterator<RESULT>} A new iterator that returns the
+ * results of applying the function to each element in the original
+ * iterator.
+ * @template THIS, VALUE, RESULT
+ */
+goog.iter.map = function(iterable, f, opt_obj) {
+ var iterator = goog.iter.toIterator(iterable);
+ var newIter = new goog.iter.Iterator;
+ newIter.next = function() {
+ var val = iterator.next();
+ return f.call(opt_obj, val, undefined, iterator);
+ };
+ return newIter;
+};
+
+
+/**
+ * Passes every element of an iterator into a function and accumulates the
+ * result.
+ *
+ * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator
+ * to iterate over.
+ * @param {function(this:THIS,VALUE,VALUE):VALUE} f The function to call for
+ * every element. This function takes 2 arguments (the function's previous
+ * result or the initial value, and the value of the current element).
+ * function(previousValue, currentElement) : newValue.
+ * @param {VALUE} val The initial value to pass into the function on the first
+ * call.
+ * @param {THIS=} opt_obj The object to be used as the value of 'this' within
+ * f.
+ * @return {VALUE} Result of evaluating f repeatedly across the values of
+ * the iterator.
+ * @template THIS, VALUE
+ */
+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;
+};
+
+
+/**
+ * Goes through the values in the iterator. Calls f for each of these, and if
+ * any of them returns true, this returns true (without checking the rest). If
+ * all return false this will return false.
+ *
+ * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator
+ * object.
+ * @param {
+ * function(this:THIS,VALUE,undefined,!goog.iter.Iterator<VALUE>):boolean} f
+ * The function to call for every value. This function takes 3 arguments
+ * (the value, undefined, and the iterator) and should return a boolean.
+ * @param {THIS=} opt_obj The object to be used as the value of 'this' within
+ * {@code f}.
+ * @return {boolean} true if any value passes the test.
+ * @template THIS, VALUE
+ */
+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;
+};
+
+
+/**
+ * Goes through the values in the iterator. Calls f for each of these and if any
+ * of them returns false this returns false (without checking the rest). If all
+ * return true this will return true.
+ *
+ * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator
+ * object.
+ * @param {
+ * function(this:THIS,VALUE,undefined,!goog.iter.Iterator<VALUE>):boolean} f
+ * The function to call for every value. This function takes 3 arguments
+ * (the value, undefined, and the iterator) and should return a boolean.
+ * @param {THIS=} opt_obj The object to be used as the value of 'this' within
+ * {@code f}.
+ * @return {boolean} true if every value passes the test.
+ * @template THIS, VALUE
+ */
+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;
+};
+
+
+/**
+ * Takes zero or more iterables and returns one iterator that will iterate over
+ * them in the order chained.
+ * @param {...!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} var_args Any
+ * number of iterable objects.
+ * @return {!goog.iter.Iterator<VALUE>} Returns a new iterator that will
+ * iterate over all the given iterables' contents.
+ * @template VALUE
+ */
+goog.iter.chain = function(var_args) {
+ return goog.iter.chainFromIterable(arguments);
+};
+
+
+/**
+ * Takes a single iterable containing zero or more iterables and returns one
+ * iterator that will iterate over each one in the order given.
+ * @see https://goo.gl/5NRp5d
+ * @param {goog.iter.Iterable} iterable The iterable of iterables to chain.
+ * @return {!goog.iter.Iterator<VALUE>} Returns a new iterator that will
+ * iterate over all the contents of the iterables contained within
+ * {@code iterable}.
+ * @template VALUE
+ */
+goog.iter.chainFromIterable = function(iterable) {
+ var iterator = goog.iter.toIterator(iterable);
+ var iter = new goog.iter.Iterator();
+ var current = null;
+
+ iter.next = function() {
+ while (true) {
+ if (current == null) {
+ var it = iterator.next();
+ current = goog.iter.toIterator(it);
+ }
+ try {
+ return current.next();
+ } catch (ex) {
+ if (ex !== goog.iter.StopIteration) {
+ throw ex;
+ }
+ current = null;
+ }
+ }
+ };
+
+ return iter;
+};
+
+
+/**
+ * Builds a new iterator that iterates over the original, but skips elements as
+ * long as a supplied function returns true.
+ * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator
+ * object.
+ * @param {
+ * function(this:THIS,VALUE,undefined,!goog.iter.Iterator<VALUE>):boolean} f
+ * The function to call for every value. This function takes 3 arguments
+ * (the value, undefined, and the iterator) and should return a boolean.
+ * @param {THIS=} opt_obj The object to be used as the value of 'this' within
+ * {@code f}.
+ * @return {!goog.iter.Iterator<VALUE>} A new iterator that drops elements from
+ * the original iterator as long as {@code f} is true.
+ * @template THIS, VALUE
+ */
+goog.iter.dropWhile = function(iterable, f, opt_obj) {
+ var iterator = goog.iter.toIterator(iterable);
+ var newIter = new goog.iter.Iterator;
+ var dropping = true;
+ newIter.next = function() {
+ while (true) {
+ var val = iterator.next();
+ if (dropping && f.call(opt_obj, val, undefined, iterator)) {
+ continue;
+ } else {
+ dropping = false;
+ }
+ return val;
+ }
+ };
+ return newIter;
+};
+
+
+/**
+ * Builds a new iterator that iterates over the original, but only as long as a
+ * supplied function returns true.
+ * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator
+ * object.
+ * @param {
+ * function(this:THIS,VALUE,undefined,!goog.iter.Iterator<VALUE>):boolean} f
+ * The function to call for every value. This function takes 3 arguments
+ * (the value, undefined, and the iterator) and should return a boolean.
+ * @param {THIS=} opt_obj This is used as the 'this' object in f when called.
+ * @return {!goog.iter.Iterator<VALUE>} A new iterator that keeps elements in
+ * the original iterator as long as the function is true.
+ * @template THIS, VALUE
+ */
+goog.iter.takeWhile = function(iterable, f, opt_obj) {
+ var iterator = goog.iter.toIterator(iterable);
+ var iter = new goog.iter.Iterator();
+ iter.next = function() {
+ var val = iterator.next();
+ if (f.call(opt_obj, val, undefined, iterator)) {
+ return val;
+ }
+ throw goog.iter.StopIteration;
+ };
+ return iter;
+};
+
+
+/**
+ * Converts the iterator to an array
+ * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator
+ * to convert to an array.
+ * @return {!Array<VALUE>} An array of the elements the iterator iterates over.
+ * @template VALUE
+ */
+goog.iter.toArray = function(iterable) {
+ // Fast path for array-like.
+ if (goog.isArrayLike(iterable)) {
+ return goog.array.toArray(/** @type {!IArrayLike<?>} */ (iterable));
+ }
+ iterable = goog.iter.toIterator(iterable);
+ var array = [];
+ goog.iter.forEach(iterable, function(val) { array.push(val); });
+ return array;
+};
+
+
+/**
+ * Iterates over two iterables and returns true if they contain the same
+ * sequence of elements and have the same length.
+ * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable1 The first
+ * iterable object.
+ * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable2 The second
+ * iterable object.
+ * @param {function(VALUE,VALUE):boolean=} opt_equalsFn Optional comparison
+ * function.
+ * Should take two arguments to compare, and return true if the arguments
+ * are equal. Defaults to {@link goog.array.defaultCompareEquality} which
+ * compares the elements using the built-in '===' operator.
+ * @return {boolean} true if the iterables contain the same sequence of elements
+ * and have the same length.
+ * @template VALUE
+ */
+goog.iter.equals = function(iterable1, iterable2, opt_equalsFn) {
+ var fillValue = {};
+ var pairs = goog.iter.zipLongest(fillValue, iterable1, iterable2);
+ var equalsFn = opt_equalsFn || goog.array.defaultCompareEquality;
+ return goog.iter.every(
+ pairs, function(pair) { return equalsFn(pair[0], pair[1]); });
+};
+
+
+/**
+ * Advances the iterator to the next position, returning the given default value
+ * instead of throwing an exception if the iterator has no more entries.
+ * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterable
+ * object.
+ * @param {VALUE} defaultValue The value to return if the iterator is empty.
+ * @return {VALUE} The next item in the iteration, or defaultValue if the
+ * iterator was empty.
+ * @template VALUE
+ */
+goog.iter.nextOrValue = function(iterable, defaultValue) {
+ try {
+ return goog.iter.toIterator(iterable).next();
+ } catch (e) {
+ if (e != goog.iter.StopIteration) {
+ throw e;
+ }
+ return defaultValue;
+ }
+};
+
+
+/**
+ * Cartesian product of zero or more sets. Gives an iterator that gives every
+ * combination of one element chosen from each set. For example,
+ * ([1, 2], [3, 4]) gives ([1, 3], [1, 4], [2, 3], [2, 4]).
+ * @see http://docs.python.org/library/itertools.html#itertools.product
+ * @param {...!IArrayLike<VALUE>} var_args Zero or more sets, as
+ * arrays.
+ * @return {!goog.iter.Iterator<!Array<VALUE>>} An iterator that gives each
+ * n-tuple (as an array).
+ * @template VALUE
+ */
+goog.iter.product = function(var_args) {
+ var someArrayEmpty =
+ goog.array.some(arguments, function(arr) { return !arr.length; });
+
+ // An empty set in a cartesian product gives an empty set.
+ if (someArrayEmpty || !arguments.length) {
+ return new goog.iter.Iterator();
+ }
+
+ var iter = new goog.iter.Iterator();
+ var arrays = arguments;
+
+ // The first indices are [0, 0, ...]
+ var indicies = goog.array.repeat(0, arrays.length);
+
+ iter.next = function() {
+
+ if (indicies) {
+ var retVal = goog.array.map(indicies, function(valueIndex, arrayIndex) {
+ return arrays[arrayIndex][valueIndex];
+ });
+
+ // Generate the next-largest indices for the next call.
+ // Increase the rightmost index. If it goes over, increase the next
+ // rightmost (like carry-over addition).
+ for (var i = indicies.length - 1; i >= 0; i--) {
+ // Assertion prevents compiler warning below.
+ goog.asserts.assert(indicies);
+ if (indicies[i] < arrays[i].length - 1) {
+ indicies[i]++;
+ break;
+ }
+
+ // We're at the last indices (the last element of every array), so
+ // the iteration is over on the next call.
+ if (i == 0) {
+ indicies = null;
+ break;
+ }
+ // Reset the index in this column and loop back to increment the
+ // next one.
+ indicies[i] = 0;
+ }
+ return retVal;
+ }
+
+ throw goog.iter.StopIteration;
+ };
+
+ return iter;
+};
+
+
+/**
+ * Create an iterator to cycle over the iterable's elements indefinitely.
+ * For example, ([1, 2, 3]) would return : 1, 2, 3, 1, 2, 3, ...
+ * @see: http://docs.python.org/library/itertools.html#itertools.cycle.
+ * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
+ * iterable object.
+ * @return {!goog.iter.Iterator<VALUE>} An iterator that iterates indefinitely
+ * over the values in {@code iterable}.
+ * @template VALUE
+ */
+goog.iter.cycle = function(iterable) {
+ var baseIterator = goog.iter.toIterator(iterable);
+
+ // We maintain a cache to store the iterable elements as we iterate
+ // over them. The cache is used to return elements once we have
+ // iterated over the iterable once.
+ var cache = [];
+ var cacheIndex = 0;
+
+ var iter = new goog.iter.Iterator();
+
+ // This flag is set after the iterable is iterated over once
+ var useCache = false;
+
+ iter.next = function() {
+ var returnElement = null;
+
+ // Pull elements off the original iterator if not using cache
+ if (!useCache) {
+ try {
+ // Return the element from the iterable
+ returnElement = baseIterator.next();
+ cache.push(returnElement);
+ return returnElement;
+ } catch (e) {
+ // If an exception other than StopIteration is thrown
+ // or if there are no elements to iterate over (the iterable was empty)
+ // throw an exception
+ if (e != goog.iter.StopIteration || goog.array.isEmpty(cache)) {
+ throw e;
+ }
+ // set useCache to true after we know that a 'StopIteration' exception
+ // was thrown and the cache is not empty (to handle the 'empty iterable'
+ // use case)
+ useCache = true;
+ }
+ }
+
+ returnElement = cache[cacheIndex];
+ cacheIndex = (cacheIndex + 1) % cache.length;
+
+ return returnElement;
+ };
+
+ return iter;
+};
+
+
+/**
+ * Creates an iterator that counts indefinitely from a starting value.
+ * @see http://docs.python.org/2/library/itertools.html#itertools.count
+ * @param {number=} opt_start The starting value. Default is 0.
+ * @param {number=} opt_step The number to increment with between each call to
+ * next. Negative and floating point numbers are allowed. Default is 1.
+ * @return {!goog.iter.Iterator<number>} A new iterator that returns the values
+ * in the series.
+ */
+goog.iter.count = function(opt_start, opt_step) {
+ var counter = opt_start || 0;
+ var step = goog.isDef(opt_step) ? opt_step : 1;
+ var iter = new goog.iter.Iterator();
+
+ iter.next = function() {
+ var returnValue = counter;
+ counter += step;
+ return returnValue;
+ };
+
+ return iter;
+};
+
+
+/**
+ * Creates an iterator that returns the same object or value repeatedly.
+ * @param {VALUE} value Any object or value to repeat.
+ * @return {!goog.iter.Iterator<VALUE>} A new iterator that returns the
+ * repeated value.
+ * @template VALUE
+ */
+goog.iter.repeat = function(value) {
+ var iter = new goog.iter.Iterator();
+
+ iter.next = goog.functions.constant(value);
+
+ return iter;
+};
+
+
+/**
+ * Creates an iterator that returns running totals from the numbers in
+ * {@code iterable}. For example, the array {@code [1, 2, 3, 4, 5]} yields
+ * {@code 1 -> 3 -> 6 -> 10 -> 15}.
+ * @see http://docs.python.org/3.2/library/itertools.html#itertools.accumulate
+ * @param {!goog.iter.Iterable} iterable The iterable of numbers to
+ * accumulate.
+ * @return {!goog.iter.Iterator<number>} A new iterator that returns the
+ * numbers in the series.
+ */
+goog.iter.accumulate = function(iterable) {
+ var iterator = goog.iter.toIterator(iterable);
+ var total = 0;
+ var iter = new goog.iter.Iterator();
+
+ iter.next = function() {
+ total += iterator.next();
+ return total;
+ };
+
+ return iter;
+};
+
+
+/**
+ * Creates an iterator that returns arrays containing the ith elements from the
+ * provided iterables. The returned arrays will be the same size as the number
+ * of iterables given in {@code var_args}. Once the shortest iterable is
+ * exhausted, subsequent calls to {@code next()} will throw
+ * {@code goog.iter.StopIteration}.
+ * @see http://docs.python.org/2/library/itertools.html#itertools.izip
+ * @param {...!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} var_args Any
+ * number of iterable objects.
+ * @return {!goog.iter.Iterator<!Array<VALUE>>} A new iterator that returns
+ * arrays of elements from the provided iterables.
+ * @template VALUE
+ */
+goog.iter.zip = function(var_args) {
+ var args = arguments;
+ var iter = new goog.iter.Iterator();
+
+ if (args.length > 0) {
+ var iterators = goog.array.map(args, goog.iter.toIterator);
+ iter.next = function() {
+ var arr = goog.array.map(iterators, function(it) { return it.next(); });
+ return arr;
+ };
+ }
+
+ return iter;
+};
+
+
+/**
+ * Creates an iterator that returns arrays containing the ith elements from the
+ * provided iterables. The returned arrays will be the same size as the number
+ * of iterables given in {@code var_args}. Shorter iterables will be extended
+ * with {@code fillValue}. Once the longest iterable is exhausted, subsequent
+ * calls to {@code next()} will throw {@code goog.iter.StopIteration}.
+ * @see http://docs.python.org/2/library/itertools.html#itertools.izip_longest
+ * @param {VALUE} fillValue The object or value used to fill shorter iterables.
+ * @param {...!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} var_args Any
+ * number of iterable objects.
+ * @return {!goog.iter.Iterator<!Array<VALUE>>} A new iterator that returns
+ * arrays of elements from the provided iterables.
+ * @template VALUE
+ */
+goog.iter.zipLongest = function(fillValue, var_args) {
+ var args = goog.array.slice(arguments, 1);
+ var iter = new goog.iter.Iterator();
+
+ if (args.length > 0) {
+ var iterators = goog.array.map(args, goog.iter.toIterator);
+
+ iter.next = function() {
+ var iteratorsHaveValues = false; // false when all iterators are empty.
+ var arr = goog.array.map(iterators, function(it) {
+ var returnValue;
+ try {
+ returnValue = it.next();
+ // Iterator had a value, so we've not exhausted the iterators.
+ // Set flag accordingly.
+ iteratorsHaveValues = true;
+ } catch (ex) {
+ if (ex !== goog.iter.StopIteration) {
+ throw ex;
+ }
+ returnValue = fillValue;
+ }
+ return returnValue;
+ });
+
+ if (!iteratorsHaveValues) {
+ throw goog.iter.StopIteration;
+ }
+ return arr;
+ };
+ }
+
+ return iter;
+};
+
+
+/**
+ * Creates an iterator that filters {@code iterable} based on a series of
+ * {@code selectors}. On each call to {@code next()}, one item is taken from
+ * both the {@code iterable} and {@code selectors} iterators. If the item from
+ * {@code selectors} evaluates to true, the item from {@code iterable} is given.
+ * Otherwise, it is skipped. Once either {@code iterable} or {@code selectors}
+ * is exhausted, subsequent calls to {@code next()} will throw
+ * {@code goog.iter.StopIteration}.
+ * @see http://docs.python.org/2/library/itertools.html#itertools.compress
+ * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
+ * iterable to filter.
+ * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} selectors An
+ * iterable of items to be evaluated in a boolean context to determine if
+ * the corresponding element in {@code iterable} should be included in the
+ * result.
+ * @return {!goog.iter.Iterator<VALUE>} A new iterator that returns the
+ * filtered values.
+ * @template VALUE
+ */
+goog.iter.compress = function(iterable, selectors) {
+ var selectorIterator = goog.iter.toIterator(selectors);
+
+ return goog.iter.filter(
+ iterable, function() { return !!selectorIterator.next(); });
+};
+
+
+
+/**
+ * Implements the {@code goog.iter.groupBy} iterator.
+ * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
+ * iterable to group.
+ * @param {function(VALUE): KEY=} opt_keyFunc Optional function for
+ * determining the key value for each group in the {@code iterable}. Default
+ * is the identity function.
+ * @constructor
+ * @extends {goog.iter.Iterator<!Array<?>>}
+ * @template KEY, VALUE
+ * @private
+ */
+goog.iter.GroupByIterator_ = function(iterable, opt_keyFunc) {
+
+ /**
+ * The iterable to group, coerced to an iterator.
+ * @type {!goog.iter.Iterator}
+ */
+ this.iterator = goog.iter.toIterator(iterable);
+
+ /**
+ * A function for determining the key value for each element in the iterable.
+ * If no function is provided, the identity function is used and returns the
+ * element unchanged.
+ * @type {function(VALUE): KEY}
+ */
+ this.keyFunc = opt_keyFunc || goog.functions.identity;
+
+ /**
+ * The target key for determining the start of a group.
+ * @type {KEY}
+ */
+ this.targetKey;
+
+ /**
+ * The current key visited during iteration.
+ * @type {KEY}
+ */
+ this.currentKey;
+
+ /**
+ * The current value being added to the group.
+ * @type {VALUE}
+ */
+ this.currentValue;
+};
+goog.inherits(goog.iter.GroupByIterator_, goog.iter.Iterator);
+
+
+/** @override */
+goog.iter.GroupByIterator_.prototype.next = function() {
+ while (this.currentKey == this.targetKey) {
+ this.currentValue = this.iterator.next(); // Exits on StopIteration
+ this.currentKey = this.keyFunc(this.currentValue);
+ }
+ this.targetKey = this.currentKey;
+ return [this.currentKey, this.groupItems_(this.targetKey)];
+};
+
+
+/**
+ * Performs the grouping of objects using the given key.
+ * @param {KEY} targetKey The target key object for the group.
+ * @return {!Array<VALUE>} An array of grouped objects.
+ * @private
+ */
+goog.iter.GroupByIterator_.prototype.groupItems_ = function(targetKey) {
+ var arr = [];
+ while (this.currentKey == targetKey) {
+ arr.push(this.currentValue);
+ try {
+ this.currentValue = this.iterator.next();
+ } catch (ex) {
+ if (ex !== goog.iter.StopIteration) {
+ throw ex;
+ }
+ break;
+ }
+ this.currentKey = this.keyFunc(this.currentValue);
+ }
+ return arr;
+};
+
+
+/**
+ * Creates an iterator that returns arrays containing elements from the
+ * {@code iterable} grouped by a key value. For iterables with repeated
+ * elements (i.e. sorted according to a particular key function), this function
+ * has a {@code uniq}-like effect. For example, grouping the array:
+ * {@code [A, B, B, C, C, A]} produces
+ * {@code [A, [A]], [B, [B, B]], [C, [C, C]], [A, [A]]}.
+ * @see http://docs.python.org/2/library/itertools.html#itertools.groupby
+ * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
+ * iterable to group.
+ * @param {function(VALUE): KEY=} opt_keyFunc Optional function for
+ * determining the key value for each group in the {@code iterable}. Default
+ * is the identity function.
+ * @return {!goog.iter.Iterator<!Array<?>>} A new iterator that returns
+ * arrays of consecutive key and groups.
+ * @template KEY, VALUE
+ */
+goog.iter.groupBy = function(iterable, opt_keyFunc) {
+ return new goog.iter.GroupByIterator_(iterable, opt_keyFunc);
+};
+
+
+/**
+ * Gives an iterator that gives the result of calling the given function
+ * <code>f</code> with the arguments taken from the next element from
+ * <code>iterable</code> (the elements are expected to also be iterables).
+ *
+ * Similar to {@see goog.iter#map} but allows the function to accept multiple
+ * arguments from the iterable.
+ *
+ * @param {!goog.iter.Iterable} iterable The iterable of
+ * iterables to iterate over.
+ * @param {function(this:THIS,...*):RESULT} f The function to call for every
+ * element. This function takes N+2 arguments, where N represents the
+ * number of items from the next element of the iterable. The two
+ * additional arguments passed to the function are undefined and the
+ * iterator itself. The function should return a new value.
+ * @param {THIS=} opt_obj The object to be used as the value of 'this' within
+ * {@code f}.
+ * @return {!goog.iter.Iterator<RESULT>} A new iterator that returns the
+ * results of applying the function to each element in the original
+ * iterator.
+ * @template THIS, RESULT
+ */
+goog.iter.starMap = function(iterable, f, opt_obj) {
+ var iterator = goog.iter.toIterator(iterable);
+ var iter = new goog.iter.Iterator();
+
+ iter.next = function() {
+ var args = goog.iter.toArray(iterator.next());
+ return f.apply(opt_obj, goog.array.concat(args, undefined, iterator));
+ };
+
+ return iter;
+};
+
+
+/**
+ * Returns an array of iterators each of which can iterate over the values in
+ * {@code iterable} without advancing the others.
+ * @see http://docs.python.org/2/library/itertools.html#itertools.tee
+ * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
+ * iterable to tee.
+ * @param {number=} opt_num The number of iterators to create. Default is 2.
+ * @return {!Array<goog.iter.Iterator<VALUE>>} An array of iterators.
+ * @template VALUE
+ */
+goog.iter.tee = function(iterable, opt_num) {
+ var iterator = goog.iter.toIterator(iterable);
+ var num = goog.isNumber(opt_num) ? opt_num : 2;
+ var buffers =
+ goog.array.map(goog.array.range(num), function() { return []; });
+
+ var addNextIteratorValueToBuffers = function() {
+ var val = iterator.next();
+ goog.array.forEach(buffers, function(buffer) { buffer.push(val); });
+ };
+
+ var createIterator = function(buffer) {
+ // Each tee'd iterator has an associated buffer (initially empty). When a
+ // tee'd iterator's buffer is empty, it calls
+ // addNextIteratorValueToBuffers(), adding the next value to all tee'd
+ // iterators' buffers, and then returns that value. This allows each
+ // iterator to be advanced independently.
+ var iter = new goog.iter.Iterator();
+
+ iter.next = function() {
+ if (goog.array.isEmpty(buffer)) {
+ addNextIteratorValueToBuffers();
+ }
+ goog.asserts.assert(!goog.array.isEmpty(buffer));
+ return buffer.shift();
+ };
+
+ return iter;
+ };
+
+ return goog.array.map(buffers, createIterator);
+};
+
+
+/**
+ * Creates an iterator that returns arrays containing a count and an element
+ * obtained from the given {@code iterable}.
+ * @see http://docs.python.org/2/library/functions.html#enumerate
+ * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
+ * iterable to enumerate.
+ * @param {number=} opt_start Optional starting value. Default is 0.
+ * @return {!goog.iter.Iterator<!Array<?>>} A new iterator containing
+ * count/item pairs.
+ * @template VALUE
+ */
+goog.iter.enumerate = function(iterable, opt_start) {
+ return goog.iter.zip(goog.iter.count(opt_start), iterable);
+};
+
+
+/**
+ * Creates an iterator that returns the first {@code limitSize} elements from an
+ * iterable. If this number is greater than the number of elements in the
+ * iterable, all the elements are returned.
+ * @see http://goo.gl/V0sihp Inspired by the limit iterator in Guava.
+ * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
+ * iterable to limit.
+ * @param {number} limitSize The maximum number of elements to return.
+ * @return {!goog.iter.Iterator<VALUE>} A new iterator containing
+ * {@code limitSize} elements.
+ * @template VALUE
+ */
+goog.iter.limit = function(iterable, limitSize) {
+ goog.asserts.assert(goog.math.isInt(limitSize) && limitSize >= 0);
+
+ var iterator = goog.iter.toIterator(iterable);
+
+ var iter = new goog.iter.Iterator();
+ var remaining = limitSize;
+
+ iter.next = function() {
+ if (remaining-- > 0) {
+ return iterator.next();
+ }
+ throw goog.iter.StopIteration;
+ };
+
+ return iter;
+};
+
+
+/**
+ * Creates an iterator that is advanced {@code count} steps ahead. Consumed
+ * values are silently discarded. If {@code count} is greater than the number
+ * of elements in {@code iterable}, an empty iterator is returned. Subsequent
+ * calls to {@code next()} will throw {@code goog.iter.StopIteration}.
+ * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
+ * iterable to consume.
+ * @param {number} count The number of elements to consume from the iterator.
+ * @return {!goog.iter.Iterator<VALUE>} An iterator advanced zero or more steps
+ * ahead.
+ * @template VALUE
+ */
+goog.iter.consume = function(iterable, count) {
+ goog.asserts.assert(goog.math.isInt(count) && count >= 0);
+
+ var iterator = goog.iter.toIterator(iterable);
+
+ while (count-- > 0) {
+ goog.iter.nextOrValue(iterator, null);
+ }
+
+ return iterator;
+};
+
+
+/**
+ * Creates an iterator that returns a range of elements from an iterable.
+ * Similar to {@see goog.array#slice} but does not support negative indexes.
+ * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
+ * iterable to slice.
+ * @param {number} start The index of the first element to return.
+ * @param {number=} opt_end The index after the last element to return. If
+ * defined, must be greater than or equal to {@code start}.
+ * @return {!goog.iter.Iterator<VALUE>} A new iterator containing a slice of
+ * the original.
+ * @template VALUE
+ */
+goog.iter.slice = function(iterable, start, opt_end) {
+ goog.asserts.assert(goog.math.isInt(start) && start >= 0);
+
+ var iterator = goog.iter.consume(iterable, start);
+
+ if (goog.isNumber(opt_end)) {
+ goog.asserts.assert(goog.math.isInt(opt_end) && opt_end >= start);
+ iterator = goog.iter.limit(iterator, opt_end - start /* limitSize */);
+ }
+
+ return iterator;
+};
+
+
+/**
+ * Checks an array for duplicate elements.
+ * @param {?IArrayLike<VALUE>} arr The array to check for
+ * duplicates.
+ * @return {boolean} True, if the array contains duplicates, false otherwise.
+ * @private
+ * @template VALUE
+ */
+// TODO(dlindquist): Consider moving this into goog.array as a public function.
+goog.iter.hasDuplicates_ = function(arr) {
+ var deduped = [];
+ goog.array.removeDuplicates(arr, deduped);
+ return arr.length != deduped.length;
+};
+
+
+/**
+ * Creates an iterator that returns permutations of elements in
+ * {@code iterable}.
+ *
+ * Permutations are obtained by taking the Cartesian product of
+ * {@code opt_length} iterables and filtering out those with repeated
+ * elements. For example, the permutations of {@code [1,2,3]} are
+ * {@code [[1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1]]}.
+ * @see http://docs.python.org/2/library/itertools.html#itertools.permutations
+ * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
+ * iterable from which to generate permutations.
+ * @param {number=} opt_length Length of each permutation. If omitted, defaults
+ * to the length of {@code iterable}.
+ * @return {!goog.iter.Iterator<!Array<VALUE>>} A new iterator containing the
+ * permutations of {@code iterable}.
+ * @template VALUE
+ */
+goog.iter.permutations = function(iterable, opt_length) {
+ var elements = goog.iter.toArray(iterable);
+ var length = goog.isNumber(opt_length) ? opt_length : elements.length;
+
+ var sets = goog.array.repeat(elements, length);
+ var product = goog.iter.product.apply(undefined, sets);
+
+ return goog.iter.filter(
+ product, function(arr) { return !goog.iter.hasDuplicates_(arr); });
+};
+
+
+/**
+ * Creates an iterator that returns combinations of elements from
+ * {@code iterable}.
+ *
+ * Combinations are obtained by taking the {@see goog.iter#permutations} of
+ * {@code iterable} and filtering those whose elements appear in the order they
+ * are encountered in {@code iterable}. For example, the 3-length combinations
+ * of {@code [0,1,2,3]} are {@code [[0,1,2], [0,1,3], [0,2,3], [1,2,3]]}.
+ * @see http://docs.python.org/2/library/itertools.html#itertools.combinations
+ * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
+ * iterable from which to generate combinations.
+ * @param {number} length The length of each combination.
+ * @return {!goog.iter.Iterator<!Array<VALUE>>} A new iterator containing
+ * combinations from the {@code iterable}.
+ * @template VALUE
+ */
+goog.iter.combinations = function(iterable, length) {
+ var elements = goog.iter.toArray(iterable);
+ var indexes = goog.iter.range(elements.length);
+ var indexIterator = goog.iter.permutations(indexes, length);
+ // sortedIndexIterator will now give arrays of with the given length that
+ // indicate what indexes into "elements" should be returned on each iteration.
+ var sortedIndexIterator = goog.iter.filter(
+ indexIterator, function(arr) { return goog.array.isSorted(arr); });
+
+ var iter = new goog.iter.Iterator();
+
+ function getIndexFromElements(index) { return elements[index]; }
+
+ iter.next = function() {
+ return goog.array.map(sortedIndexIterator.next(), getIndexFromElements);
+ };
+
+ return iter;
+};
+
+
+/**
+ * Creates an iterator that returns combinations of elements from
+ * {@code iterable}, with repeated elements possible.
+ *
+ * Combinations are obtained by taking the Cartesian product of {@code length}
+ * iterables and filtering those whose elements appear in the order they are
+ * encountered in {@code iterable}. For example, the 2-length combinations of
+ * {@code [1,2,3]} are {@code [[1,1], [1,2], [1,3], [2,2], [2,3], [3,3]]}.
+ * @see https://goo.gl/C0yXe4
+ * @see https://goo.gl/djOCsk
+ * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The
+ * iterable to combine.
+ * @param {number} length The length of each combination.
+ * @return {!goog.iter.Iterator<!Array<VALUE>>} A new iterator containing
+ * combinations from the {@code iterable}.
+ * @template VALUE
+ */
+goog.iter.combinationsWithReplacement = function(iterable, length) {
+ var elements = goog.iter.toArray(iterable);
+ var indexes = goog.array.range(elements.length);
+ var sets = goog.array.repeat(indexes, length);
+ var indexIterator = goog.iter.product.apply(undefined, sets);
+ // sortedIndexIterator will now give arrays of with the given length that
+ // indicate what indexes into "elements" should be returned on each iteration.
+ var sortedIndexIterator = goog.iter.filter(
+ indexIterator, function(arr) { return goog.array.isSorted(arr); });
+
+ var iter = new goog.iter.Iterator();
+
+ function getIndexFromElements(index) { return elements[index]; }
+
+ iter.next = function() {
+ return goog.array.map(
+ /** @type {!Array<number>} */
+ (sortedIndexIterator.next()), getIndexFromElements);
+ };
+
+ return iter;
+};
diff --git a/chromium/third_party/ink/closure/labs/useragent/browser.js b/chromium/third_party/ink/closure/labs/useragent/browser.js
new file mode 100644
index 00000000000..78578e4e95e
--- /dev/null
+++ b/chromium/third_party/ink/closure/labs/useragent/browser.js
@@ -0,0 +1,339 @@
+// Copyright 2013 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Closure user agent detection (Browser).
+ * @see <a href="http://www.useragentstring.com/">User agent strings</a>
+ * For more information on rendering engine, platform, or device see the other
+ * sub-namespaces in goog.labs.userAgent, goog.labs.userAgent.platform,
+ * goog.labs.userAgent.device respectively.)
+ *
+ * @author vbhasin@google.com (Vipul Bhasin)
+ * @author martone@google.com (Andy Martone)
+ */
+
+goog.provide('goog.labs.userAgent.browser');
+
+goog.require('goog.array');
+goog.require('goog.labs.userAgent.util');
+goog.require('goog.object');
+goog.require('goog.string');
+
+
+// TODO(nnaze): Refactor to remove excessive exclusion logic in matching
+// functions.
+
+
+/**
+ * @return {boolean} Whether the user's browser is Opera. Note: Chromium
+ * based Opera (Opera 15+) is detected as Chrome to avoid unnecessary
+ * special casing.
+ * @private
+ */
+goog.labs.userAgent.browser.matchOpera_ = function() {
+ return goog.labs.userAgent.util.matchUserAgent('Opera');
+};
+
+
+/**
+ * @return {boolean} Whether the user's browser is IE.
+ * @private
+ */
+goog.labs.userAgent.browser.matchIE_ = function() {
+ return goog.labs.userAgent.util.matchUserAgent('Trident') ||
+ goog.labs.userAgent.util.matchUserAgent('MSIE');
+};
+
+
+/**
+ * @return {boolean} Whether the user's browser is Edge.
+ * @private
+ */
+goog.labs.userAgent.browser.matchEdge_ = function() {
+ return goog.labs.userAgent.util.matchUserAgent('Edge');
+};
+
+
+/**
+ * @return {boolean} Whether the user's browser is Firefox.
+ * @private
+ */
+goog.labs.userAgent.browser.matchFirefox_ = function() {
+ return goog.labs.userAgent.util.matchUserAgent('Firefox');
+};
+
+
+/**
+ * @return {boolean} Whether the user's browser is Safari.
+ * @private
+ */
+goog.labs.userAgent.browser.matchSafari_ = function() {
+ return goog.labs.userAgent.util.matchUserAgent('Safari') &&
+ !(goog.labs.userAgent.browser.matchChrome_() ||
+ goog.labs.userAgent.browser.matchCoast_() ||
+ goog.labs.userAgent.browser.matchOpera_() ||
+ goog.labs.userAgent.browser.matchEdge_() ||
+ goog.labs.userAgent.browser.isSilk() ||
+ goog.labs.userAgent.util.matchUserAgent('Android'));
+};
+
+
+/**
+ * @return {boolean} Whether the user's browser is Coast (Opera's Webkit-based
+ * iOS browser).
+ * @private
+ */
+goog.labs.userAgent.browser.matchCoast_ = function() {
+ return goog.labs.userAgent.util.matchUserAgent('Coast');
+};
+
+
+/**
+ * @return {boolean} Whether the user's browser is iOS Webview.
+ * @private
+ */
+goog.labs.userAgent.browser.matchIosWebview_ = function() {
+ // iOS Webview does not show up as Chrome or Safari. Also check for Opera's
+ // WebKit-based iOS browser, Coast.
+ return (goog.labs.userAgent.util.matchUserAgent('iPad') ||
+ goog.labs.userAgent.util.matchUserAgent('iPhone')) &&
+ !goog.labs.userAgent.browser.matchSafari_() &&
+ !goog.labs.userAgent.browser.matchChrome_() &&
+ !goog.labs.userAgent.browser.matchCoast_() &&
+ goog.labs.userAgent.util.matchUserAgent('AppleWebKit');
+};
+
+
+/**
+ * @return {boolean} Whether the user's browser is Chrome.
+ * @private
+ */
+goog.labs.userAgent.browser.matchChrome_ = function() {
+ return (goog.labs.userAgent.util.matchUserAgent('Chrome') ||
+ goog.labs.userAgent.util.matchUserAgent('CriOS')) &&
+ !goog.labs.userAgent.browser.matchEdge_();
+};
+
+
+/**
+ * @return {boolean} Whether the user's browser is the Android browser.
+ * @private
+ */
+goog.labs.userAgent.browser.matchAndroidBrowser_ = function() {
+ // Android can appear in the user agent string for Chrome on Android.
+ // This is not the Android standalone browser if it does.
+ return goog.labs.userAgent.util.matchUserAgent('Android') &&
+ !(goog.labs.userAgent.browser.isChrome() ||
+ goog.labs.userAgent.browser.isFirefox() ||
+ goog.labs.userAgent.browser.isOpera() ||
+ goog.labs.userAgent.browser.isSilk());
+};
+
+
+/**
+ * @return {boolean} Whether the user's browser is Opera.
+ */
+goog.labs.userAgent.browser.isOpera = goog.labs.userAgent.browser.matchOpera_;
+
+
+/**
+ * @return {boolean} Whether the user's browser is IE.
+ */
+goog.labs.userAgent.browser.isIE = goog.labs.userAgent.browser.matchIE_;
+
+
+/**
+ * @return {boolean} Whether the user's browser is Edge.
+ */
+goog.labs.userAgent.browser.isEdge = goog.labs.userAgent.browser.matchEdge_;
+
+
+/**
+ * @return {boolean} Whether the user's browser is Firefox.
+ */
+goog.labs.userAgent.browser.isFirefox =
+ goog.labs.userAgent.browser.matchFirefox_;
+
+
+/**
+ * @return {boolean} Whether the user's browser is Safari.
+ */
+goog.labs.userAgent.browser.isSafari = goog.labs.userAgent.browser.matchSafari_;
+
+
+/**
+ * @return {boolean} Whether the user's browser is Coast (Opera's Webkit-based
+ * iOS browser).
+ */
+goog.labs.userAgent.browser.isCoast = goog.labs.userAgent.browser.matchCoast_;
+
+
+/**
+ * @return {boolean} Whether the user's browser is iOS Webview.
+ */
+goog.labs.userAgent.browser.isIosWebview =
+ goog.labs.userAgent.browser.matchIosWebview_;
+
+
+/**
+ * @return {boolean} Whether the user's browser is Chrome.
+ */
+goog.labs.userAgent.browser.isChrome = goog.labs.userAgent.browser.matchChrome_;
+
+
+/**
+ * @return {boolean} Whether the user's browser is the Android browser.
+ */
+goog.labs.userAgent.browser.isAndroidBrowser =
+ goog.labs.userAgent.browser.matchAndroidBrowser_;
+
+
+/**
+ * For more information, see:
+ * http://docs.aws.amazon.com/silk/latest/developerguide/user-agent.html
+ * @return {boolean} Whether the user's browser is Silk.
+ */
+goog.labs.userAgent.browser.isSilk = function() {
+ return goog.labs.userAgent.util.matchUserAgent('Silk');
+};
+
+
+/**
+ * @return {string} The browser version or empty string if version cannot be
+ * determined. Note that for Internet Explorer, this returns the version of
+ * the browser, not the version of the rendering engine. (IE 8 in
+ * compatibility mode will return 8.0 rather than 7.0. To determine the
+ * rendering engine version, look at document.documentMode instead. See
+ * http://msdn.microsoft.com/en-us/library/cc196988(v=vs.85).aspx for more
+ * details.)
+ */
+goog.labs.userAgent.browser.getVersion = function() {
+ var userAgentString = goog.labs.userAgent.util.getUserAgent();
+ // Special case IE since IE's version is inside the parenthesis and
+ // without the '/'.
+ if (goog.labs.userAgent.browser.isIE()) {
+ return goog.labs.userAgent.browser.getIEVersion_(userAgentString);
+ }
+
+ var versionTuples =
+ goog.labs.userAgent.util.extractVersionTuples(userAgentString);
+
+ // Construct a map for easy lookup.
+ var versionMap = {};
+ goog.array.forEach(versionTuples, function(tuple) {
+ // Note that the tuple is of length three, but we only care about the
+ // first two.
+ var key = tuple[0];
+ var value = tuple[1];
+ versionMap[key] = value;
+ });
+
+ var versionMapHasKey = goog.partial(goog.object.containsKey, versionMap);
+
+ // Gives the value with the first key it finds, otherwise empty string.
+ function lookUpValueWithKeys(keys) {
+ var key = goog.array.find(keys, versionMapHasKey);
+ return versionMap[key] || '';
+ }
+
+ // Check Opera before Chrome since Opera 15+ has "Chrome" in the string.
+ // See
+ // http://my.opera.com/ODIN/blog/2013/07/15/opera-user-agent-strings-opera-15-and-beyond
+ if (goog.labs.userAgent.browser.isOpera()) {
+ // Opera 10 has Version/10.0 but Opera/9.8, so look for "Version" first.
+ // Opera uses 'OPR' for more recent UAs.
+ return lookUpValueWithKeys(['Version', 'Opera']);
+ }
+
+ // Check Edge before Chrome since it has Chrome in the string.
+ if (goog.labs.userAgent.browser.isEdge()) {
+ return lookUpValueWithKeys(['Edge']);
+ }
+
+ if (goog.labs.userAgent.browser.isChrome()) {
+ return lookUpValueWithKeys(['Chrome', 'CriOS']);
+ }
+
+ // Usually products browser versions are in the third tuple after "Mozilla"
+ // and the engine.
+ var tuple = versionTuples[2];
+ return tuple && tuple[1] || '';
+};
+
+
+/**
+ * @param {string|number} version The version to check.
+ * @return {boolean} Whether the browser version is higher or the same as the
+ * given version.
+ */
+goog.labs.userAgent.browser.isVersionOrHigher = function(version) {
+ return goog.string.compareVersions(
+ goog.labs.userAgent.browser.getVersion(), version) >= 0;
+};
+
+
+/**
+ * Determines IE version. More information:
+ * http://msdn.microsoft.com/en-us/library/ie/bg182625(v=vs.85).aspx#uaString
+ * http://msdn.microsoft.com/en-us/library/hh869301(v=vs.85).aspx
+ * http://blogs.msdn.com/b/ie/archive/2010/03/23/introducing-ie9-s-user-agent-string.aspx
+ * http://blogs.msdn.com/b/ie/archive/2009/01/09/the-internet-explorer-8-user-agent-string-updated-edition.aspx
+ *
+ * @param {string} userAgent the User-Agent.
+ * @return {string}
+ * @private
+ */
+goog.labs.userAgent.browser.getIEVersion_ = function(userAgent) {
+ // IE11 may identify itself as MSIE 9.0 or MSIE 10.0 due to an IE 11 upgrade
+ // bug. Example UA:
+ // Mozilla/5.0 (MSIE 9.0; Windows NT 6.1; WOW64; Trident/7.0; rv:11.0)
+ // like Gecko.
+ // See http://www.whatismybrowser.com/developers/unknown-user-agent-fragments.
+ var rv = /rv: *([\d\.]*)/.exec(userAgent);
+ if (rv && rv[1]) {
+ return rv[1];
+ }
+
+ var version = '';
+ var msie = /MSIE +([\d\.]+)/.exec(userAgent);
+ if (msie && msie[1]) {
+ // IE in compatibility mode usually identifies itself as MSIE 7.0; in this
+ // case, use the Trident version to determine the version of IE. For more
+ // details, see the links above.
+ var tridentVersion = /Trident\/(\d.\d)/.exec(userAgent);
+ if (msie[1] == '7.0') {
+ if (tridentVersion && tridentVersion[1]) {
+ switch (tridentVersion[1]) {
+ case '4.0':
+ version = '8.0';
+ break;
+ case '5.0':
+ version = '9.0';
+ break;
+ case '6.0':
+ version = '10.0';
+ break;
+ case '7.0':
+ version = '11.0';
+ break;
+ }
+ } else {
+ version = '7.0';
+ }
+ } else {
+ version = msie[1];
+ }
+ }
+ return version;
+};
diff --git a/chromium/third_party/ink/closure/labs/useragent/engine.js b/chromium/third_party/ink/closure/labs/useragent/engine.js
new file mode 100644
index 00000000000..4de0ff33efe
--- /dev/null
+++ b/chromium/third_party/ink/closure/labs/useragent/engine.js
@@ -0,0 +1,157 @@
+// Copyright 2013 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Closure user agent detection.
+ * @see http://en.wikipedia.org/wiki/User_agent
+ * For more information on browser brand, platform, or device see the other
+ * sub-namespaces in goog.labs.userAgent (browser, platform, and device).
+ *
+ * @author vbhasin@google.com (Vipul Bhasin)
+ */
+
+goog.provide('goog.labs.userAgent.engine');
+
+goog.require('goog.array');
+goog.require('goog.labs.userAgent.util');
+goog.require('goog.string');
+
+
+/**
+ * @return {boolean} Whether the rendering engine is Presto.
+ */
+goog.labs.userAgent.engine.isPresto = function() {
+ return goog.labs.userAgent.util.matchUserAgent('Presto');
+};
+
+
+/**
+ * @return {boolean} Whether the rendering engine is Trident.
+ */
+goog.labs.userAgent.engine.isTrident = function() {
+ // IE only started including the Trident token in IE8.
+ return goog.labs.userAgent.util.matchUserAgent('Trident') ||
+ goog.labs.userAgent.util.matchUserAgent('MSIE');
+};
+
+
+/**
+ * @return {boolean} Whether the rendering engine is Edge.
+ */
+goog.labs.userAgent.engine.isEdge = function() {
+ return goog.labs.userAgent.util.matchUserAgent('Edge');
+};
+
+
+/**
+ * @return {boolean} Whether the rendering engine is WebKit.
+ */
+goog.labs.userAgent.engine.isWebKit = function() {
+ return goog.labs.userAgent.util.matchUserAgentIgnoreCase('WebKit') &&
+ !goog.labs.userAgent.engine.isEdge();
+};
+
+
+/**
+ * @return {boolean} Whether the rendering engine is Gecko.
+ */
+goog.labs.userAgent.engine.isGecko = function() {
+ return goog.labs.userAgent.util.matchUserAgent('Gecko') &&
+ !goog.labs.userAgent.engine.isWebKit() &&
+ !goog.labs.userAgent.engine.isTrident() &&
+ !goog.labs.userAgent.engine.isEdge();
+};
+
+
+/**
+ * @return {string} The rendering engine's version or empty string if version
+ * can't be determined.
+ */
+goog.labs.userAgent.engine.getVersion = function() {
+ var userAgentString = goog.labs.userAgent.util.getUserAgent();
+ if (userAgentString) {
+ var tuples = goog.labs.userAgent.util.extractVersionTuples(userAgentString);
+
+ var engineTuple = goog.labs.userAgent.engine.getEngineTuple_(tuples);
+ if (engineTuple) {
+ // In Gecko, the version string is either in the browser info or the
+ // Firefox version. See Gecko user agent string reference:
+ // http://goo.gl/mULqa
+ if (engineTuple[0] == 'Gecko') {
+ return goog.labs.userAgent.engine.getVersionForKey_(tuples, 'Firefox');
+ }
+
+ return engineTuple[1];
+ }
+
+ // MSIE has only one version identifier, and the Trident version is
+ // specified in the parenthetical. IE Edge is covered in the engine tuple
+ // detection.
+ var browserTuple = tuples[0];
+ var info;
+ if (browserTuple && (info = browserTuple[2])) {
+ var match = /Trident\/([^\s;]+)/.exec(info);
+ if (match) {
+ return match[1];
+ }
+ }
+ }
+ return '';
+};
+
+
+/**
+ * @param {!Array<!Array<string>>} tuples Extracted version tuples.
+ * @return {!Array<string>|undefined} The engine tuple or undefined if not
+ * found.
+ * @private
+ */
+goog.labs.userAgent.engine.getEngineTuple_ = function(tuples) {
+ if (!goog.labs.userAgent.engine.isEdge()) {
+ return tuples[1];
+ }
+ for (var i = 0; i < tuples.length; i++) {
+ var tuple = tuples[i];
+ if (tuple[0] == 'Edge') {
+ return tuple;
+ }
+ }
+};
+
+
+/**
+ * @param {string|number} version The version to check.
+ * @return {boolean} Whether the rendering engine version is higher or the same
+ * as the given version.
+ */
+goog.labs.userAgent.engine.isVersionOrHigher = function(version) {
+ return goog.string.compareVersions(
+ goog.labs.userAgent.engine.getVersion(), version) >= 0;
+};
+
+
+/**
+ * @param {!Array<!Array<string>>} tuples Version tuples.
+ * @param {string} key The key to look for.
+ * @return {string} The version string of the given key, if present.
+ * Otherwise, the empty string.
+ * @private
+ */
+goog.labs.userAgent.engine.getVersionForKey_ = function(tuples, key) {
+ // TODO(nnaze): Move to util if useful elsewhere.
+
+ var pair = goog.array.find(tuples, function(pair) { return key == pair[0]; });
+
+ return pair && pair[1] || '';
+};
diff --git a/chromium/third_party/ink/closure/labs/useragent/platform.js b/chromium/third_party/ink/closure/labs/useragent/platform.js
new file mode 100644
index 00000000000..d5c35379e34
--- /dev/null
+++ b/chromium/third_party/ink/closure/labs/useragent/platform.js
@@ -0,0 +1,161 @@
+// Copyright 2013 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Closure user agent platform detection.
+ * @see <a href="http://www.useragentstring.com/">User agent strings</a>
+ * For more information on browser brand, rendering engine, or device see the
+ * other sub-namespaces in goog.labs.userAgent (browser, engine, and device
+ * respectively).
+ *
+ * @author vbhasin@google.com (Vipul Bhasin)
+ */
+
+goog.provide('goog.labs.userAgent.platform');
+
+goog.require('goog.labs.userAgent.util');
+goog.require('goog.string');
+
+
+/**
+ * @return {boolean} Whether the platform is Android.
+ */
+goog.labs.userAgent.platform.isAndroid = function() {
+ return goog.labs.userAgent.util.matchUserAgent('Android');
+};
+
+
+/**
+ * @return {boolean} Whether the platform is iPod.
+ */
+goog.labs.userAgent.platform.isIpod = function() {
+ return goog.labs.userAgent.util.matchUserAgent('iPod');
+};
+
+
+/**
+ * @return {boolean} Whether the platform is iPhone.
+ */
+goog.labs.userAgent.platform.isIphone = function() {
+ return goog.labs.userAgent.util.matchUserAgent('iPhone') &&
+ !goog.labs.userAgent.util.matchUserAgent('iPod') &&
+ !goog.labs.userAgent.util.matchUserAgent('iPad');
+};
+
+
+/**
+ * @return {boolean} Whether the platform is iPad.
+ */
+goog.labs.userAgent.platform.isIpad = function() {
+ return goog.labs.userAgent.util.matchUserAgent('iPad');
+};
+
+
+/**
+ * @return {boolean} Whether the platform is iOS.
+ */
+goog.labs.userAgent.platform.isIos = function() {
+ return goog.labs.userAgent.platform.isIphone() ||
+ goog.labs.userAgent.platform.isIpad() ||
+ goog.labs.userAgent.platform.isIpod();
+};
+
+
+/**
+ * @return {boolean} Whether the platform is Mac.
+ */
+goog.labs.userAgent.platform.isMacintosh = function() {
+ return goog.labs.userAgent.util.matchUserAgent('Macintosh');
+};
+
+
+/**
+ * Note: ChromeOS is not considered to be Linux as it does not report itself
+ * as Linux in the user agent string.
+ * @return {boolean} Whether the platform is Linux.
+ */
+goog.labs.userAgent.platform.isLinux = function() {
+ return goog.labs.userAgent.util.matchUserAgent('Linux');
+};
+
+
+/**
+ * @return {boolean} Whether the platform is Windows.
+ */
+goog.labs.userAgent.platform.isWindows = function() {
+ return goog.labs.userAgent.util.matchUserAgent('Windows');
+};
+
+
+/**
+ * @return {boolean} Whether the platform is ChromeOS.
+ */
+goog.labs.userAgent.platform.isChromeOS = function() {
+ return goog.labs.userAgent.util.matchUserAgent('CrOS');
+};
+
+
+/**
+ * The version of the platform. We only determine the version for Windows,
+ * Mac, and Chrome OS. It doesn't make much sense on Linux. For Windows, we only
+ * look at the NT version. Non-NT-based versions (e.g. 95, 98, etc.) are given
+ * version 0.0.
+ *
+ * @return {string} The platform version or empty string if version cannot be
+ * determined.
+ */
+goog.labs.userAgent.platform.getVersion = function() {
+ var userAgentString = goog.labs.userAgent.util.getUserAgent();
+ var version = '', re;
+ if (goog.labs.userAgent.platform.isWindows()) {
+ re = /Windows (?:NT|Phone) ([0-9.]+)/;
+ var match = re.exec(userAgentString);
+ if (match) {
+ version = match[1];
+ } else {
+ version = '0.0';
+ }
+ } else if (goog.labs.userAgent.platform.isIos()) {
+ re = /(?:iPhone|iPod|iPad|CPU)\s+OS\s+(\S+)/;
+ var match = re.exec(userAgentString);
+ // Report the version as x.y.z and not x_y_z
+ version = match && match[1].replace(/_/g, '.');
+ } else if (goog.labs.userAgent.platform.isMacintosh()) {
+ re = /Mac OS X ([0-9_.]+)/;
+ var match = re.exec(userAgentString);
+ // Note: some old versions of Camino do not report an OSX version.
+ // Default to 10.
+ version = match ? match[1].replace(/_/g, '.') : '10';
+ } else if (goog.labs.userAgent.platform.isAndroid()) {
+ re = /Android\s+([^\);]+)(\)|;)/;
+ var match = re.exec(userAgentString);
+ version = match && match[1];
+ } else if (goog.labs.userAgent.platform.isChromeOS()) {
+ re = /(?:CrOS\s+(?:i686|x86_64)\s+([0-9.]+))/;
+ var match = re.exec(userAgentString);
+ version = match && match[1];
+ }
+ return version || '';
+};
+
+
+/**
+ * @param {string|number} version The version to check.
+ * @return {boolean} Whether the browser version is higher or the same as the
+ * given version.
+ */
+goog.labs.userAgent.platform.isVersionOrHigher = function(version) {
+ return goog.string.compareVersions(
+ goog.labs.userAgent.platform.getVersion(), version) >= 0;
+};
diff --git a/chromium/third_party/ink/closure/labs/useragent/util.js b/chromium/third_party/ink/closure/labs/useragent/util.js
new file mode 100644
index 00000000000..a57e5d8dd0c
--- /dev/null
+++ b/chromium/third_party/ink/closure/labs/useragent/util.js
@@ -0,0 +1,157 @@
+// Copyright 2013 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Utilities used by goog.labs.userAgent tools. These functions
+ * should not be used outside of goog.labs.userAgent.*.
+ *
+ * MOE:begin_intracomment_strip
+ * @visibility {//javascript/abc/libs/objects3d:__subpackages__}
+ * @visibility {//javascript/closure/bin/sizetests:__pkg__}
+ * @visibility {//javascript/closure/dom:__subpackages__}
+ * @visibility {//javascript/closure/style:__pkg__}
+ * @visibility {//javascript/closure/testing:__pkg__}
+ * @visibility {//javascript/closure/useragent:__subpackages__}
+ * @visibility {//testing/puppet/modules:__pkg__}
+ * @visibility {:util_legacy_users}
+ * MOE:end_intracomment_strip
+ *
+ * @author nnaze@google.com (Nathan Naze)
+ */
+
+goog.provide('goog.labs.userAgent.util');
+
+goog.require('goog.string');
+
+
+/**
+ * Gets the native userAgent string from navigator if it exists.
+ * If navigator or navigator.userAgent string is missing, returns an empty
+ * string.
+ * @return {string}
+ * @private
+ */
+goog.labs.userAgent.util.getNativeUserAgentString_ = function() {
+ var navigator = goog.labs.userAgent.util.getNavigator_();
+ if (navigator) {
+ var userAgent = navigator.userAgent;
+ if (userAgent) {
+ return userAgent;
+ }
+ }
+ return '';
+};
+
+
+/**
+ * Getter for the native navigator.
+ * This is a separate function so it can be stubbed out in testing.
+ * @return {Navigator}
+ * @private
+ */
+goog.labs.userAgent.util.getNavigator_ = function() {
+ return goog.global.navigator;
+};
+
+
+/**
+ * A possible override for applications which wish to not check
+ * navigator.userAgent but use a specified value for detection instead.
+ * @private {string}
+ */
+goog.labs.userAgent.util.userAgent_ =
+ goog.labs.userAgent.util.getNativeUserAgentString_();
+
+
+/**
+ * Applications may override browser detection on the built in
+ * navigator.userAgent object by setting this string. Set to null to use the
+ * browser object instead.
+ * @param {?string=} opt_userAgent The User-Agent override.
+ */
+goog.labs.userAgent.util.setUserAgent = function(opt_userAgent) {
+ goog.labs.userAgent.util.userAgent_ =
+ opt_userAgent || goog.labs.userAgent.util.getNativeUserAgentString_();
+};
+
+
+/**
+ * @return {string} The user agent string.
+ */
+goog.labs.userAgent.util.getUserAgent = function() {
+ return goog.labs.userAgent.util.userAgent_;
+};
+
+
+/**
+ * @param {string} str
+ * @return {boolean} Whether the user agent contains the given string.
+ */
+goog.labs.userAgent.util.matchUserAgent = function(str) {
+ var userAgent = goog.labs.userAgent.util.getUserAgent();
+ return goog.string.contains(userAgent, str);
+};
+
+
+/**
+ * @param {string} str
+ * @return {boolean} Whether the user agent contains the given string, ignoring
+ * case.
+ */
+goog.labs.userAgent.util.matchUserAgentIgnoreCase = function(str) {
+ var userAgent = goog.labs.userAgent.util.getUserAgent();
+ return goog.string.caseInsensitiveContains(userAgent, str);
+};
+
+
+/**
+ * Parses the user agent into tuples for each section.
+ * @param {string} userAgent
+ * @return {!Array<!Array<string>>} Tuples of key, version, and the contents
+ * of the parenthetical.
+ */
+goog.labs.userAgent.util.extractVersionTuples = function(userAgent) {
+ // Matches each section of a user agent string.
+ // Example UA:
+ // Mozilla/5.0 (iPad; U; CPU OS 3_2_1 like Mac OS X; en-us)
+ // AppleWebKit/531.21.10 (KHTML, like Gecko) Mobile/7B405
+ // This has three version tuples: Mozilla, AppleWebKit, and Mobile.
+
+ var versionRegExp = new RegExp(
+ // Key. Note that a key may have a space.
+ // (i.e. 'Mobile Safari' in 'Mobile Safari/5.0')
+ '(\\w[\\w ]+)' +
+
+ '/' + // slash
+ '([^\\s]+)' + // version (i.e. '5.0b')
+ '\\s*' + // whitespace
+ '(?:\\((.*?)\\))?', // parenthetical info. parentheses not matched.
+ 'g');
+
+ var data = [];
+ var match;
+
+ // Iterate and collect the version tuples. Each iteration will be the
+ // next regex match.
+ while (match = versionRegExp.exec(userAgent)) {
+ data.push([
+ match[1], // key
+ match[2], // value
+ // || undefined as this is not undefined in IE7 and IE8
+ match[3] || undefined // info
+ ]);
+ }
+
+ return data;
+};
diff --git a/chromium/third_party/ink/closure/math/box.js b/chromium/third_party/ink/closure/math/box.js
new file mode 100644
index 00000000000..108e6f77a99
--- /dev/null
+++ b/chromium/third_party/ink/closure/math/box.js
@@ -0,0 +1,403 @@
+// Copyright 2006 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview A utility class for representing a numeric box.
+ * @author pupius@google.com (Daniel Pupius)
+ */
+
+
+goog.provide('goog.math.Box');
+
+goog.require('goog.asserts');
+goog.require('goog.math.Coordinate');
+
+
+
+/**
+ * Class for representing a box. A box is specified as a top, right, bottom,
+ * and left. A box is useful for representing margins and padding.
+ *
+ * This class assumes 'screen coordinates': larger Y coordinates are further
+ * from the top of the screen.
+ *
+ * @param {number} top Top.
+ * @param {number} right Right.
+ * @param {number} bottom Bottom.
+ * @param {number} left Left.
+ * @struct
+ * @constructor
+ */
+goog.math.Box = function(top, right, bottom, left) {
+ /**
+ * Top
+ * @type {number}
+ */
+ this.top = top;
+
+ /**
+ * Right
+ * @type {number}
+ */
+ this.right = right;
+
+ /**
+ * Bottom
+ * @type {number}
+ */
+ this.bottom = bottom;
+
+ /**
+ * Left
+ * @type {number}
+ */
+ this.left = left;
+};
+
+
+/**
+ * Creates a Box by bounding a collection of goog.math.Coordinate objects
+ * @param {...goog.math.Coordinate} var_args Coordinates to be included inside
+ * the box.
+ * @return {!goog.math.Box} A Box containing all the specified Coordinates.
+ */
+goog.math.Box.boundingBox = function(var_args) {
+ var box = new goog.math.Box(
+ arguments[0].y, arguments[0].x, arguments[0].y, arguments[0].x);
+ for (var i = 1; i < arguments.length; i++) {
+ box.expandToIncludeCoordinate(arguments[i]);
+ }
+ return box;
+};
+
+
+/**
+ * @return {number} width The width of this Box.
+ */
+goog.math.Box.prototype.getWidth = function() {
+ return this.right - this.left;
+};
+
+
+/**
+ * @return {number} height The height of this Box.
+ */
+goog.math.Box.prototype.getHeight = function() {
+ return this.bottom - this.top;
+};
+
+
+/**
+ * Creates a copy of the box with the same dimensions.
+ * @return {!goog.math.Box} A clone of this Box.
+ */
+goog.math.Box.prototype.clone = function() {
+ return new goog.math.Box(this.top, this.right, this.bottom, this.left);
+};
+
+
+if (goog.DEBUG) {
+ /**
+ * Returns a nice string representing the box.
+ * @return {string} In the form (50t, 73r, 24b, 13l).
+ * @override
+ */
+ goog.math.Box.prototype.toString = function() {
+ return '(' + this.top + 't, ' + this.right + 'r, ' + this.bottom + 'b, ' +
+ this.left + 'l)';
+ };
+}
+
+
+/**
+ * Returns whether the box contains a coordinate or another box.
+ *
+ * @param {goog.math.Coordinate|goog.math.Box} other A Coordinate or a Box.
+ * @return {boolean} Whether the box contains the coordinate or other box.
+ */
+goog.math.Box.prototype.contains = function(other) {
+ return goog.math.Box.contains(this, other);
+};
+
+
+/**
+ * Expands box with the given margins.
+ *
+ * @param {number|goog.math.Box} top Top margin or box with all margins.
+ * @param {number=} opt_right Right margin.
+ * @param {number=} opt_bottom Bottom margin.
+ * @param {number=} opt_left Left margin.
+ * @return {!goog.math.Box} A reference to this Box.
+ */
+goog.math.Box.prototype.expand = function(
+ top, opt_right, opt_bottom, opt_left) {
+ if (goog.isObject(top)) {
+ this.top -= top.top;
+ this.right += top.right;
+ this.bottom += top.bottom;
+ this.left -= top.left;
+ } else {
+ this.top -= /** @type {number} */ (top);
+ this.right += Number(opt_right);
+ this.bottom += Number(opt_bottom);
+ this.left -= Number(opt_left);
+ }
+
+ return this;
+};
+
+
+/**
+ * Expand this box to include another box.
+ * NOTE(bcornell): This is used in code that needs to be very fast, please don't
+ * add functionality to this function at the expense of speed (variable
+ * arguments, accepting multiple argument types, etc).
+ * @param {goog.math.Box} box The box to include in this one.
+ */
+goog.math.Box.prototype.expandToInclude = function(box) {
+ this.left = Math.min(this.left, box.left);
+ this.top = Math.min(this.top, box.top);
+ this.right = Math.max(this.right, box.right);
+ this.bottom = Math.max(this.bottom, box.bottom);
+};
+
+
+/**
+ * Expand this box to include the coordinate.
+ * @param {!goog.math.Coordinate} coord The coordinate to be included
+ * inside the box.
+ */
+goog.math.Box.prototype.expandToIncludeCoordinate = function(coord) {
+ this.top = Math.min(this.top, coord.y);
+ this.right = Math.max(this.right, coord.x);
+ this.bottom = Math.max(this.bottom, coord.y);
+ this.left = Math.min(this.left, coord.x);
+};
+
+
+/**
+ * Compares boxes for equality.
+ * @param {goog.math.Box} a A Box.
+ * @param {goog.math.Box} b A Box.
+ * @return {boolean} True iff the boxes are equal, or if both are null.
+ */
+goog.math.Box.equals = function(a, b) {
+ if (a == b) {
+ return true;
+ }
+ if (!a || !b) {
+ return false;
+ }
+ return a.top == b.top && a.right == b.right && a.bottom == b.bottom &&
+ a.left == b.left;
+};
+
+
+/**
+ * Returns whether a box contains a coordinate or another box.
+ *
+ * @param {goog.math.Box} box A Box.
+ * @param {goog.math.Coordinate|goog.math.Box} other A Coordinate or a Box.
+ * @return {boolean} Whether the box contains the coordinate or other box.
+ */
+goog.math.Box.contains = function(box, other) {
+ if (!box || !other) {
+ return false;
+ }
+
+ if (other instanceof goog.math.Box) {
+ return other.left >= box.left && other.right <= box.right &&
+ other.top >= box.top && other.bottom <= box.bottom;
+ }
+
+ // other is a Coordinate.
+ return other.x >= box.left && other.x <= box.right && other.y >= box.top &&
+ other.y <= box.bottom;
+};
+
+
+/**
+ * Returns the relative x position of a coordinate compared to a box. Returns
+ * zero if the coordinate is inside the box.
+ *
+ * @param {goog.math.Box} box A Box.
+ * @param {goog.math.Coordinate} coord A Coordinate.
+ * @return {number} The x position of {@code coord} relative to the nearest
+ * side of {@code box}, or zero if {@code coord} is inside {@code box}.
+ */
+goog.math.Box.relativePositionX = function(box, coord) {
+ if (coord.x < box.left) {
+ return coord.x - box.left;
+ } else if (coord.x > box.right) {
+ return coord.x - box.right;
+ }
+ return 0;
+};
+
+
+/**
+ * Returns the relative y position of a coordinate compared to a box. Returns
+ * zero if the coordinate is inside the box.
+ *
+ * @param {goog.math.Box} box A Box.
+ * @param {goog.math.Coordinate} coord A Coordinate.
+ * @return {number} The y position of {@code coord} relative to the nearest
+ * side of {@code box}, or zero if {@code coord} is inside {@code box}.
+ */
+goog.math.Box.relativePositionY = function(box, coord) {
+ if (coord.y < box.top) {
+ return coord.y - box.top;
+ } else if (coord.y > box.bottom) {
+ return coord.y - box.bottom;
+ }
+ return 0;
+};
+
+
+/**
+ * Returns the distance between a coordinate and the nearest corner/side of a
+ * box. Returns zero if the coordinate is inside the box.
+ *
+ * @param {goog.math.Box} box A Box.
+ * @param {goog.math.Coordinate} coord A Coordinate.
+ * @return {number} The distance between {@code coord} and the nearest
+ * corner/side of {@code box}, or zero if {@code coord} is inside
+ * {@code box}.
+ */
+goog.math.Box.distance = function(box, coord) {
+ var x = goog.math.Box.relativePositionX(box, coord);
+ var y = goog.math.Box.relativePositionY(box, coord);
+ return Math.sqrt(x * x + y * y);
+};
+
+
+/**
+ * Returns whether two boxes intersect.
+ *
+ * @param {goog.math.Box} a A Box.
+ * @param {goog.math.Box} b A second Box.
+ * @return {boolean} Whether the boxes intersect.
+ */
+goog.math.Box.intersects = function(a, b) {
+ return (
+ a.left <= b.right && b.left <= a.right && a.top <= b.bottom &&
+ b.top <= a.bottom);
+};
+
+
+/**
+ * Returns whether two boxes would intersect with additional padding.
+ *
+ * @param {goog.math.Box} a A Box.
+ * @param {goog.math.Box} b A second Box.
+ * @param {number} padding The additional padding.
+ * @return {boolean} Whether the boxes intersect.
+ */
+goog.math.Box.intersectsWithPadding = function(a, b, padding) {
+ return (
+ a.left <= b.right + padding && b.left <= a.right + padding &&
+ a.top <= b.bottom + padding && b.top <= a.bottom + padding);
+};
+
+
+/**
+ * Rounds the fields to the next larger integer values.
+ *
+ * @return {!goog.math.Box} This box with ceil'd fields.
+ */
+goog.math.Box.prototype.ceil = function() {
+ this.top = Math.ceil(this.top);
+ this.right = Math.ceil(this.right);
+ this.bottom = Math.ceil(this.bottom);
+ this.left = Math.ceil(this.left);
+ return this;
+};
+
+
+/**
+ * Rounds the fields to the next smaller integer values.
+ *
+ * @return {!goog.math.Box} This box with floored fields.
+ */
+goog.math.Box.prototype.floor = function() {
+ this.top = Math.floor(this.top);
+ this.right = Math.floor(this.right);
+ this.bottom = Math.floor(this.bottom);
+ this.left = Math.floor(this.left);
+ return this;
+};
+
+
+/**
+ * Rounds the fields to nearest integer values.
+ *
+ * @return {!goog.math.Box} This box with rounded fields.
+ */
+goog.math.Box.prototype.round = function() {
+ this.top = Math.round(this.top);
+ this.right = Math.round(this.right);
+ this.bottom = Math.round(this.bottom);
+ this.left = Math.round(this.left);
+ return this;
+};
+
+
+/**
+ * Translates this box by the given offsets. If a {@code goog.math.Coordinate}
+ * is given, then the left and right values are translated by the coordinate's
+ * x value and the top and bottom values are translated by the coordinate's y
+ * value. Otherwise, {@code tx} and {@code opt_ty} are used to translate the x
+ * and y dimension values.
+ *
+ * @param {number|goog.math.Coordinate} tx The value to translate the x
+ * dimension values by or the the coordinate to translate this box by.
+ * @param {number=} opt_ty The value to translate y dimension values by.
+ * @return {!goog.math.Box} This box after translating.
+ */
+goog.math.Box.prototype.translate = function(tx, opt_ty) {
+ if (tx instanceof goog.math.Coordinate) {
+ this.left += tx.x;
+ this.right += tx.x;
+ this.top += tx.y;
+ this.bottom += tx.y;
+ } else {
+ goog.asserts.assertNumber(tx);
+ this.left += tx;
+ this.right += tx;
+ if (goog.isNumber(opt_ty)) {
+ this.top += opt_ty;
+ this.bottom += opt_ty;
+ }
+ }
+ return this;
+};
+
+
+/**
+ * Scales this coordinate by the given scale factors. The x and y dimension
+ * values are scaled by {@code sx} and {@code opt_sy} respectively.
+ * If {@code opt_sy} is not given, then {@code sx} is used for both x and y.
+ *
+ * @param {number} sx The scale factor to use for the x dimension.
+ * @param {number=} opt_sy The scale factor to use for the y dimension.
+ * @return {!goog.math.Box} This box after scaling.
+ */
+goog.math.Box.prototype.scale = function(sx, opt_sy) {
+ var sy = goog.isNumber(opt_sy) ? opt_sy : sx;
+ this.left *= sx;
+ this.right *= sx;
+ this.top *= sy;
+ this.bottom *= sy;
+ return this;
+};
diff --git a/chromium/third_party/ink/closure/math/coordinate.js b/chromium/third_party/ink/closure/math/coordinate.js
new file mode 100644
index 00000000000..6966451d9d0
--- /dev/null
+++ b/chromium/third_party/ink/closure/math/coordinate.js
@@ -0,0 +1,280 @@
+// Copyright 2006 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview A utility class for representing two-dimensional positions.
+ * @author pupius@google.com (Daniel Pupius)
+ */
+
+
+goog.provide('goog.math.Coordinate');
+
+goog.require('goog.math');
+
+
+
+/**
+ * Class for representing coordinates and positions.
+ * @param {number=} opt_x Left, defaults to 0.
+ * @param {number=} opt_y Top, defaults to 0.
+ * @struct
+ * @constructor
+ */
+goog.math.Coordinate = function(opt_x, opt_y) {
+ /**
+ * X-value
+ * @type {number}
+ */
+ this.x = goog.isDef(opt_x) ? opt_x : 0;
+
+ /**
+ * Y-value
+ * @type {number}
+ */
+ this.y = goog.isDef(opt_y) ? opt_y : 0;
+};
+
+
+/**
+ * Returns a new copy of the coordinate.
+ * @return {!goog.math.Coordinate} A clone of this coordinate.
+ */
+goog.math.Coordinate.prototype.clone = function() {
+ return new goog.math.Coordinate(this.x, this.y);
+};
+
+
+if (goog.DEBUG) {
+ /**
+ * Returns a nice string representing the coordinate.
+ * @return {string} In the form (50, 73).
+ * @override
+ */
+ goog.math.Coordinate.prototype.toString = function() {
+ return '(' + this.x + ', ' + this.y + ')';
+ };
+}
+
+
+/**
+ * Returns whether the specified value is equal to this coordinate.
+ * @param {*} other Some other value.
+ * @return {boolean} Whether the specified value is equal to this coordinate.
+ */
+goog.math.Coordinate.prototype.equals = function(other) {
+ return other instanceof goog.math.Coordinate &&
+ goog.math.Coordinate.equals(this, other);
+};
+
+
+/**
+ * Compares coordinates for equality.
+ * @param {goog.math.Coordinate} a A Coordinate.
+ * @param {goog.math.Coordinate} b A Coordinate.
+ * @return {boolean} True iff the coordinates are equal, or if both are null.
+ */
+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;
+};
+
+
+/**
+ * Returns the distance between two coordinates.
+ * @param {!goog.math.Coordinate} a A Coordinate.
+ * @param {!goog.math.Coordinate} b A Coordinate.
+ * @return {number} The distance between {@code a} and {@code b}.
+ */
+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);
+};
+
+
+/**
+ * Returns the magnitude of a coordinate.
+ * @param {!goog.math.Coordinate} a A Coordinate.
+ * @return {number} The distance between the origin and {@code a}.
+ */
+goog.math.Coordinate.magnitude = function(a) {
+ return Math.sqrt(a.x * a.x + a.y * a.y);
+};
+
+
+/**
+ * Returns the angle from the origin to a coordinate.
+ * @param {!goog.math.Coordinate} a A Coordinate.
+ * @return {number} The angle, in degrees, clockwise from the positive X
+ * axis to {@code a}.
+ */
+goog.math.Coordinate.azimuth = function(a) {
+ return goog.math.angle(0, 0, a.x, a.y);
+};
+
+
+/**
+ * Returns the squared distance between two coordinates. Squared distances can
+ * be used for comparisons when the actual value is not required.
+ *
+ * Performance note: eliminating the square root is an optimization often used
+ * in lower-level languages, but the speed difference is not nearly as
+ * pronounced in JavaScript (only a few percent.)
+ *
+ * @param {!goog.math.Coordinate} a A Coordinate.
+ * @param {!goog.math.Coordinate} b A Coordinate.
+ * @return {number} The squared distance between {@code a} and {@code b}.
+ */
+goog.math.Coordinate.squaredDistance = function(a, b) {
+ var dx = a.x - b.x;
+ var dy = a.y - b.y;
+ return dx * dx + dy * dy;
+};
+
+
+/**
+ * Returns the difference between two coordinates as a new
+ * goog.math.Coordinate.
+ * @param {!goog.math.Coordinate} a A Coordinate.
+ * @param {!goog.math.Coordinate} b A Coordinate.
+ * @return {!goog.math.Coordinate} A Coordinate representing the difference
+ * between {@code a} and {@code b}.
+ */
+goog.math.Coordinate.difference = function(a, b) {
+ return new goog.math.Coordinate(a.x - b.x, a.y - b.y);
+};
+
+
+/**
+ * Returns the sum of two coordinates as a new goog.math.Coordinate.
+ * @param {!goog.math.Coordinate} a A Coordinate.
+ * @param {!goog.math.Coordinate} b A Coordinate.
+ * @return {!goog.math.Coordinate} A Coordinate representing the sum of the two
+ * coordinates.
+ */
+goog.math.Coordinate.sum = function(a, b) {
+ return new goog.math.Coordinate(a.x + b.x, a.y + b.y);
+};
+
+
+/**
+ * Rounds the x and y fields to the next larger integer values.
+ * @return {!goog.math.Coordinate} This coordinate with ceil'd fields.
+ */
+goog.math.Coordinate.prototype.ceil = function() {
+ this.x = Math.ceil(this.x);
+ this.y = Math.ceil(this.y);
+ return this;
+};
+
+
+/**
+ * Rounds the x and y fields to the next smaller integer values.
+ * @return {!goog.math.Coordinate} This coordinate with floored fields.
+ */
+goog.math.Coordinate.prototype.floor = function() {
+ this.x = Math.floor(this.x);
+ this.y = Math.floor(this.y);
+ return this;
+};
+
+
+/**
+ * Rounds the x and y fields to the nearest integer values.
+ * @return {!goog.math.Coordinate} This coordinate with rounded fields.
+ */
+goog.math.Coordinate.prototype.round = function() {
+ this.x = Math.round(this.x);
+ this.y = Math.round(this.y);
+ return this;
+};
+
+
+/**
+ * Translates this box by the given offsets. If a {@code goog.math.Coordinate}
+ * is given, then the x and y values are translated by the coordinate's x and y.
+ * Otherwise, x and y are translated by {@code tx} and {@code opt_ty}
+ * respectively.
+ * @param {number|goog.math.Coordinate} tx The value to translate x by or the
+ * the coordinate to translate this coordinate by.
+ * @param {number=} opt_ty The value to translate y by.
+ * @return {!goog.math.Coordinate} This coordinate after translating.
+ */
+goog.math.Coordinate.prototype.translate = function(tx, opt_ty) {
+ if (tx instanceof goog.math.Coordinate) {
+ this.x += tx.x;
+ this.y += tx.y;
+ } else {
+ this.x += Number(tx);
+ if (goog.isNumber(opt_ty)) {
+ this.y += opt_ty;
+ }
+ }
+ return this;
+};
+
+
+/**
+ * Scales this coordinate by the given scale factors. The x and y values are
+ * scaled by {@code sx} and {@code opt_sy} respectively. If {@code opt_sy}
+ * is not given, then {@code sx} is used for both x and y.
+ * @param {number} sx The scale factor to use for the x dimension.
+ * @param {number=} opt_sy The scale factor to use for the y dimension.
+ * @return {!goog.math.Coordinate} This coordinate after scaling.
+ */
+goog.math.Coordinate.prototype.scale = function(sx, opt_sy) {
+ var sy = goog.isNumber(opt_sy) ? opt_sy : sx;
+ this.x *= sx;
+ this.y *= sy;
+ return this;
+};
+
+
+/**
+ * Rotates this coordinate clockwise about the origin (or, optionally, the given
+ * center) by the given angle, in radians.
+ * @param {number} radians The angle by which to rotate this coordinate
+ * clockwise about the given center, in radians.
+ * @param {!goog.math.Coordinate=} opt_center The center of rotation. Defaults
+ * to (0, 0) if not given.
+ */
+goog.math.Coordinate.prototype.rotateRadians = function(radians, opt_center) {
+ var center = opt_center || new goog.math.Coordinate(0, 0);
+
+ var x = this.x;
+ var y = this.y;
+ var cos = Math.cos(radians);
+ var sin = Math.sin(radians);
+
+ this.x = (x - center.x) * cos - (y - center.y) * sin + center.x;
+ this.y = (x - center.x) * sin + (y - center.y) * cos + center.y;
+};
+
+
+/**
+ * Rotates this coordinate clockwise about the origin (or, optionally, the given
+ * center) by the given angle, in degrees.
+ * @param {number} degrees The angle by which to rotate this coordinate
+ * clockwise about the given center, in degrees.
+ * @param {!goog.math.Coordinate=} opt_center The center of rotation. Defaults
+ * to (0, 0) if not given.
+ */
+goog.math.Coordinate.prototype.rotateDegrees = function(degrees, opt_center) {
+ this.rotateRadians(goog.math.toRadians(degrees), opt_center);
+};
diff --git a/chromium/third_party/ink/closure/math/irect.js b/chromium/third_party/ink/closure/math/irect.js
new file mode 100644
index 00000000000..db7cee1d608
--- /dev/null
+++ b/chromium/third_party/ink/closure/math/irect.js
@@ -0,0 +1,45 @@
+// Copyright 2016 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview A record declaration to allow ClientRect and other rectangle
+ * like objects to be used with goog.math.Rect.
+ */
+
+goog.provide('goog.math.IRect');
+
+
+/**
+ * Record for representing rectangular regions, allows compatibility between
+ * things like ClientRect and goog.math.Rect.
+ *
+ * @record
+ */
+goog.math.IRect = function() {};
+
+
+/** @type {number} */
+goog.math.IRect.prototype.left;
+
+
+/** @type {number} */
+goog.math.IRect.prototype.top;
+
+
+/** @type {number} */
+goog.math.IRect.prototype.width;
+
+
+/** @type {number} */
+goog.math.IRect.prototype.height;
diff --git a/chromium/third_party/ink/closure/math/long.js b/chromium/third_party/ink/closure/math/long.js
new file mode 100644
index 00000000000..60ddef0787b
--- /dev/null
+++ b/chromium/third_party/ink/closure/math/long.js
@@ -0,0 +1,966 @@
+// Copyright 2009 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Defines a Long class for representing a 64-bit two's-complement
+ * integer value, which faithfully simulates the behavior of a Java "long". This
+ * implementation is derived from LongLib in GWT.
+ *
+ * @author kevinz@google.com (Kevin Zatloukal)
+ */
+
+goog.provide('goog.math.Long');
+
+goog.require('goog.asserts');
+goog.require('goog.reflect');
+
+
+
+/**
+ * Constructs a 64-bit two's-complement integer, given its low and high 32-bit
+ * values as *signed* integers. See the from* functions below for more
+ * convenient ways of constructing Longs.
+ *
+ * The internal representation of a long is the two given signed, 32-bit values.
+ * We use 32-bit pieces because these are the size of integers on which
+ * Javascript performs bit-operations. For operations like addition and
+ * multiplication, we split each number into 16-bit pieces, which can easily be
+ * multiplied within Javascript's floating-point representation without overflow
+ * or change in sign.
+ *
+ * In the algorithms below, we frequently reduce the negative case to the
+ * positive case by negating the input(s) and then post-processing the result.
+ * Note that we must ALWAYS check specially whether those values are MIN_VALUE
+ * (-2^63) because -MIN_VALUE == MIN_VALUE (since 2^63 cannot be represented as
+ * a positive number, it overflows back into a negative). Not handling this
+ * case would often result in infinite recursion.
+ *
+ * @param {number} low The low (signed) 32 bits of the long.
+ * @param {number} high The high (signed) 32 bits of the long.
+ * @struct
+ * @constructor
+ * @final
+ */
+goog.math.Long = function(low, high) {
+ /**
+ * @type {number}
+ * @private
+ */
+ this.low_ = low | 0; // force into 32 signed bits.
+
+ /**
+ * @type {number}
+ * @private
+ */
+ this.high_ = high | 0; // force into 32 signed bits.
+};
+
+
+// NOTE: Common constant values ZERO, ONE, NEG_ONE, etc. are defined below the
+// from* methods on which they depend.
+
+
+/**
+ * A cache of the Long representations of small integer values.
+ * @type {!Object<number, !goog.math.Long>}
+ * @private
+ */
+goog.math.Long.IntCache_ = {};
+
+
+/**
+ * A cache of the Long representations of common values.
+ * @type {!Object<goog.math.Long.ValueCacheId_, !goog.math.Long>}
+ * @private
+ */
+goog.math.Long.valueCache_ = {};
+
+/**
+ * Returns a cached long number representing the given (32-bit) integer value.
+ * @param {number} value The 32-bit integer in question.
+ * @return {!goog.math.Long} The corresponding Long value.
+ * @private
+ */
+goog.math.Long.getCachedIntValue_ = function(value) {
+ return goog.reflect.cache(goog.math.Long.IntCache_, value, function(val) {
+ return new goog.math.Long(val, val < 0 ? -1 : 0);
+ });
+};
+
+/**
+ * The array of maximum values of a Long in string representation for a given
+ * radix between 2 and 36, inclusive.
+ * @private @const {!Array<string>}
+ */
+goog.math.Long.MAX_VALUE_FOR_RADIX_ = [
+ '', '', // unused
+ '111111111111111111111111111111111111111111111111111111111111111',
+ // base 2
+ '2021110011022210012102010021220101220221', // base 3
+ '13333333333333333333333333333333', // base 4
+ '1104332401304422434310311212', // base 5
+ '1540241003031030222122211', // base 6
+ '22341010611245052052300', // base 7
+ '777777777777777777777', // base 8
+ '67404283172107811827', // base 9
+ '9223372036854775807', // base 10
+ '1728002635214590697', // base 11
+ '41a792678515120367', // base 12
+ '10b269549075433c37', // base 13
+ '4340724c6c71dc7a7', // base 14
+ '160e2ad3246366807', // base 15
+ '7fffffffffffffff', // base 16
+ '33d3d8307b214008', // base 17
+ '16agh595df825fa7', // base 18
+ 'ba643dci0ffeehh', // base 19
+ '5cbfjia3fh26ja7', // base 20
+ '2heiciiie82dh97', // base 21
+ '1adaibb21dckfa7', // base 22
+ 'i6k448cf4192c2', // base 23
+ 'acd772jnc9l0l7', // base 24
+ '64ie1focnn5g77', // base 25
+ '3igoecjbmca687', // base 26
+ '27c48l5b37oaop', // base 27
+ '1bk39f3ah3dmq7', // base 28
+ 'q1se8f0m04isb', // base 29
+ 'hajppbc1fc207', // base 30
+ 'bm03i95hia437', // base 31
+ '7vvvvvvvvvvvv', // base 32
+ '5hg4ck9jd4u37', // base 33
+ '3tdtk1v8j6tpp', // base 34
+ '2pijmikexrxp7', // base 35
+ '1y2p0ij32e8e7' // base 36
+];
+
+
+/**
+ * The array of minimum values of a Long in string representation for a given
+ * radix between 2 and 36, inclusive.
+ * @private @const {!Array<string>}
+ */
+goog.math.Long.MIN_VALUE_FOR_RADIX_ = [
+ '', '', // unused
+ '-1000000000000000000000000000000000000000000000000000000000000000',
+ // base 2
+ '-2021110011022210012102010021220101220222', // base 3
+ '-20000000000000000000000000000000', // base 4
+ '-1104332401304422434310311213', // base 5
+ '-1540241003031030222122212', // base 6
+ '-22341010611245052052301', // base 7
+ '-1000000000000000000000', // base 8
+ '-67404283172107811828', // base 9
+ '-9223372036854775808', // base 10
+ '-1728002635214590698', // base 11
+ '-41a792678515120368', // base 12
+ '-10b269549075433c38', // base 13
+ '-4340724c6c71dc7a8', // base 14
+ '-160e2ad3246366808', // base 15
+ '-8000000000000000', // base 16
+ '-33d3d8307b214009', // base 17
+ '-16agh595df825fa8', // base 18
+ '-ba643dci0ffeehi', // base 19
+ '-5cbfjia3fh26ja8', // base 20
+ '-2heiciiie82dh98', // base 21
+ '-1adaibb21dckfa8', // base 22
+ '-i6k448cf4192c3', // base 23
+ '-acd772jnc9l0l8', // base 24
+ '-64ie1focnn5g78', // base 25
+ '-3igoecjbmca688', // base 26
+ '-27c48l5b37oaoq', // base 27
+ '-1bk39f3ah3dmq8', // base 28
+ '-q1se8f0m04isc', // base 29
+ '-hajppbc1fc208', // base 30
+ '-bm03i95hia438', // base 31
+ '-8000000000000', // base 32
+ '-5hg4ck9jd4u38', // base 33
+ '-3tdtk1v8j6tpq', // base 34
+ '-2pijmikexrxp8', // base 35
+ '-1y2p0ij32e8e8' // base 36
+];
+
+
+/**
+ * Returns a Long representing the given (32-bit) integer value.
+ * @param {number} value The 32-bit integer in question.
+ * @return {!goog.math.Long} The corresponding Long value.
+ */
+goog.math.Long.fromInt = function(value) {
+ var intValue = value | 0;
+ goog.asserts.assert(value === intValue, 'value should be a 32-bit integer');
+
+ if (-128 <= intValue && intValue < 128) {
+ return goog.math.Long.getCachedIntValue_(intValue);
+ } else {
+ return new goog.math.Long(intValue, intValue < 0 ? -1 : 0);
+ }
+};
+
+
+/**
+ * Returns a Long representing the given value.
+ * NaN will be returned as zero. Infinity is converted to max value and
+ * -Infinity to min value.
+ * @param {number} value The number in question.
+ * @return {!goog.math.Long} The corresponding Long value.
+ */
+goog.math.Long.fromNumber = function(value) {
+ if (isNaN(value)) {
+ return goog.math.Long.getZero();
+ } else if (value <= -goog.math.Long.TWO_PWR_63_DBL_) {
+ return goog.math.Long.getMinValue();
+ } else if (value + 1 >= goog.math.Long.TWO_PWR_63_DBL_) {
+ return goog.math.Long.getMaxValue();
+ } else if (value < 0) {
+ return goog.math.Long.fromNumber(-value).negate();
+ } else {
+ return new goog.math.Long(
+ (value % goog.math.Long.TWO_PWR_32_DBL_) | 0,
+ (value / goog.math.Long.TWO_PWR_32_DBL_) | 0);
+ }
+};
+
+
+/**
+ * Returns a Long representing the 64-bit integer that comes by concatenating
+ * the given high and low bits. Each is assumed to use 32 bits.
+ * @param {number} lowBits The low 32-bits.
+ * @param {number} highBits The high 32-bits.
+ * @return {!goog.math.Long} The corresponding Long value.
+ */
+goog.math.Long.fromBits = function(lowBits, highBits) {
+ return new goog.math.Long(lowBits, highBits);
+};
+
+
+/**
+ * Returns a Long representation of the given string, written using the given
+ * radix.
+ * @param {string} str The textual representation of the Long.
+ * @param {number=} opt_radix The radix in which the text is written.
+ * @return {!goog.math.Long} The corresponding Long value.
+ */
+goog.math.Long.fromString = function(str, opt_radix) {
+ if (str.length == 0) {
+ throw new Error('number format error: empty string');
+ }
+
+ var radix = opt_radix || 10;
+ if (radix < 2 || 36 < radix) {
+ throw new Error('radix out of range: ' + radix);
+ }
+
+ if (str.charAt(0) == '-') {
+ return goog.math.Long.fromString(str.substring(1), radix).negate();
+ } else if (str.indexOf('-') >= 0) {
+ throw new Error('number format error: interior "-" character: ' + str);
+ }
+
+ // Do several (8) digits each time through the loop, so as to
+ // minimize the calls to the very expensive emulated div.
+ var radixToPower = goog.math.Long.fromNumber(Math.pow(radix, 8));
+
+ var result = goog.math.Long.getZero();
+ for (var i = 0; i < str.length; i += 8) {
+ var size = Math.min(8, str.length - i);
+ var value = parseInt(str.substring(i, i + size), radix);
+ if (size < 8) {
+ var power = goog.math.Long.fromNumber(Math.pow(radix, size));
+ result = result.multiply(power).add(goog.math.Long.fromNumber(value));
+ } else {
+ result = result.multiply(radixToPower);
+ result = result.add(goog.math.Long.fromNumber(value));
+ }
+ }
+ return result;
+};
+
+/**
+ * Returns the boolean value of whether the input string is within a Long's
+ * range. Assumes an input string containing only numeric characters with an
+ * optional preceding '-'.
+ * @param {string} str The textual representation of the Long.
+ * @param {number=} opt_radix The radix in which the text is written.
+ * @return {boolean} Whether the string is within the range of a Long.
+ */
+goog.math.Long.isStringInRange = function(str, opt_radix) {
+ var radix = opt_radix || 10;
+ if (radix < 2 || 36 < radix) {
+ throw new Error('radix out of range: ' + radix);
+ }
+
+ var extremeValue = (str.charAt(0) == '-') ?
+ goog.math.Long.MIN_VALUE_FOR_RADIX_[radix] :
+ goog.math.Long.MAX_VALUE_FOR_RADIX_[radix];
+
+ if (str.length < extremeValue.length) {
+ return true;
+ } else if (str.length == extremeValue.length && str <= extremeValue) {
+ return true;
+ } else {
+ return false;
+ }
+};
+
+// NOTE: the compiler should inline these constant values below and then remove
+// these variables, so there should be no runtime penalty for these.
+
+
+/**
+ * Number used repeated below in calculations. This must appear before the
+ * first call to any from* function below.
+ * @type {number}
+ * @private
+ */
+goog.math.Long.TWO_PWR_16_DBL_ = 1 << 16;
+
+
+/**
+ * @type {number}
+ * @private
+ */
+goog.math.Long.TWO_PWR_32_DBL_ =
+ goog.math.Long.TWO_PWR_16_DBL_ * goog.math.Long.TWO_PWR_16_DBL_;
+
+
+/**
+ * @type {number}
+ * @private
+ */
+goog.math.Long.TWO_PWR_64_DBL_ =
+ goog.math.Long.TWO_PWR_32_DBL_ * goog.math.Long.TWO_PWR_32_DBL_;
+
+
+/**
+ * @type {number}
+ * @private
+ */
+goog.math.Long.TWO_PWR_63_DBL_ = goog.math.Long.TWO_PWR_64_DBL_ / 2;
+
+
+/**
+ * @return {!goog.math.Long}
+ * @public
+ */
+goog.math.Long.getZero = function() {
+ return goog.math.Long.getCachedIntValue_(0);
+};
+
+
+/**
+ * @return {!goog.math.Long}
+ * @public
+ */
+goog.math.Long.getOne = function() {
+ return goog.math.Long.getCachedIntValue_(1);
+};
+
+
+/**
+ * @return {!goog.math.Long}
+ * @public
+ */
+goog.math.Long.getNegOne = function() {
+ return goog.math.Long.getCachedIntValue_(-1);
+};
+
+
+/**
+ * @return {!goog.math.Long}
+ * @public
+ */
+goog.math.Long.getMaxValue = function() {
+ return goog.reflect.cache(
+ goog.math.Long.valueCache_, goog.math.Long.ValueCacheId_.MAX_VALUE,
+ function() {
+ return goog.math.Long.fromBits(0xFFFFFFFF | 0, 0x7FFFFFFF | 0);
+ });
+};
+
+
+/**
+ * @return {!goog.math.Long}
+ * @public
+ */
+goog.math.Long.getMinValue = function() {
+ return goog.reflect.cache(
+ goog.math.Long.valueCache_, goog.math.Long.ValueCacheId_.MIN_VALUE,
+ function() { return goog.math.Long.fromBits(0, 0x80000000 | 0); });
+};
+
+
+/**
+ * @return {!goog.math.Long}
+ * @public
+ */
+goog.math.Long.getTwoPwr24 = function() {
+ return goog.reflect.cache(
+ goog.math.Long.valueCache_, goog.math.Long.ValueCacheId_.TWO_PWR_24,
+ function() { return goog.math.Long.fromInt(1 << 24); });
+};
+
+
+/** @return {number} The value, assuming it is a 32-bit integer. */
+goog.math.Long.prototype.toInt = function() {
+ return this.low_;
+};
+
+
+/** @return {number} The closest floating-point representation to this value. */
+goog.math.Long.prototype.toNumber = function() {
+ return this.high_ * goog.math.Long.TWO_PWR_32_DBL_ +
+ this.getLowBitsUnsigned();
+};
+
+
+/**
+ * @param {number=} opt_radix The radix in which the text should be written.
+ * @return {string} The textual representation of this value.
+ * @override
+ */
+goog.math.Long.prototype.toString = function(opt_radix) {
+ var radix = opt_radix || 10;
+ if (radix < 2 || 36 < radix) {
+ throw new Error('radix out of range: ' + radix);
+ }
+
+ if (this.isZero()) {
+ return '0';
+ }
+
+ if (this.isNegative()) {
+ if (this.equals(goog.math.Long.getMinValue())) {
+ // We need to change the Long value before it can be negated, so we remove
+ // the bottom-most digit in this base and then recurse to do the rest.
+ var radixLong = goog.math.Long.fromNumber(radix);
+ var div = this.div(radixLong);
+ var rem = div.multiply(radixLong).subtract(this);
+ return div.toString(radix) + rem.toInt().toString(radix);
+ } else {
+ return '-' + this.negate().toString(radix);
+ }
+ }
+
+ // Do several (6) digits each time through the loop, so as to
+ // minimize the calls to the very expensive emulated div.
+ var radixToPower = goog.math.Long.fromNumber(Math.pow(radix, 6));
+
+ var rem = this;
+ var result = '';
+ while (true) {
+ var remDiv = rem.div(radixToPower);
+ // The right shifting fixes negative values in the case when
+ // intval >= 2^31; for more details see
+ // https://github.com/google/closure-library/pull/498
+ var intval = rem.subtract(remDiv.multiply(radixToPower)).toInt() >>> 0;
+ var digits = intval.toString(radix);
+
+ rem = remDiv;
+ if (rem.isZero()) {
+ return digits + result;
+ } else {
+ while (digits.length < 6) {
+ digits = '0' + digits;
+ }
+ result = '' + digits + result;
+ }
+ }
+};
+
+
+/** @return {number} The high 32-bits as a signed value. */
+goog.math.Long.prototype.getHighBits = function() {
+ return this.high_;
+};
+
+
+/** @return {number} The low 32-bits as a signed value. */
+goog.math.Long.prototype.getLowBits = function() {
+ return this.low_;
+};
+
+
+/** @return {number} The low 32-bits as an unsigned value. */
+goog.math.Long.prototype.getLowBitsUnsigned = function() {
+ return (this.low_ >= 0) ? this.low_ :
+ goog.math.Long.TWO_PWR_32_DBL_ + this.low_;
+};
+
+
+/**
+ * @return {number} Returns the number of bits needed to represent the absolute
+ * value of this Long.
+ */
+goog.math.Long.prototype.getNumBitsAbs = function() {
+ if (this.isNegative()) {
+ if (this.equals(goog.math.Long.getMinValue())) {
+ return 64;
+ } else {
+ return this.negate().getNumBitsAbs();
+ }
+ } else {
+ var val = this.high_ != 0 ? this.high_ : this.low_;
+ for (var bit = 31; bit > 0; bit--) {
+ if ((val & (1 << bit)) != 0) {
+ break;
+ }
+ }
+ return this.high_ != 0 ? bit + 33 : bit + 1;
+ }
+};
+
+
+/** @return {boolean} Whether this value is zero. */
+goog.math.Long.prototype.isZero = function() {
+ return this.high_ == 0 && this.low_ == 0;
+};
+
+
+/** @return {boolean} Whether this value is negative. */
+goog.math.Long.prototype.isNegative = function() {
+ return this.high_ < 0;
+};
+
+
+/** @return {boolean} Whether this value is odd. */
+goog.math.Long.prototype.isOdd = function() {
+ return (this.low_ & 1) == 1;
+};
+
+
+/**
+ * @param {goog.math.Long} other Long to compare against.
+ * @return {boolean} Whether this Long equals the other.
+ */
+goog.math.Long.prototype.equals = function(other) {
+ return (this.high_ == other.high_) && (this.low_ == other.low_);
+};
+
+
+/**
+ * @param {goog.math.Long} other Long to compare against.
+ * @return {boolean} Whether this Long does not equal the other.
+ */
+goog.math.Long.prototype.notEquals = function(other) {
+ return (this.high_ != other.high_) || (this.low_ != other.low_);
+};
+
+
+/**
+ * @param {goog.math.Long} other Long to compare against.
+ * @return {boolean} Whether this Long is less than the other.
+ */
+goog.math.Long.prototype.lessThan = function(other) {
+ return this.compare(other) < 0;
+};
+
+
+/**
+ * @param {goog.math.Long} other Long to compare against.
+ * @return {boolean} Whether this Long is less than or equal to the other.
+ */
+goog.math.Long.prototype.lessThanOrEqual = function(other) {
+ return this.compare(other) <= 0;
+};
+
+
+/**
+ * @param {goog.math.Long} other Long to compare against.
+ * @return {boolean} Whether this Long is greater than the other.
+ */
+goog.math.Long.prototype.greaterThan = function(other) {
+ return this.compare(other) > 0;
+};
+
+
+/**
+ * @param {goog.math.Long} other Long to compare against.
+ * @return {boolean} Whether this Long is greater than or equal to the other.
+ */
+goog.math.Long.prototype.greaterThanOrEqual = function(other) {
+ return this.compare(other) >= 0;
+};
+
+
+/**
+ * Compares this Long with the given one.
+ * @param {goog.math.Long} other Long to compare against.
+ * @return {number} 0 if they are the same, 1 if the this is greater, and -1
+ * if the given one is greater.
+ */
+goog.math.Long.prototype.compare = function(other) {
+ if (this.equals(other)) {
+ return 0;
+ }
+
+ var thisNeg = this.isNegative();
+ var otherNeg = other.isNegative();
+ if (thisNeg && !otherNeg) {
+ return -1;
+ }
+ if (!thisNeg && otherNeg) {
+ return 1;
+ }
+
+ // at this point, the signs are the same, so subtraction will not overflow
+ if (this.subtract(other).isNegative()) {
+ return -1;
+ } else {
+ return 1;
+ }
+};
+
+
+/** @return {!goog.math.Long} The negation of this value. */
+goog.math.Long.prototype.negate = function() {
+ if (this.equals(goog.math.Long.getMinValue())) {
+ return goog.math.Long.getMinValue();
+ } else {
+ return this.not().add(goog.math.Long.getOne());
+ }
+};
+
+
+/**
+ * Returns the sum of this and the given Long.
+ * @param {goog.math.Long} other Long to add to this one.
+ * @return {!goog.math.Long} The sum of this and the given Long.
+ */
+goog.math.Long.prototype.add = function(other) {
+ // Divide each number into 4 chunks of 16 bits, and then sum the chunks.
+
+ var a48 = this.high_ >>> 16;
+ var a32 = this.high_ & 0xFFFF;
+ var a16 = this.low_ >>> 16;
+ var a00 = this.low_ & 0xFFFF;
+
+ var b48 = other.high_ >>> 16;
+ var b32 = other.high_ & 0xFFFF;
+ var b16 = other.low_ >>> 16;
+ var b00 = other.low_ & 0xFFFF;
+
+ var c48 = 0, c32 = 0, c16 = 0, c00 = 0;
+ c00 += a00 + b00;
+ c16 += c00 >>> 16;
+ c00 &= 0xFFFF;
+ c16 += a16 + b16;
+ c32 += c16 >>> 16;
+ c16 &= 0xFFFF;
+ c32 += a32 + b32;
+ c48 += c32 >>> 16;
+ c32 &= 0xFFFF;
+ c48 += a48 + b48;
+ c48 &= 0xFFFF;
+ return goog.math.Long.fromBits((c16 << 16) | c00, (c48 << 16) | c32);
+};
+
+
+/**
+ * Returns the difference of this and the given Long.
+ * @param {goog.math.Long} other Long to subtract from this.
+ * @return {!goog.math.Long} The difference of this and the given Long.
+ */
+goog.math.Long.prototype.subtract = function(other) {
+ return this.add(other.negate());
+};
+
+
+/**
+ * Returns the product of this and the given long.
+ * @param {goog.math.Long} other Long to multiply with this.
+ * @return {!goog.math.Long} The product of this and the other.
+ */
+goog.math.Long.prototype.multiply = function(other) {
+ if (this.isZero()) {
+ return goog.math.Long.getZero();
+ } else if (other.isZero()) {
+ return goog.math.Long.getZero();
+ }
+
+ if (this.equals(goog.math.Long.getMinValue())) {
+ return other.isOdd() ? goog.math.Long.getMinValue() :
+ goog.math.Long.getZero();
+ } else if (other.equals(goog.math.Long.getMinValue())) {
+ return this.isOdd() ? goog.math.Long.getMinValue() :
+ goog.math.Long.getZero();
+ }
+
+ if (this.isNegative()) {
+ if (other.isNegative()) {
+ return this.negate().multiply(other.negate());
+ } else {
+ return this.negate().multiply(other).negate();
+ }
+ } else if (other.isNegative()) {
+ return this.multiply(other.negate()).negate();
+ }
+
+ // If both longs are small, use float multiplication
+ if (this.lessThan(goog.math.Long.getTwoPwr24()) &&
+ other.lessThan(goog.math.Long.getTwoPwr24())) {
+ return goog.math.Long.fromNumber(this.toNumber() * other.toNumber());
+ }
+
+ // Divide each long into 4 chunks of 16 bits, and then add up 4x4 products.
+ // We can skip products that would overflow.
+
+ var a48 = this.high_ >>> 16;
+ var a32 = this.high_ & 0xFFFF;
+ var a16 = this.low_ >>> 16;
+ var a00 = this.low_ & 0xFFFF;
+
+ var b48 = other.high_ >>> 16;
+ var b32 = other.high_ & 0xFFFF;
+ var b16 = other.low_ >>> 16;
+ var b00 = other.low_ & 0xFFFF;
+
+ var c48 = 0, c32 = 0, c16 = 0, c00 = 0;
+ c00 += a00 * b00;
+ c16 += c00 >>> 16;
+ c00 &= 0xFFFF;
+ c16 += a16 * b00;
+ c32 += c16 >>> 16;
+ c16 &= 0xFFFF;
+ c16 += a00 * b16;
+ c32 += c16 >>> 16;
+ c16 &= 0xFFFF;
+ c32 += a32 * b00;
+ c48 += c32 >>> 16;
+ c32 &= 0xFFFF;
+ c32 += a16 * b16;
+ c48 += c32 >>> 16;
+ c32 &= 0xFFFF;
+ c32 += a00 * b32;
+ c48 += c32 >>> 16;
+ c32 &= 0xFFFF;
+ c48 += a48 * b00 + a32 * b16 + a16 * b32 + a00 * b48;
+ c48 &= 0xFFFF;
+ return goog.math.Long.fromBits((c16 << 16) | c00, (c48 << 16) | c32);
+};
+
+
+/**
+ * Returns this Long divided by the given one.
+ * @param {goog.math.Long} other Long by which to divide.
+ * @return {!goog.math.Long} This Long divided by the given one.
+ */
+goog.math.Long.prototype.div = function(other) {
+ if (other.isZero()) {
+ throw new Error('division by zero');
+ } else if (this.isZero()) {
+ return goog.math.Long.getZero();
+ }
+
+ if (this.equals(goog.math.Long.getMinValue())) {
+ if (other.equals(goog.math.Long.getOne()) ||
+ other.equals(goog.math.Long.getNegOne())) {
+ return goog.math.Long.getMinValue(); // recall -MIN_VALUE == MIN_VALUE
+ } else if (other.equals(goog.math.Long.getMinValue())) {
+ return goog.math.Long.getOne();
+ } else {
+ // At this point, we have |other| >= 2, so |this/other| < |MIN_VALUE|.
+ var halfThis = this.shiftRight(1);
+ var approx = halfThis.div(other).shiftLeft(1);
+ if (approx.equals(goog.math.Long.getZero())) {
+ return other.isNegative() ? goog.math.Long.getOne() :
+ goog.math.Long.getNegOne();
+ } else {
+ var rem = this.subtract(other.multiply(approx));
+ var result = approx.add(rem.div(other));
+ return result;
+ }
+ }
+ } else if (other.equals(goog.math.Long.getMinValue())) {
+ return goog.math.Long.getZero();
+ }
+
+ if (this.isNegative()) {
+ if (other.isNegative()) {
+ return this.negate().div(other.negate());
+ } else {
+ return this.negate().div(other).negate();
+ }
+ } else if (other.isNegative()) {
+ return this.div(other.negate()).negate();
+ }
+
+ // Repeat the following until the remainder is less than other: find a
+ // floating-point that approximates remainder / other *from below*, add this
+ // into the result, and subtract it from the remainder. It is critical that
+ // the approximate value is less than or equal to the real value so that the
+ // remainder never becomes negative.
+ var res = goog.math.Long.getZero();
+ var rem = this;
+ while (rem.greaterThanOrEqual(other)) {
+ // Approximate the result of division. This may be a little greater or
+ // smaller than the actual value.
+ var approx = Math.max(1, Math.floor(rem.toNumber() / other.toNumber()));
+
+ // We will tweak the approximate result by changing it in the 48-th digit or
+ // the smallest non-fractional digit, whichever is larger.
+ var log2 = Math.ceil(Math.log(approx) / Math.LN2);
+ var delta = (log2 <= 48) ? 1 : Math.pow(2, log2 - 48);
+
+ // Decrease the approximation until it is smaller than the remainder. Note
+ // that if it is too large, the product overflows and is negative.
+ var approxRes = goog.math.Long.fromNumber(approx);
+ var approxRem = approxRes.multiply(other);
+ while (approxRem.isNegative() || approxRem.greaterThan(rem)) {
+ approx -= delta;
+ approxRes = goog.math.Long.fromNumber(approx);
+ approxRem = approxRes.multiply(other);
+ }
+
+ // We know the answer can't be zero... and actually, zero would cause
+ // infinite recursion since we would make no progress.
+ if (approxRes.isZero()) {
+ approxRes = goog.math.Long.getOne();
+ }
+
+ res = res.add(approxRes);
+ rem = rem.subtract(approxRem);
+ }
+ return res;
+};
+
+
+/**
+ * Returns this Long modulo the given one.
+ * @param {goog.math.Long} other Long by which to mod.
+ * @return {!goog.math.Long} This Long modulo the given one.
+ */
+goog.math.Long.prototype.modulo = function(other) {
+ return this.subtract(this.div(other).multiply(other));
+};
+
+
+/** @return {!goog.math.Long} The bitwise-NOT of this value. */
+goog.math.Long.prototype.not = function() {
+ return goog.math.Long.fromBits(~this.low_, ~this.high_);
+};
+
+
+/**
+ * Returns the bitwise-AND of this Long and the given one.
+ * @param {goog.math.Long} other The Long with which to AND.
+ * @return {!goog.math.Long} The bitwise-AND of this and the other.
+ */
+goog.math.Long.prototype.and = function(other) {
+ return goog.math.Long.fromBits(
+ this.low_ & other.low_, this.high_ & other.high_);
+};
+
+
+/**
+ * Returns the bitwise-OR of this Long and the given one.
+ * @param {goog.math.Long} other The Long with which to OR.
+ * @return {!goog.math.Long} The bitwise-OR of this and the other.
+ */
+goog.math.Long.prototype.or = function(other) {
+ return goog.math.Long.fromBits(
+ this.low_ | other.low_, this.high_ | other.high_);
+};
+
+
+/**
+ * Returns the bitwise-XOR of this Long and the given one.
+ * @param {goog.math.Long} other The Long with which to XOR.
+ * @return {!goog.math.Long} The bitwise-XOR of this and the other.
+ */
+goog.math.Long.prototype.xor = function(other) {
+ return goog.math.Long.fromBits(
+ this.low_ ^ other.low_, this.high_ ^ other.high_);
+};
+
+
+/**
+ * Returns this Long with bits shifted to the left by the given amount.
+ * @param {number} numBits The number of bits by which to shift.
+ * @return {!goog.math.Long} This shifted to the left by the given amount.
+ */
+goog.math.Long.prototype.shiftLeft = function(numBits) {
+ numBits &= 63;
+ if (numBits == 0) {
+ return this;
+ } else {
+ var low = this.low_;
+ if (numBits < 32) {
+ var high = this.high_;
+ return goog.math.Long.fromBits(
+ low << numBits, (high << numBits) | (low >>> (32 - numBits)));
+ } else {
+ return goog.math.Long.fromBits(0, low << (numBits - 32));
+ }
+ }
+};
+
+
+/**
+ * Returns this Long with bits shifted to the right by the given amount.
+ * The new leading bits match the current sign bit.
+ * @param {number} numBits The number of bits by which to shift.
+ * @return {!goog.math.Long} This shifted to the right by the given amount.
+ */
+goog.math.Long.prototype.shiftRight = function(numBits) {
+ numBits &= 63;
+ if (numBits == 0) {
+ return this;
+ } else {
+ var high = this.high_;
+ if (numBits < 32) {
+ var low = this.low_;
+ return goog.math.Long.fromBits(
+ (low >>> numBits) | (high << (32 - numBits)), high >> numBits);
+ } else {
+ return goog.math.Long.fromBits(
+ high >> (numBits - 32), high >= 0 ? 0 : -1);
+ }
+ }
+};
+
+
+/**
+ * Returns this Long with bits shifted to the right by the given amount, with
+ * zeros placed into the new leading bits.
+ * @param {number} numBits The number of bits by which to shift.
+ * @return {!goog.math.Long} This shifted to the right by the given amount, with
+ * zeros placed into the new leading bits.
+ */
+goog.math.Long.prototype.shiftRightUnsigned = function(numBits) {
+ numBits &= 63;
+ if (numBits == 0) {
+ return this;
+ } else {
+ var high = this.high_;
+ if (numBits < 32) {
+ var low = this.low_;
+ return goog.math.Long.fromBits(
+ (low >>> numBits) | (high << (32 - numBits)), high >>> numBits);
+ } else if (numBits == 32) {
+ return goog.math.Long.fromBits(high, 0);
+ } else {
+ return goog.math.Long.fromBits(high >>> (numBits - 32), 0);
+ }
+ }
+};
+
+
+/**
+ * @enum {number} Ids of commonly requested Long instances.
+ * @private
+ */
+goog.math.Long.ValueCacheId_ = {
+ MAX_VALUE: 1,
+ MIN_VALUE: 2,
+ TWO_PWR_24: 6
+};
diff --git a/chromium/third_party/ink/closure/math/math.js b/chromium/third_party/ink/closure/math/math.js
new file mode 100644
index 00000000000..a251005b29a
--- /dev/null
+++ b/chromium/third_party/ink/closure/math/math.js
@@ -0,0 +1,449 @@
+// Copyright 2006 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Additional mathematical functions.
+ * @author pupius@google.com (Daniel Pupius)
+ */
+
+goog.provide('goog.math');
+
+goog.require('goog.array');
+goog.require('goog.asserts');
+
+
+/**
+ * Returns a random integer greater than or equal to 0 and less than {@code a}.
+ * @param {number} a The upper bound for the random integer (exclusive).
+ * @return {number} A random integer N such that 0 <= N < a.
+ */
+goog.math.randomInt = function(a) {
+ return Math.floor(Math.random() * a);
+};
+
+
+/**
+ * Returns a random number greater than or equal to {@code a} and less than
+ * {@code b}.
+ * @param {number} a The lower bound for the random number (inclusive).
+ * @param {number} b The upper bound for the random number (exclusive).
+ * @return {number} A random number N such that a <= N < b.
+ */
+goog.math.uniformRandom = function(a, b) {
+ return a + Math.random() * (b - a);
+};
+
+
+/**
+ * Takes a number and clamps it to within the provided bounds.
+ * @param {number} value The input number.
+ * @param {number} min The minimum value to return.
+ * @param {number} max The maximum value to return.
+ * @return {number} The input number if it is within bounds, or the nearest
+ * number within the bounds.
+ */
+goog.math.clamp = function(value, min, max) {
+ return Math.min(Math.max(value, min), max);
+};
+
+
+/**
+ * The % operator in JavaScript returns the remainder of a / b, but differs from
+ * some other languages in that the result will have the same sign as the
+ * dividend. For example, -1 % 8 == -1, whereas in some other languages
+ * (such as Python) the result would be 7. This function emulates the more
+ * correct modulo behavior, which is useful for certain applications such as
+ * calculating an offset index in a circular list.
+ *
+ * @param {number} a The dividend.
+ * @param {number} b The divisor.
+ * @return {number} a % b where the result is between 0 and b (either 0 <= x < b
+ * or b < x <= 0, depending on the sign of b).
+ */
+goog.math.modulo = function(a, b) {
+ var r = a % b;
+ // If r and b differ in sign, add b to wrap the result to the correct sign.
+ return (r * b < 0) ? r + b : r;
+};
+
+
+/**
+ * Performs linear interpolation between values a and b. Returns the value
+ * between a and b proportional to x (when x is between 0 and 1. When x is
+ * outside this range, the return value is a linear extrapolation).
+ * @param {number} a A number.
+ * @param {number} b A number.
+ * @param {number} x The proportion between a and b.
+ * @return {number} The interpolated value between a and b.
+ */
+goog.math.lerp = function(a, b, x) {
+ return a + x * (b - a);
+};
+
+
+/**
+ * Tests whether the two values are equal to each other, within a certain
+ * tolerance to adjust for floating point errors.
+ * @param {number} a A number.
+ * @param {number} b A number.
+ * @param {number=} opt_tolerance Optional tolerance range. Defaults
+ * to 0.000001. If specified, should be greater than 0.
+ * @return {boolean} Whether {@code a} and {@code b} are nearly equal.
+ */
+goog.math.nearlyEquals = function(a, b, opt_tolerance) {
+ return Math.abs(a - b) <= (opt_tolerance || 0.000001);
+};
+
+
+// TODO(jrajeshwar): Rename to normalizeAngle, retaining old name as deprecated
+// alias.
+/**
+ * Normalizes an angle to be in range [0-360). Angles outside this range will
+ * be normalized to be the equivalent angle with that range.
+ * @param {number} angle Angle in degrees.
+ * @return {number} Standardized angle.
+ */
+goog.math.standardAngle = function(angle) {
+ return goog.math.modulo(angle, 360);
+};
+
+
+/**
+ * Normalizes an angle to be in range [0-2*PI). Angles outside this range will
+ * be normalized to be the equivalent angle with that range.
+ * @param {number} angle Angle in radians.
+ * @return {number} Standardized angle.
+ */
+goog.math.standardAngleInRadians = function(angle) {
+ return goog.math.modulo(angle, 2 * Math.PI);
+};
+
+
+/**
+ * Converts degrees to radians.
+ * @param {number} angleDegrees Angle in degrees.
+ * @return {number} Angle in radians.
+ */
+goog.math.toRadians = function(angleDegrees) {
+ return angleDegrees * Math.PI / 180;
+};
+
+
+/**
+ * Converts radians to degrees.
+ * @param {number} angleRadians Angle in radians.
+ * @return {number} Angle in degrees.
+ */
+goog.math.toDegrees = function(angleRadians) {
+ return angleRadians * 180 / Math.PI;
+};
+
+
+/**
+ * For a given angle and radius, finds the X portion of the offset.
+ * @param {number} degrees Angle in degrees (zero points in +X direction).
+ * @param {number} radius Radius.
+ * @return {number} The x-distance for the angle and radius.
+ */
+goog.math.angleDx = function(degrees, radius) {
+ return radius * Math.cos(goog.math.toRadians(degrees));
+};
+
+
+/**
+ * For a given angle and radius, finds the Y portion of the offset.
+ * @param {number} degrees Angle in degrees (zero points in +X direction).
+ * @param {number} radius Radius.
+ * @return {number} The y-distance for the angle and radius.
+ */
+goog.math.angleDy = function(degrees, radius) {
+ return radius * Math.sin(goog.math.toRadians(degrees));
+};
+
+
+/**
+ * Computes the angle between two points (x1,y1) and (x2,y2).
+ * Angle zero points in the +X direction, 90 degrees points in the +Y
+ * direction (down) and from there we grow clockwise towards 360 degrees.
+ * @param {number} x1 x of first point.
+ * @param {number} y1 y of first point.
+ * @param {number} x2 x of second point.
+ * @param {number} y2 y of second point.
+ * @return {number} Standardized angle in degrees of the vector from
+ * x1,y1 to x2,y2.
+ */
+goog.math.angle = function(x1, y1, x2, y2) {
+ return goog.math.standardAngle(
+ goog.math.toDegrees(Math.atan2(y2 - y1, x2 - x1)));
+};
+
+
+/**
+ * Computes the difference between startAngle and endAngle (angles in degrees).
+ * @param {number} startAngle Start angle in degrees.
+ * @param {number} endAngle End angle in degrees.
+ * @return {number} The number of degrees that when added to
+ * startAngle will result in endAngle. Positive numbers mean that the
+ * direction is clockwise. Negative numbers indicate a counter-clockwise
+ * direction.
+ * The shortest route (clockwise vs counter-clockwise) between the angles
+ * is used.
+ * When the difference is 180 degrees, the function returns 180 (not -180)
+ * angleDifference(30, 40) is 10, and angleDifference(40, 30) is -10.
+ * angleDifference(350, 10) is 20, and angleDifference(10, 350) is -20.
+ */
+goog.math.angleDifference = function(startAngle, endAngle) {
+ var d =
+ goog.math.standardAngle(endAngle) - goog.math.standardAngle(startAngle);
+ if (d > 180) {
+ d = d - 360;
+ } else if (d <= -180) {
+ d = 360 + d;
+ }
+ return d;
+};
+
+
+/**
+ * Returns the sign of a number as per the "sign" or "signum" function.
+ * @param {number} x The number to take the sign of.
+ * @return {number} -1 when negative, 1 when positive, 0 when 0. Preserves
+ * signed zeros and NaN.
+ */
+goog.math.sign = function(x) {
+ if (x > 0) {
+ return 1;
+ }
+ if (x < 0) {
+ return -1;
+ }
+ return x; // Preserves signed zeros and NaN.
+};
+
+
+/**
+ * JavaScript implementation of Longest Common Subsequence problem.
+ * http://en.wikipedia.org/wiki/Longest_common_subsequence
+ *
+ * Returns the longest possible array that is subarray of both of given arrays.
+ *
+ * @param {IArrayLike<S>} array1 First array of objects.
+ * @param {IArrayLike<T>} array2 Second array of objects.
+ * @param {Function=} opt_compareFn Function that acts as a custom comparator
+ * for the array ojects. Function should return true if objects are equal,
+ * otherwise false.
+ * @param {Function=} opt_collectorFn Function used to decide what to return
+ * as a result subsequence. It accepts 2 arguments: index of common element
+ * in the first array and index in the second. The default function returns
+ * element from the first array.
+ * @return {!Array<S|T>} A list of objects that are common to both arrays
+ * such that there is no common subsequence with size greater than the
+ * length of the list.
+ * @template S,T
+ */
+goog.math.longestCommonSubsequence = function(
+ array1, array2, opt_compareFn, opt_collectorFn) {
+
+ var compare = opt_compareFn || function(a, b) { return a == b; };
+
+ var collect = opt_collectorFn || function(i1, i2) { return array1[i1]; };
+
+ var length1 = array1.length;
+ var length2 = array2.length;
+
+ var arr = [];
+ for (var i = 0; i < length1 + 1; i++) {
+ arr[i] = [];
+ arr[i][0] = 0;
+ }
+
+ for (var j = 0; j < length2 + 1; j++) {
+ arr[0][j] = 0;
+ }
+
+ for (i = 1; i <= length1; i++) {
+ for (j = 1; j <= length2; j++) {
+ if (compare(array1[i - 1], array2[j - 1])) {
+ arr[i][j] = arr[i - 1][j - 1] + 1;
+ } else {
+ arr[i][j] = Math.max(arr[i - 1][j], arr[i][j - 1]);
+ }
+ }
+ }
+
+ // Backtracking
+ var result = [];
+ var i = length1, j = length2;
+ while (i > 0 && j > 0) {
+ if (compare(array1[i - 1], array2[j - 1])) {
+ result.unshift(collect(i - 1, j - 1));
+ i--;
+ j--;
+ } else {
+ if (arr[i - 1][j] > arr[i][j - 1]) {
+ i--;
+ } else {
+ j--;
+ }
+ }
+ }
+
+ return result;
+};
+
+
+/**
+ * Returns the sum of the arguments.
+ * @param {...number} var_args Numbers to add.
+ * @return {number} The sum of the arguments (0 if no arguments were provided,
+ * {@code NaN} if any of the arguments is not a valid number).
+ */
+goog.math.sum = function(var_args) {
+ return /** @type {number} */ (
+ goog.array.reduce(
+ arguments, function(sum, value) { return sum + value; }, 0));
+};
+
+
+/**
+ * Returns the arithmetic mean of the arguments.
+ * @param {...number} var_args Numbers to average.
+ * @return {number} The average of the arguments ({@code NaN} if no arguments
+ * were provided or any of the arguments is not a valid number).
+ */
+goog.math.average = function(var_args) {
+ return goog.math.sum.apply(null, arguments) / arguments.length;
+};
+
+
+/**
+ * Returns the unbiased sample variance of the arguments. For a definition,
+ * see e.g. http://en.wikipedia.org/wiki/Variance
+ * @param {...number} var_args Number samples to analyze.
+ * @return {number} The unbiased sample variance of the arguments (0 if fewer
+ * than two samples were provided, or {@code NaN} if any of the samples is
+ * not a valid number).
+ */
+goog.math.sampleVariance = function(var_args) {
+ var sampleSize = arguments.length;
+ if (sampleSize < 2) {
+ return 0;
+ }
+
+ var mean = goog.math.average.apply(null, arguments);
+ var variance =
+ goog.math.sum.apply(null, goog.array.map(arguments, function(val) {
+ return Math.pow(val - mean, 2);
+ })) / (sampleSize - 1);
+
+ return variance;
+};
+
+
+/**
+ * Returns the sample standard deviation of the arguments. For a definition of
+ * sample standard deviation, see e.g.
+ * http://en.wikipedia.org/wiki/Standard_deviation
+ * @param {...number} var_args Number samples to analyze.
+ * @return {number} The sample standard deviation of the arguments (0 if fewer
+ * than two samples were provided, or {@code NaN} if any of the samples is
+ * not a valid number).
+ */
+goog.math.standardDeviation = function(var_args) {
+ return Math.sqrt(goog.math.sampleVariance.apply(null, arguments));
+};
+
+
+/**
+ * Returns whether the supplied number represents an integer, i.e. that is has
+ * no fractional component. No range-checking is performed on the number.
+ * @param {number} num The number to test.
+ * @return {boolean} Whether {@code num} is an integer.
+ */
+goog.math.isInt = function(num) {
+ return isFinite(num) && num % 1 == 0;
+};
+
+
+/**
+ * Returns whether the supplied number is finite and not NaN.
+ * @param {number} num The number to test.
+ * @return {boolean} Whether {@code num} is a finite number.
+ * @deprecated Use {@link isFinite} instead.
+ */
+goog.math.isFiniteNumber = function(num) {
+ return isFinite(num);
+};
+
+
+/**
+ * @param {number} num The number to test.
+ * @return {boolean} Whether it is negative zero.
+ */
+goog.math.isNegativeZero = function(num) {
+ return num == 0 && 1 / num < 0;
+};
+
+
+/**
+ * Returns the precise value of floor(log10(num)).
+ * Simpler implementations didn't work because of floating point rounding
+ * errors. For example
+ * <ul>
+ * <li>Math.floor(Math.log(num) / Math.LN10) is off by one for num == 1e+3.
+ * <li>Math.floor(Math.log(num) * Math.LOG10E) is off by one for num == 1e+15.
+ * <li>Math.floor(Math.log10(num)) is off by one for num == 1e+15 - 1.
+ * </ul>
+ * @param {number} num A floating point number.
+ * @return {number} Its logarithm to base 10 rounded down to the nearest
+ * integer if num > 0. -Infinity if num == 0. NaN if num < 0.
+ */
+goog.math.log10Floor = function(num) {
+ if (num > 0) {
+ var x = Math.round(Math.log(num) * Math.LOG10E);
+ return x - (parseFloat('1e' + x) > num ? 1 : 0);
+ }
+ return num == 0 ? -Infinity : NaN;
+};
+
+
+/**
+ * A tweaked variant of {@code Math.floor} which tolerates if the passed number
+ * is infinitesimally smaller than the closest integer. It often happens with
+ * the results of floating point calculations because of the finite precision
+ * of the intermediate results. For example {@code Math.floor(Math.log(1000) /
+ * Math.LN10) == 2}, not 3 as one would expect.
+ * @param {number} num A number.
+ * @param {number=} opt_epsilon An infinitesimally small positive number, the
+ * rounding error to tolerate.
+ * @return {number} The largest integer less than or equal to {@code num}.
+ */
+goog.math.safeFloor = function(num, opt_epsilon) {
+ goog.asserts.assert(!goog.isDef(opt_epsilon) || opt_epsilon > 0);
+ return Math.floor(num + (opt_epsilon || 2e-15));
+};
+
+
+/**
+ * A tweaked variant of {@code Math.ceil}. See {@code goog.math.safeFloor} for
+ * details.
+ * @param {number} num A number.
+ * @param {number=} opt_epsilon An infinitesimally small positive number, the
+ * rounding error to tolerate.
+ * @return {number} The smallest integer greater than or equal to {@code num}.
+ */
+goog.math.safeCeil = function(num, opt_epsilon) {
+ goog.asserts.assert(!goog.isDef(opt_epsilon) || opt_epsilon > 0);
+ return Math.ceil(num - (opt_epsilon || 2e-15));
+};
diff --git a/chromium/third_party/ink/closure/math/rect.js b/chromium/third_party/ink/closure/math/rect.js
new file mode 100644
index 00000000000..28bf840ed50
--- /dev/null
+++ b/chromium/third_party/ink/closure/math/rect.js
@@ -0,0 +1,478 @@
+// Copyright 2006 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview A utility class for representing rectangles. Some of these
+ * functions should be migrated over to non-nullable params.
+ * @author pupius@google.com (Daniel Pupius)
+ */
+
+goog.provide('goog.math.Rect');
+
+goog.require('goog.asserts');
+goog.require('goog.math.Box');
+goog.require('goog.math.Coordinate');
+goog.require('goog.math.IRect');
+goog.require('goog.math.Size');
+
+
+
+/**
+ * Class for representing rectangular regions.
+ * @param {number} x Left.
+ * @param {number} y Top.
+ * @param {number} w Width.
+ * @param {number} h Height.
+ * @struct
+ * @constructor
+ * @implements {goog.math.IRect}
+ */
+goog.math.Rect = function(x, y, w, h) {
+ /** @type {number} */
+ this.left = x;
+
+ /** @type {number} */
+ this.top = y;
+
+ /** @type {number} */
+ this.width = w;
+
+ /** @type {number} */
+ this.height = h;
+};
+
+
+/**
+ * @return {!goog.math.Rect} A new copy of this Rectangle.
+ */
+goog.math.Rect.prototype.clone = function() {
+ return new goog.math.Rect(this.left, this.top, this.width, this.height);
+};
+
+
+/**
+ * Returns a new Box object with the same position and dimensions as this
+ * rectangle.
+ * @return {!goog.math.Box} A new Box representation of this Rectangle.
+ */
+goog.math.Rect.prototype.toBox = function() {
+ var right = this.left + this.width;
+ var bottom = this.top + this.height;
+ return new goog.math.Box(this.top, right, bottom, this.left);
+};
+
+
+/**
+ * Creates a new Rect object with the position and size given.
+ * @param {!goog.math.Coordinate} position The top-left coordinate of the Rect
+ * @param {!goog.math.Size} size The size of the Rect
+ * @return {!goog.math.Rect} A new Rect initialized with the given position and
+ * size.
+ */
+goog.math.Rect.createFromPositionAndSize = function(position, size) {
+ return new goog.math.Rect(position.x, position.y, size.width, size.height);
+};
+
+
+/**
+ * Creates a new Rect object with the same position and dimensions as a given
+ * Box. Note that this is only the inverse of toBox if left/top are defined.
+ * @param {goog.math.Box} box A box.
+ * @return {!goog.math.Rect} A new Rect initialized with the box's position
+ * and size.
+ */
+goog.math.Rect.createFromBox = function(box) {
+ return new goog.math.Rect(
+ box.left, box.top, box.right - box.left, box.bottom - box.top);
+};
+
+
+if (goog.DEBUG) {
+ /**
+ * Returns a nice string representing size and dimensions of rectangle.
+ * @return {string} In the form (50, 73 - 75w x 25h).
+ * @override
+ */
+ goog.math.Rect.prototype.toString = function() {
+ return '(' + this.left + ', ' + this.top + ' - ' + this.width + 'w x ' +
+ this.height + 'h)';
+ };
+}
+
+
+/**
+ * Compares rectangles for equality.
+ * @param {goog.math.IRect} a A Rectangle.
+ * @param {goog.math.IRect} b A Rectangle.
+ * @return {boolean} True iff the rectangles have the same left, top, width,
+ * and height, or if both are null.
+ */
+goog.math.Rect.equals = function(a, b) {
+ if (a == b) {
+ return true;
+ }
+ if (!a || !b) {
+ return false;
+ }
+ return a.left == b.left && a.width == b.width && a.top == b.top &&
+ a.height == b.height;
+};
+
+
+/**
+ * Computes the intersection of this rectangle and the rectangle parameter. If
+ * there is no intersection, returns false and leaves this rectangle as is.
+ * @param {goog.math.IRect} rect A Rectangle.
+ * @return {boolean} True iff this rectangle intersects with the parameter.
+ */
+goog.math.Rect.prototype.intersection = function(rect) {
+ var x0 = Math.max(this.left, rect.left);
+ var x1 = Math.min(this.left + this.width, rect.left + rect.width);
+
+ if (x0 <= x1) {
+ var y0 = Math.max(this.top, rect.top);
+ var y1 = Math.min(this.top + this.height, rect.top + rect.height);
+
+ if (y0 <= y1) {
+ this.left = x0;
+ this.top = y0;
+ this.width = x1 - x0;
+ this.height = y1 - y0;
+
+ return true;
+ }
+ }
+ return false;
+};
+
+
+/**
+ * Returns the intersection of two rectangles. Two rectangles intersect if they
+ * touch at all, for example, two zero width and height rectangles would
+ * intersect if they had the same top and left.
+ * @param {goog.math.IRect} a A Rectangle.
+ * @param {goog.math.IRect} b A Rectangle.
+ * @return {goog.math.Rect} A new intersection rect (even if width and height
+ * are 0), or null if there is no intersection.
+ */
+goog.math.Rect.intersection = function(a, b) {
+ // There is no nice way to do intersection via a clone, because any such
+ // clone might be unnecessary if this function returns null. So, we duplicate
+ // code from above.
+
+ var x0 = Math.max(a.left, b.left);
+ var x1 = Math.min(a.left + a.width, b.left + b.width);
+
+ if (x0 <= x1) {
+ var y0 = Math.max(a.top, b.top);
+ var y1 = Math.min(a.top + a.height, b.top + b.height);
+
+ if (y0 <= y1) {
+ return new goog.math.Rect(x0, y0, x1 - x0, y1 - y0);
+ }
+ }
+ return null;
+};
+
+
+/**
+ * Returns whether two rectangles intersect. Two rectangles intersect if they
+ * touch at all, for example, two zero width and height rectangles would
+ * intersect if they had the same top and left.
+ * @param {goog.math.IRect} a A Rectangle.
+ * @param {goog.math.IRect} b A Rectangle.
+ * @return {boolean} Whether a and b intersect.
+ */
+goog.math.Rect.intersects = function(a, b) {
+ return (
+ a.left <= b.left + b.width && b.left <= a.left + a.width &&
+ a.top <= b.top + b.height && b.top <= a.top + a.height);
+};
+
+
+/**
+ * Returns whether a rectangle intersects this rectangle.
+ * @param {goog.math.IRect} rect A rectangle.
+ * @return {boolean} Whether rect intersects this rectangle.
+ */
+goog.math.Rect.prototype.intersects = function(rect) {
+ return goog.math.Rect.intersects(this, rect);
+};
+
+
+/**
+ * Computes the difference regions between two rectangles. The return value is
+ * an array of 0 to 4 rectangles defining the remaining regions of the first
+ * rectangle after the second has been subtracted.
+ * @param {goog.math.Rect} a A Rectangle.
+ * @param {goog.math.IRect} b A Rectangle.
+ * @return {!Array<!goog.math.Rect>} An array with 0 to 4 rectangles which
+ * together define the difference area of rectangle a minus rectangle b.
+ */
+goog.math.Rect.difference = function(a, b) {
+ var intersection = goog.math.Rect.intersection(a, b);
+ if (!intersection || !intersection.height || !intersection.width) {
+ return [a.clone()];
+ }
+
+ var result = [];
+
+ var top = a.top;
+ var height = a.height;
+
+ var ar = a.left + a.width;
+ var ab = a.top + a.height;
+
+ var br = b.left + b.width;
+ var bb = b.top + b.height;
+
+ // Subtract off any area on top where A extends past B
+ if (b.top > a.top) {
+ result.push(new goog.math.Rect(a.left, a.top, a.width, b.top - a.top));
+ top = b.top;
+ // If we're moving the top down, we also need to subtract the height diff.
+ height -= b.top - a.top;
+ }
+ // Subtract off any area on bottom where A extends past B
+ if (bb < ab) {
+ result.push(new goog.math.Rect(a.left, bb, a.width, ab - bb));
+ height = bb - top;
+ }
+ // Subtract any area on left where A extends past B
+ if (b.left > a.left) {
+ result.push(new goog.math.Rect(a.left, top, b.left - a.left, height));
+ }
+ // Subtract any area on right where A extends past B
+ if (br < ar) {
+ result.push(new goog.math.Rect(br, top, ar - br, height));
+ }
+
+ return result;
+};
+
+
+/**
+ * Computes the difference regions between this rectangle and {@code rect}. The
+ * return value is an array of 0 to 4 rectangles defining the remaining regions
+ * of this rectangle after the other has been subtracted.
+ * @param {goog.math.IRect} rect A Rectangle.
+ * @return {!Array<!goog.math.Rect>} An array with 0 to 4 rectangles which
+ * together define the difference area of rectangle a minus rectangle b.
+ */
+goog.math.Rect.prototype.difference = function(rect) {
+ return goog.math.Rect.difference(this, rect);
+};
+
+
+/**
+ * Expand this rectangle to also include the area of the given rectangle.
+ * @param {goog.math.IRect} rect The other rectangle.
+ */
+goog.math.Rect.prototype.boundingRect = function(rect) {
+ // We compute right and bottom before we change left and top below.
+ var right = Math.max(this.left + this.width, rect.left + rect.width);
+ var bottom = Math.max(this.top + this.height, rect.top + rect.height);
+
+ this.left = Math.min(this.left, rect.left);
+ this.top = Math.min(this.top, rect.top);
+
+ this.width = right - this.left;
+ this.height = bottom - this.top;
+};
+
+
+/**
+ * Returns a new rectangle which completely contains both input rectangles.
+ * @param {goog.math.IRect} a A rectangle.
+ * @param {goog.math.IRect} b A rectangle.
+ * @return {goog.math.Rect} A new bounding rect, or null if either rect is
+ * null.
+ */
+goog.math.Rect.boundingRect = function(a, b) {
+ if (!a || !b) {
+ return null;
+ }
+
+ var newRect = new goog.math.Rect(a.left, a.top, a.width, a.height);
+ newRect.boundingRect(b);
+
+ return newRect;
+};
+
+
+/**
+ * Tests whether this rectangle entirely contains another rectangle or
+ * coordinate.
+ *
+ * @param {goog.math.IRect|goog.math.Coordinate} another The rectangle or
+ * coordinate to test for containment.
+ * @return {boolean} Whether this rectangle contains given rectangle or
+ * coordinate.
+ */
+goog.math.Rect.prototype.contains = function(another) {
+ if (another instanceof goog.math.Coordinate) {
+ return another.x >= this.left && another.x <= this.left + this.width &&
+ another.y >= this.top && another.y <= this.top + this.height;
+ } else { // (another instanceof goog.math.IRect)
+ return this.left <= another.left &&
+ this.left + this.width >= another.left + another.width &&
+ this.top <= another.top &&
+ this.top + this.height >= another.top + another.height;
+ }
+};
+
+
+/**
+ * @param {!goog.math.Coordinate} point A coordinate.
+ * @return {number} The squared distance between the point and the closest
+ * point inside the rectangle. Returns 0 if the point is inside the
+ * rectangle.
+ */
+goog.math.Rect.prototype.squaredDistance = function(point) {
+ var dx = point.x < this.left ?
+ this.left - point.x :
+ Math.max(point.x - (this.left + this.width), 0);
+ var dy = point.y < this.top ? this.top - point.y :
+ Math.max(point.y - (this.top + this.height), 0);
+ return dx * dx + dy * dy;
+};
+
+
+/**
+ * @param {!goog.math.Coordinate} point A coordinate.
+ * @return {number} The distance between the point and the closest point
+ * inside the rectangle. Returns 0 if the point is inside the rectangle.
+ */
+goog.math.Rect.prototype.distance = function(point) {
+ return Math.sqrt(this.squaredDistance(point));
+};
+
+
+/**
+ * @return {!goog.math.Size} The size of this rectangle.
+ */
+goog.math.Rect.prototype.getSize = function() {
+ return new goog.math.Size(this.width, this.height);
+};
+
+
+/**
+ * @return {!goog.math.Coordinate} A new coordinate for the top-left corner of
+ * the rectangle.
+ */
+goog.math.Rect.prototype.getTopLeft = function() {
+ return new goog.math.Coordinate(this.left, this.top);
+};
+
+
+/**
+ * @return {!goog.math.Coordinate} A new coordinate for the center of the
+ * rectangle.
+ */
+goog.math.Rect.prototype.getCenter = function() {
+ return new goog.math.Coordinate(
+ this.left + this.width / 2, this.top + this.height / 2);
+};
+
+
+/**
+ * @return {!goog.math.Coordinate} A new coordinate for the bottom-right corner
+ * of the rectangle.
+ */
+goog.math.Rect.prototype.getBottomRight = function() {
+ return new goog.math.Coordinate(
+ this.left + this.width, this.top + this.height);
+};
+
+
+/**
+ * Rounds the fields to the next larger integer values.
+ * @return {!goog.math.Rect} This rectangle with ceil'd fields.
+ */
+goog.math.Rect.prototype.ceil = function() {
+ this.left = Math.ceil(this.left);
+ this.top = Math.ceil(this.top);
+ this.width = Math.ceil(this.width);
+ this.height = Math.ceil(this.height);
+ return this;
+};
+
+
+/**
+ * Rounds the fields to the next smaller integer values.
+ * @return {!goog.math.Rect} This rectangle with floored fields.
+ */
+goog.math.Rect.prototype.floor = function() {
+ this.left = Math.floor(this.left);
+ this.top = Math.floor(this.top);
+ this.width = Math.floor(this.width);
+ this.height = Math.floor(this.height);
+ return this;
+};
+
+
+/**
+ * Rounds the fields to nearest integer values.
+ * @return {!goog.math.Rect} This rectangle with rounded fields.
+ */
+goog.math.Rect.prototype.round = function() {
+ this.left = Math.round(this.left);
+ this.top = Math.round(this.top);
+ this.width = Math.round(this.width);
+ this.height = Math.round(this.height);
+ return this;
+};
+
+
+/**
+ * Translates this rectangle by the given offsets. If a
+ * {@code goog.math.Coordinate} is given, then the left and top values are
+ * translated by the coordinate's x and y values. Otherwise, top and left are
+ * translated by {@code tx} and {@code opt_ty} respectively.
+ * @param {number|goog.math.Coordinate} tx The value to translate left by or the
+ * the coordinate to translate this rect by.
+ * @param {number=} opt_ty The value to translate top by.
+ * @return {!goog.math.Rect} This rectangle after translating.
+ */
+goog.math.Rect.prototype.translate = function(tx, opt_ty) {
+ if (tx instanceof goog.math.Coordinate) {
+ this.left += tx.x;
+ this.top += tx.y;
+ } else {
+ this.left += goog.asserts.assertNumber(tx);
+ if (goog.isNumber(opt_ty)) {
+ this.top += opt_ty;
+ }
+ }
+ return this;
+};
+
+
+/**
+ * Scales this rectangle by the given scale factors. The left and width values
+ * are scaled by {@code sx} and the top and height values are scaled by
+ * {@code opt_sy}. If {@code opt_sy} is not given, then all fields are scaled
+ * by {@code sx}.
+ * @param {number} sx The scale factor to use for the x dimension.
+ * @param {number=} opt_sy The scale factor to use for the y dimension.
+ * @return {!goog.math.Rect} This rectangle after scaling.
+ */
+goog.math.Rect.prototype.scale = function(sx, opt_sy) {
+ var sy = goog.isNumber(opt_sy) ? opt_sy : sx;
+ this.left *= sx;
+ this.width *= sx;
+ this.top *= sy;
+ this.height *= sy;
+ return this;
+};
diff --git a/chromium/third_party/ink/closure/math/size.js b/chromium/third_party/ink/closure/math/size.js
new file mode 100644
index 00000000000..b539d0c8a07
--- /dev/null
+++ b/chromium/third_party/ink/closure/math/size.js
@@ -0,0 +1,228 @@
+// Copyright 2007 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview A utility class for representing two-dimensional sizes.
+ * @author pupius@google.com (Dan Pupius)
+ * @author brenneman@google.com (Shawn Brenneman)
+ */
+
+
+goog.provide('goog.math.Size');
+
+
+
+/**
+ * Class for representing sizes consisting of a width and height. Undefined
+ * width and height support is deprecated and results in compiler warning.
+ * @param {number} width Width.
+ * @param {number} height Height.
+ * @struct
+ * @constructor
+ */
+goog.math.Size = function(width, height) {
+ /**
+ * Width
+ * @type {number}
+ */
+ this.width = width;
+
+ /**
+ * Height
+ * @type {number}
+ */
+ this.height = height;
+};
+
+
+/**
+ * Compares sizes for equality.
+ * @param {goog.math.Size} a A Size.
+ * @param {goog.math.Size} b A Size.
+ * @return {boolean} True iff the sizes have equal widths and equal
+ * heights, or if both are null.
+ */
+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;
+};
+
+
+/**
+ * @return {!goog.math.Size} A new copy of the Size.
+ */
+goog.math.Size.prototype.clone = function() {
+ return new goog.math.Size(this.width, this.height);
+};
+
+
+if (goog.DEBUG) {
+ /**
+ * Returns a nice string representing size.
+ * @return {string} In the form (50 x 73).
+ * @override
+ */
+ goog.math.Size.prototype.toString = function() {
+ return '(' + this.width + ' x ' + this.height + ')';
+ };
+}
+
+
+/**
+ * @return {number} The longer of the two dimensions in the size.
+ */
+goog.math.Size.prototype.getLongest = function() {
+ return Math.max(this.width, this.height);
+};
+
+
+/**
+ * @return {number} The shorter of the two dimensions in the size.
+ */
+goog.math.Size.prototype.getShortest = function() {
+ return Math.min(this.width, this.height);
+};
+
+
+/**
+ * @return {number} The area of the size (width * height).
+ */
+goog.math.Size.prototype.area = function() {
+ return this.width * this.height;
+};
+
+
+/**
+ * @return {number} The perimeter of the size (width + height) * 2.
+ */
+goog.math.Size.prototype.perimeter = function() {
+ return (this.width + this.height) * 2;
+};
+
+
+/**
+ * @return {number} The ratio of the size's width to its height.
+ */
+goog.math.Size.prototype.aspectRatio = function() {
+ return this.width / this.height;
+};
+
+
+/**
+ * @return {boolean} True if the size has zero area, false if both dimensions
+ * are non-zero numbers.
+ */
+goog.math.Size.prototype.isEmpty = function() {
+ return !this.area();
+};
+
+
+/**
+ * Clamps the width and height parameters upward to integer values.
+ * @return {!goog.math.Size} This size with ceil'd components.
+ */
+goog.math.Size.prototype.ceil = function() {
+ this.width = Math.ceil(this.width);
+ this.height = Math.ceil(this.height);
+ return this;
+};
+
+
+/**
+ * @param {!goog.math.Size} target The target size.
+ * @return {boolean} True if this Size is the same size or smaller than the
+ * target size in both dimensions.
+ */
+goog.math.Size.prototype.fitsInside = function(target) {
+ return this.width <= target.width && this.height <= target.height;
+};
+
+
+/**
+ * Clamps the width and height parameters downward to integer values.
+ * @return {!goog.math.Size} This size with floored components.
+ */
+goog.math.Size.prototype.floor = function() {
+ this.width = Math.floor(this.width);
+ this.height = Math.floor(this.height);
+ return this;
+};
+
+
+/**
+ * Rounds the width and height parameters to integer values.
+ * @return {!goog.math.Size} This size with rounded components.
+ */
+goog.math.Size.prototype.round = function() {
+ this.width = Math.round(this.width);
+ this.height = Math.round(this.height);
+ return this;
+};
+
+
+/**
+ * Scales this size by the given scale factors. The width and height are scaled
+ * by {@code sx} and {@code opt_sy} respectively. If {@code opt_sy} is not
+ * given, then {@code sx} is used for both the width and height.
+ * @param {number} sx The scale factor to use for the width.
+ * @param {number=} opt_sy The scale factor to use for the height.
+ * @return {!goog.math.Size} This Size object after scaling.
+ */
+goog.math.Size.prototype.scale = function(sx, opt_sy) {
+ var sy = goog.isNumber(opt_sy) ? opt_sy : sx;
+ this.width *= sx;
+ this.height *= sy;
+ return this;
+};
+
+
+/**
+ * Uniformly scales the size to perfectly cover the dimensions of a given size.
+ * If the size is already larger than the target, it will be scaled down to the
+ * minimum size at which it still covers the entire target. The original aspect
+ * ratio will be preserved.
+ *
+ * This function assumes that both Sizes contain strictly positive dimensions.
+ * @param {!goog.math.Size} target The target size.
+ * @return {!goog.math.Size} This Size object, after optional scaling.
+ */
+goog.math.Size.prototype.scaleToCover = function(target) {
+ var s = this.aspectRatio() <= target.aspectRatio() ?
+ target.width / this.width :
+ target.height / this.height;
+
+ return this.scale(s);
+};
+
+
+/**
+ * Uniformly scales the size to fit inside the dimensions of a given size. The
+ * original aspect ratio will be preserved.
+ *
+ * This function assumes that both Sizes contain strictly positive dimensions.
+ * @param {!goog.math.Size} target The target size.
+ * @return {!goog.math.Size} This Size object, after optional scaling.
+ */
+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);
+};
diff --git a/chromium/third_party/ink/closure/object/object.js b/chromium/third_party/ink/closure/object/object.js
new file mode 100644
index 00000000000..286d24e0e5d
--- /dev/null
+++ b/chromium/third_party/ink/closure/object/object.js
@@ -0,0 +1,751 @@
+// Copyright 2006 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Utilities for manipulating objects/maps/hashes.
+ * @author pupius@google.com (Daniel Pupius)
+ * @author arv@google.com (Erik Arvidsson)
+ * @author pallosp@google.com (Peter Pallos)
+ */
+
+goog.provide('goog.object');
+
+
+/**
+ * Whether two values are not observably distinguishable. This
+ * correctly detects that 0 is not the same as -0 and two NaNs are
+ * practically equivalent.
+ *
+ * The implementation is as suggested by harmony:egal proposal.
+ *
+ * @param {*} v The first value to compare.
+ * @param {*} v2 The second value to compare.
+ * @return {boolean} Whether two values are not observably distinguishable.
+ * @see http://wiki.ecmascript.org/doku.php?id=harmony:egal
+ */
+goog.object.is = function(v, v2) {
+ if (v === v2) {
+ // 0 === -0, but they are not identical.
+ // We need the cast because the compiler requires that v2 is a
+ // number (although 1/v2 works with non-number). We cast to ? to
+ // stop the compiler from type-checking this statement.
+ return v !== 0 || 1 / v === 1 / /** @type {?} */ (v2);
+ }
+
+ // NaN is non-reflexive: NaN !== NaN, although they are identical.
+ return v !== v && v2 !== v2;
+};
+
+
+/**
+ * Calls a function for each element in an object/map/hash.
+ *
+ * @param {Object<K,V>} obj The object over which to iterate.
+ * @param {function(this:T,V,?,Object<K,V>):?} f The function to call
+ * for every element. This function takes 3 arguments (the value, the
+ * key and the object) and the return value is ignored.
+ * @param {T=} opt_obj This is used as the 'this' object within f.
+ * @template T,K,V
+ */
+goog.object.forEach = function(obj, f, opt_obj) {
+ for (var key in obj) {
+ f.call(/** @type {?} */ (opt_obj), obj[key], key, obj);
+ }
+};
+
+
+/**
+ * Calls a function for each element in an object/map/hash. If that call returns
+ * true, adds the element to a new object.
+ *
+ * @param {Object<K,V>} obj The object over which to iterate.
+ * @param {function(this:T,V,?,Object<K,V>):boolean} f The function to call
+ * for every element. This
+ * function takes 3 arguments (the value, the key and the object)
+ * and should return a boolean. If the return value is true the
+ * element is added to the result object. If it is false the
+ * element is not included.
+ * @param {T=} opt_obj This is used as the 'this' object within f.
+ * @return {!Object<K,V>} a new object in which only elements that passed the
+ * test are present.
+ * @template T,K,V
+ */
+goog.object.filter = function(obj, f, opt_obj) {
+ var res = {};
+ for (var key in obj) {
+ if (f.call(/** @type {?} */ (opt_obj), obj[key], key, obj)) {
+ res[key] = obj[key];
+ }
+ }
+ return res;
+};
+
+
+/**
+ * For every element in an object/map/hash calls a function and inserts the
+ * result into a new object.
+ *
+ * @param {Object<K,V>} obj The object over which to iterate.
+ * @param {function(this:T,V,?,Object<K,V>):R} f The function to call
+ * for every element. This function
+ * takes 3 arguments (the value, the key and the object)
+ * and should return something. The result will be inserted
+ * into a new object.
+ * @param {T=} opt_obj This is used as the 'this' object within f.
+ * @return {!Object<K,R>} a new object with the results from f.
+ * @template T,K,V,R
+ */
+goog.object.map = function(obj, f, opt_obj) {
+ var res = {};
+ for (var key in obj) {
+ res[key] = f.call(/** @type {?} */ (opt_obj), obj[key], key, obj);
+ }
+ return res;
+};
+
+
+/**
+ * Calls a function for each element in an object/map/hash. If any
+ * call returns true, returns true (without checking the rest). If
+ * all calls return false, returns false.
+ *
+ * @param {Object<K,V>} obj The object to check.
+ * @param {function(this:T,V,?,Object<K,V>):boolean} f The function to
+ * call for every element. This function
+ * takes 3 arguments (the value, the key and the object) and should
+ * return a boolean.
+ * @param {T=} opt_obj This is used as the 'this' object within f.
+ * @return {boolean} true if any element passes the test.
+ * @template T,K,V
+ */
+goog.object.some = function(obj, f, opt_obj) {
+ for (var key in obj) {
+ if (f.call(/** @type {?} */ (opt_obj), obj[key], key, obj)) {
+ return true;
+ }
+ }
+ return false;
+};
+
+
+/**
+ * Calls a function for each element in an object/map/hash. If
+ * all calls return true, returns true. If any call returns false, returns
+ * false at this point and does not continue to check the remaining elements.
+ *
+ * @param {Object<K,V>} obj The object to check.
+ * @param {?function(this:T,V,?,Object<K,V>):boolean} f The function to
+ * call for every element. This function
+ * takes 3 arguments (the value, the key and the object) and should
+ * return a boolean.
+ * @param {T=} opt_obj This is used as the 'this' object within f.
+ * @return {boolean} false if any element fails the test.
+ * @template T,K,V
+ */
+goog.object.every = function(obj, f, opt_obj) {
+ for (var key in obj) {
+ if (!f.call(/** @type {?} */ (opt_obj), obj[key], key, obj)) {
+ return false;
+ }
+ }
+ return true;
+};
+
+
+/**
+ * Returns the number of key-value pairs in the object map.
+ *
+ * @param {Object} obj The object for which to get the number of key-value
+ * pairs.
+ * @return {number} The number of key-value pairs in the object map.
+ */
+goog.object.getCount = function(obj) {
+ var rv = 0;
+ for (var key in obj) {
+ rv++;
+ }
+ return rv;
+};
+
+
+/**
+ * Returns one key from the object map, if any exists.
+ * For map literals the returned key will be the first one in most of the
+ * browsers (a know exception is Konqueror).
+ *
+ * @param {Object} obj The object to pick a key from.
+ * @return {string|undefined} The key or undefined if the object is empty.
+ */
+goog.object.getAnyKey = function(obj) {
+ for (var key in obj) {
+ return key;
+ }
+};
+
+
+/**
+ * Returns one value from the object map, if any exists.
+ * For map literals the returned value will be the first one in most of the
+ * browsers (a know exception is Konqueror).
+ *
+ * @param {Object<K,V>} obj The object to pick a value from.
+ * @return {V|undefined} The value or undefined if the object is empty.
+ * @template K,V
+ */
+goog.object.getAnyValue = function(obj) {
+ for (var key in obj) {
+ return obj[key];
+ }
+};
+
+
+/**
+ * Whether the object/hash/map contains the given object as a value.
+ * An alias for goog.object.containsValue(obj, val).
+ *
+ * @param {Object<K,V>} obj The object in which to look for val.
+ * @param {V} val The object for which to check.
+ * @return {boolean} true if val is present.
+ * @template K,V
+ */
+goog.object.contains = function(obj, val) {
+ return goog.object.containsValue(obj, val);
+};
+
+
+/**
+ * Returns the values of the object/map/hash.
+ *
+ * @param {Object<K,V>} obj The object from which to get the values.
+ * @return {!Array<V>} The values in the object/map/hash.
+ * @template K,V
+ */
+goog.object.getValues = function(obj) {
+ var res = [];
+ var i = 0;
+ for (var key in obj) {
+ res[i++] = obj[key];
+ }
+ return res;
+};
+
+
+/**
+ * Returns the keys of the object/map/hash.
+ *
+ * @param {Object} obj The object from which to get the keys.
+ * @return {!Array<string>} Array of property keys.
+ */
+goog.object.getKeys = function(obj) {
+ var res = [];
+ var i = 0;
+ for (var key in obj) {
+ res[i++] = key;
+ }
+ return res;
+};
+
+
+/**
+ * Get a value from an object multiple levels deep. This is useful for
+ * pulling values from deeply nested objects, such as JSON responses.
+ * Example usage: getValueByKeys(jsonObj, 'foo', 'entries', 3)
+ *
+ * @param {!Object} obj An object to get the value from. Can be array-like.
+ * @param {...(string|number|!IArrayLike<number|string>)}
+ * var_args A number of keys
+ * (as strings, or numbers, for array-like objects). Can also be
+ * specified as a single array of keys.
+ * @return {*} The resulting value. If, at any point, the value for a key
+ * in the current object is null or undefined, returns undefined.
+ */
+goog.object.getValueByKeys = function(obj, var_args) {
+ var isArrayLike = goog.isArrayLike(var_args);
+ var keys = isArrayLike ? var_args : arguments;
+
+ // Start with the 2nd parameter for the variable parameters syntax.
+ for (var i = isArrayLike ? 0 : 1; i < keys.length; i++) {
+ if (obj == null) return undefined;
+ obj = obj[keys[i]];
+ }
+
+ return obj;
+};
+
+
+/**
+ * Whether the object/map/hash contains the given key.
+ *
+ * @param {Object} obj The object in which to look for key.
+ * @param {?} key The key for which to check.
+ * @return {boolean} true If the map contains the key.
+ */
+goog.object.containsKey = function(obj, key) {
+ return obj !== null && key in obj;
+};
+
+
+/**
+ * Whether the object/map/hash contains the given value. This is O(n).
+ *
+ * @param {Object<K,V>} obj The object in which to look for val.
+ * @param {V} val The value for which to check.
+ * @return {boolean} true If the map contains the value.
+ * @template K,V
+ */
+goog.object.containsValue = function(obj, val) {
+ for (var key in obj) {
+ if (obj[key] == val) {
+ return true;
+ }
+ }
+ return false;
+};
+
+
+/**
+ * Searches an object for an element that satisfies the given condition and
+ * returns its key.
+ * @param {Object<K,V>} obj The object to search in.
+ * @param {function(this:T,V,string,Object<K,V>):boolean} f The
+ * function to call for every element. Takes 3 arguments (the value,
+ * the key and the object) and should return a boolean.
+ * @param {T=} opt_this An optional "this" context for the function.
+ * @return {string|undefined} The key of an element for which the function
+ * returns true or undefined if no such element is found.
+ * @template T,K,V
+ */
+goog.object.findKey = function(obj, f, opt_this) {
+ for (var key in obj) {
+ if (f.call(/** @type {?} */ (opt_this), obj[key], key, obj)) {
+ return key;
+ }
+ }
+ return undefined;
+};
+
+
+/**
+ * Searches an object for an element that satisfies the given condition and
+ * returns its value.
+ * @param {Object<K,V>} obj The object to search in.
+ * @param {function(this:T,V,string,Object<K,V>):boolean} f The function
+ * to call for every element. Takes 3 arguments (the value, the key
+ * and the object) and should return a boolean.
+ * @param {T=} opt_this An optional "this" context for the function.
+ * @return {V} The value of an element for which the function returns true or
+ * undefined if no such element is found.
+ * @template T,K,V
+ */
+goog.object.findValue = function(obj, f, opt_this) {
+ var key = goog.object.findKey(obj, f, opt_this);
+ return key && obj[key];
+};
+
+
+/**
+ * Whether the object/map/hash is empty.
+ *
+ * @param {Object} obj The object to test.
+ * @return {boolean} true if obj is empty.
+ */
+goog.object.isEmpty = function(obj) {
+ for (var key in obj) {
+ return false;
+ }
+ return true;
+};
+
+
+/**
+ * Removes all key value pairs from the object/map/hash.
+ *
+ * @param {Object} obj The object to clear.
+ */
+goog.object.clear = function(obj) {
+ for (var i in obj) {
+ delete obj[i];
+ }
+};
+
+
+/**
+ * Removes a key-value pair based on the key.
+ *
+ * @param {Object} obj The object from which to remove the key.
+ * @param {?} key The key to remove.
+ * @return {boolean} Whether an element was removed.
+ */
+goog.object.remove = function(obj, key) {
+ var rv;
+ if (rv = key in /** @type {!Object} */ (obj)) {
+ delete obj[key];
+ }
+ return rv;
+};
+
+
+/**
+ * Adds a key-value pair to the object. Throws an exception if the key is
+ * already in use. Use set if you want to change an existing pair.
+ *
+ * @param {Object<K,V>} obj The object to which to add the key-value pair.
+ * @param {string} key The key to add.
+ * @param {V} val The value to add.
+ * @template K,V
+ */
+goog.object.add = function(obj, key, val) {
+ if (obj !== null && key in obj) {
+ throw new Error('The object already contains the key "' + key + '"');
+ }
+ goog.object.set(obj, key, val);
+};
+
+
+/**
+ * Returns the value for the given key.
+ *
+ * @param {Object<K,V>} obj The object from which to get the value.
+ * @param {string} key The key for which to get the value.
+ * @param {R=} opt_val The value to return if no item is found for the given
+ * key (default is undefined).
+ * @return {V|R|undefined} The value for the given key.
+ * @template K,V,R
+ */
+goog.object.get = function(obj, key, opt_val) {
+ if (obj !== null && key in obj) {
+ return obj[key];
+ }
+ return opt_val;
+};
+
+
+/**
+ * Adds a key-value pair to the object/map/hash.
+ *
+ * @param {Object<K,V>} obj The object to which to add the key-value pair.
+ * @param {string} key The key to add.
+ * @param {V} value The value to add.
+ * @template K,V
+ */
+goog.object.set = function(obj, key, value) {
+ obj[key] = value;
+};
+
+
+/**
+ * Adds a key-value pair to the object/map/hash if it doesn't exist yet.
+ *
+ * @param {Object<K,V>} obj The object to which to add the key-value pair.
+ * @param {string} key The key to add.
+ * @param {V} value The value to add if the key wasn't present.
+ * @return {V} The value of the entry at the end of the function.
+ * @template K,V
+ */
+goog.object.setIfUndefined = function(obj, key, value) {
+ return key in /** @type {!Object} */ (obj) ? obj[key] : (obj[key] = value);
+};
+
+
+/**
+ * Sets a key and value to an object if the key is not set. The value will be
+ * the return value of the given function. If the key already exists, the
+ * object will not be changed and the function will not be called (the function
+ * will be lazily evaluated -- only called if necessary).
+ *
+ * This function is particularly useful for use with a map used a as a cache.
+ *
+ * @param {!Object<K,V>} obj The object to which to add the key-value pair.
+ * @param {string} key The key to add.
+ * @param {function():V} f The value to add if the key wasn't present.
+ * @return {V} The value of the entry at the end of the function.
+ * @template K,V
+ */
+goog.object.setWithReturnValueIfNotSet = function(obj, key, f) {
+ if (key in obj) {
+ return obj[key];
+ }
+
+ var val = f();
+ obj[key] = val;
+ return val;
+};
+
+
+/**
+ * Compares two objects for equality using === on the values.
+ *
+ * @param {!Object<K,V>} a
+ * @param {!Object<K,V>} b
+ * @return {boolean}
+ * @template K,V
+ */
+goog.object.equals = function(a, b) {
+ for (var k in a) {
+ if (!(k in b) || a[k] !== b[k]) {
+ return false;
+ }
+ }
+ for (var k in b) {
+ if (!(k in a)) {
+ return false;
+ }
+ }
+ return true;
+};
+
+
+/**
+ * Returns a shallow clone of the object.
+ *
+ * @param {Object<K,V>} obj Object to clone.
+ * @return {!Object<K,V>} Clone of the input object.
+ * @template K,V
+ */
+goog.object.clone = function(obj) {
+ // We cannot use the prototype trick because a lot of methods depend on where
+ // the actual key is set.
+
+ var res = {};
+ for (var key in obj) {
+ res[key] = obj[key];
+ }
+ return res;
+ // We could also use goog.mixin but I wanted this to be independent from that.
+};
+
+
+/**
+ * Clones a value. The input may be an Object, Array, or basic type. Objects and
+ * arrays will be cloned recursively.
+ *
+ * WARNINGS:
+ * <code>goog.object.unsafeClone</code> does not detect reference loops. Objects
+ * that refer to themselves will cause infinite recursion.
+ *
+ * <code>goog.object.unsafeClone</code> is unaware of unique identifiers, and
+ * copies UIDs created by <code>getUid</code> into cloned results.
+ *
+ * @param {T} obj The value to clone.
+ * @return {T} A clone of the input value.
+ * @template T
+ */
+goog.object.unsafeClone = function(obj) {
+ var type = goog.typeOf(obj);
+ if (type == 'object' || type == 'array') {
+ if (goog.isFunction(obj.clone)) {
+ return obj.clone();
+ }
+ var clone = type == 'array' ? [] : {};
+ for (var key in obj) {
+ clone[key] = goog.object.unsafeClone(obj[key]);
+ }
+ return clone;
+ }
+
+ return obj;
+};
+
+
+/**
+ * Returns a new object in which all the keys and values are interchanged
+ * (keys become values and values become keys). If multiple keys map to the
+ * same value, the chosen transposed value is implementation-dependent.
+ *
+ * @param {Object} obj The object to transpose.
+ * @return {!Object} The transposed object.
+ */
+goog.object.transpose = function(obj) {
+ var transposed = {};
+ for (var key in obj) {
+ transposed[obj[key]] = key;
+ }
+ return transposed;
+};
+
+
+/**
+ * The names of the fields that are defined on Object.prototype.
+ * @type {Array<string>}
+ * @private
+ */
+goog.object.PROTOTYPE_FIELDS_ = [
+ 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable',
+ 'toLocaleString', 'toString', 'valueOf'
+];
+
+
+/**
+ * Extends an object with another object.
+ * This operates 'in-place'; it does not create a new Object.
+ *
+ * Example:
+ * var o = {};
+ * goog.object.extend(o, {a: 0, b: 1});
+ * o; // {a: 0, b: 1}
+ * goog.object.extend(o, {b: 2, c: 3});
+ * o; // {a: 0, b: 2, c: 3}
+ *
+ * @param {Object} target The object to modify. Existing properties will be
+ * overwritten if they are also present in one of the objects in
+ * {@code var_args}.
+ * @param {...Object} var_args The objects from which values will be copied.
+ */
+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 IE the for-in-loop does not contain any properties that are not
+ // enumerable on the prototype object (for example isPrototypeOf from
+ // Object.prototype) and it will also not include 'replace' on objects that
+ // extend String and change 'replace' (not that it is common for anyone to
+ // extend anything except Object).
+
+ 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];
+ }
+ }
+ }
+};
+
+
+/**
+ * Creates a new object built from the key-value pairs provided as arguments.
+ * @param {...*} var_args If only one argument is provided and it is an array
+ * then this is used as the arguments, otherwise even arguments are used as
+ * the property names and odd arguments are used as the property values.
+ * @return {!Object} The new object.
+ * @throws {Error} If there are uneven number of arguments or there is only one
+ * non array argument.
+ */
+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 new Error('Uneven number of arguments');
+ }
+
+ var rv = {};
+ for (var i = 0; i < argLength; i += 2) {
+ rv[arguments[i]] = arguments[i + 1];
+ }
+ return rv;
+};
+
+
+/**
+ * Creates a new object where the property names come from the arguments but
+ * the value is always set to true
+ * @param {...*} var_args If only one argument is provided and it is an array
+ * then this is used as the arguments, otherwise the arguments are used
+ * as the property names.
+ * @return {!Object} The new object.
+ */
+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;
+};
+
+
+/**
+ * Creates an immutable view of the underlying object, if the browser
+ * supports immutable objects.
+ *
+ * In default mode, writes to this view will fail silently. In strict mode,
+ * they will throw an error.
+ *
+ * @param {!Object<K,V>} obj An object.
+ * @return {!Object<K,V>} An immutable view of that object, or the
+ * original object if this browser does not support immutables.
+ * @template K,V
+ */
+goog.object.createImmutableView = function(obj) {
+ var result = obj;
+ if (Object.isFrozen && !Object.isFrozen(obj)) {
+ result = Object.create(obj);
+ Object.freeze(result);
+ }
+ return result;
+};
+
+
+/**
+ * @param {!Object} obj An object.
+ * @return {boolean} Whether this is an immutable view of the object.
+ */
+goog.object.isImmutableView = function(obj) {
+ return !!Object.isFrozen && Object.isFrozen(obj);
+};
+
+
+/**
+ * Get all properties names on a given Object regardless of enumerability.
+ *
+ * <p> If the browser does not support {@code Object.getOwnPropertyNames} nor
+ * {@code Object.getPrototypeOf} then this is equivalent to using {@code
+ * goog.object.getKeys}
+ *
+ * @param {?Object} obj The object to get the properties of.
+ * @param {boolean=} opt_includeObjectPrototype Whether properties defined on
+ * {@code Object.prototype} should be included in the result.
+ * @param {boolean=} opt_includeFunctionPrototype Whether properties defined on
+ * {@code Function.prototype} should be included in the result.
+ * @return {!Array<string>}
+ * @public
+ */
+goog.object.getAllPropertyNames = function(
+ obj, opt_includeObjectPrototype, opt_includeFunctionPrototype) {
+ if (!obj) {
+ return [];
+ }
+
+ // Naively use a for..in loop to get the property names if the browser doesn't
+ // support any other APIs for getting it.
+ if (!Object.getOwnPropertyNames || !Object.getPrototypeOf) {
+ return goog.object.getKeys(obj);
+ }
+
+ var visitedSet = {};
+
+ // Traverse the prototype chain and add all properties to the visited set.
+ var proto = obj;
+ while (proto &&
+ (proto !== Object.prototype || !!opt_includeObjectPrototype) &&
+ (proto !== Function.prototype || !!opt_includeFunctionPrototype)) {
+ var names = Object.getOwnPropertyNames(proto);
+ for (var i = 0; i < names.length; i++) {
+ visitedSet[names[i]] = true;
+ }
+ proto = Object.getPrototypeOf(proto);
+ }
+
+ return goog.object.getKeys(visitedSet);
+};
diff --git a/chromium/third_party/ink/closure/proto2/descriptor.js b/chromium/third_party/ink/closure/proto2/descriptor.js
new file mode 100644
index 00000000000..4abc3a35784
--- /dev/null
+++ b/chromium/third_party/ink/closure/proto2/descriptor.js
@@ -0,0 +1,202 @@
+// Copyright 2008 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Protocol Buffer (Message) Descriptor class.
+ * @author jschorr@google.com (Joseph Schorr)
+ */
+
+goog.provide('goog.proto2.Descriptor');
+goog.provide('goog.proto2.Metadata');
+
+goog.require('goog.array');
+goog.require('goog.asserts');
+goog.require('goog.object');
+goog.require('goog.string');
+
+
+/**
+ * @typedef {{name: (string|undefined),
+ * fullName: (string|undefined),
+ * containingType: (goog.proto2.Message|undefined)}}
+ */
+goog.proto2.Metadata;
+
+
+
+/**
+ * A class which describes a Protocol Buffer 2 Message.
+ *
+ * @param {function(new:goog.proto2.Message)} messageType Constructor for
+ * the message class that this descriptor describes.
+ * @param {!goog.proto2.Metadata} metadata The metadata about the message that
+ * will be used to construct this descriptor.
+ * @param {Array<!goog.proto2.FieldDescriptor>} fields The fields of the
+ * message described by this descriptor.
+ *
+ * @constructor
+ * @final
+ */
+goog.proto2.Descriptor = function(messageType, metadata, fields) {
+
+ /**
+ * @type {function(new:goog.proto2.Message)}
+ * @private
+ */
+ this.messageType_ = messageType;
+
+ /**
+ * @type {?string}
+ * @private
+ */
+ this.name_ = metadata.name || null;
+
+ /**
+ * @type {?string}
+ * @private
+ */
+ this.fullName_ = metadata.fullName || null;
+
+ /**
+ * @type {goog.proto2.Message|undefined}
+ * @private
+ */
+ this.containingType_ = metadata.containingType;
+
+ /**
+ * The fields of the message described by this descriptor.
+ * @type {!Object<number, !goog.proto2.FieldDescriptor>}
+ * @private
+ */
+ this.fields_ = {};
+
+ for (var i = 0; i < fields.length; i++) {
+ var field = fields[i];
+ this.fields_[field.getTag()] = field;
+ }
+};
+
+
+/**
+ * Returns the name of the message, if any.
+ *
+ * @return {?string} The name.
+ */
+goog.proto2.Descriptor.prototype.getName = function() {
+ return this.name_;
+};
+
+
+/**
+ * Returns the full name of the message, if any.
+ *
+ * @return {?string} The name.
+ */
+goog.proto2.Descriptor.prototype.getFullName = function() {
+ return this.fullName_;
+};
+
+
+/**
+ * Returns the descriptor of the containing message type or null if none.
+ *
+ * @return {goog.proto2.Descriptor} The descriptor.
+ */
+goog.proto2.Descriptor.prototype.getContainingType = function() {
+ if (!this.containingType_) {
+ return null;
+ }
+
+ return this.containingType_.getDescriptor();
+};
+
+
+/**
+ * Returns the fields in the message described by this descriptor ordered by
+ * tag.
+ *
+ * @return {!Array<!goog.proto2.FieldDescriptor>} The array of field
+ * descriptors.
+ */
+goog.proto2.Descriptor.prototype.getFields = function() {
+ /**
+ * @param {!goog.proto2.FieldDescriptor} fieldA First field.
+ * @param {!goog.proto2.FieldDescriptor} fieldB Second field.
+ * @return {number} Negative if fieldA's tag number is smaller, positive
+ * if greater, zero if the same.
+ */
+ function tagComparator(fieldA, fieldB) {
+ return fieldA.getTag() - fieldB.getTag();
+ }
+
+ var fields = goog.object.getValues(this.fields_);
+ goog.array.sort(fields, tagComparator);
+
+ return fields;
+};
+
+
+/**
+ * Returns the fields in the message as a key/value map, where the key is
+ * the tag number of the field. DO NOT MODIFY THE RETURNED OBJECT. We return
+ * the actual, internal, fields map for performance reasons, and changing the
+ * map can result in undefined behavior of this library.
+ *
+ * @return {!Object<number, !goog.proto2.FieldDescriptor>} The field map.
+ */
+goog.proto2.Descriptor.prototype.getFieldsMap = function() {
+ return this.fields_;
+};
+
+
+/**
+ * Returns the field matching the given name, if any. Note that
+ * this method searches over the *original* name of the field,
+ * not the camelCase version.
+ *
+ * @param {string} name The field name for which to search.
+ *
+ * @return {goog.proto2.FieldDescriptor} The field found, if any.
+ */
+goog.proto2.Descriptor.prototype.findFieldByName = function(name) {
+ var valueFound = goog.object.findValue(
+ this.fields_,
+ function(field, key, obj) { return field.getName() == name; });
+
+ return /** @type {goog.proto2.FieldDescriptor} */ (valueFound) || null;
+};
+
+
+/**
+ * Returns the field matching the given tag number, if any.
+ *
+ * @param {number|string} tag The field tag number for which to search.
+ *
+ * @return {goog.proto2.FieldDescriptor} The field found, if any.
+ */
+goog.proto2.Descriptor.prototype.findFieldByTag = function(tag) {
+ goog.asserts.assert(goog.string.isNumeric(tag));
+ return this.fields_[parseInt(tag, 10)] || null;
+};
+
+
+/**
+ * Creates an instance of the message type that this descriptor
+ * describes.
+ *
+ * @return {!goog.proto2.Message} The instance of the message.
+ */
+goog.proto2.Descriptor.prototype.createMessageInstance = function() {
+ return new this.messageType_;
+};
diff --git a/chromium/third_party/ink/closure/proto2/fielddescriptor.js b/chromium/third_party/ink/closure/proto2/fielddescriptor.js
new file mode 100644
index 00000000000..7fbef9885b7
--- /dev/null
+++ b/chromium/third_party/ink/closure/proto2/fielddescriptor.js
@@ -0,0 +1,313 @@
+// Copyright 2008 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Protocol Buffer Field Descriptor class.
+ * @author jschorr@google.com (Joseph Schorr)
+ */
+
+goog.provide('goog.proto2.FieldDescriptor');
+
+goog.require('goog.asserts');
+goog.require('goog.string');
+
+
+
+/**
+ * A class which describes a field in a Protocol Buffer 2 Message.
+ *
+ * @param {function(new:goog.proto2.Message)} messageType Constructor for the
+ * message class to which the field described by this class belongs.
+ * @param {number|string} tag The field's tag index.
+ * @param {Object} metadata The metadata about this field that will be used
+ * to construct this descriptor.
+ *
+ * @constructor
+ * @final
+ */
+goog.proto2.FieldDescriptor = function(messageType, tag, metadata) {
+ /**
+ * The message type that contains the field that this
+ * descriptor describes.
+ * @private {function(new:goog.proto2.Message)}
+ */
+ this.parent_ = messageType;
+
+ // Ensure that the tag is numeric.
+ goog.asserts.assert(goog.string.isNumeric(tag));
+
+ /**
+ * The field's tag number.
+ * @private {number}
+ */
+ this.tag_ = /** @type {number} */ (tag);
+
+ /**
+ * The field's name.
+ * @private {string}
+ */
+ this.name_ = metadata.name;
+
+ /** @type {goog.proto2.FieldDescriptor.FieldType} */
+ metadata.fieldType;
+
+ /** @type {*} */
+ metadata.repeated;
+
+ /** @type {*} */
+ metadata.required;
+
+ /** @type {*} */
+ metadata.packed;
+
+ /**
+ * If true, this field is a packed field.
+ * @private {boolean}
+ */
+ this.isPacked_ = !!metadata.packed;
+
+ /**
+ * If true, this field is a repeating field.
+ * @private {boolean}
+ */
+ this.isRepeated_ = !!metadata.repeated;
+
+ /**
+ * If true, this field is required.
+ * @private {boolean}
+ */
+ this.isRequired_ = !!metadata.required;
+
+ /**
+ * The field type of this field.
+ * @private {goog.proto2.FieldDescriptor.FieldType}
+ */
+ this.fieldType_ = metadata.fieldType;
+
+ /**
+ * If this field is a primitive: The native (ECMAScript) type of this field.
+ * If an enumeration: The enumeration object.
+ * If a message or group field: The Message function.
+ * @private {Function}
+ */
+ this.nativeType_ = metadata.type;
+
+ /**
+ * Is it permissible on deserialization to convert between numbers and
+ * well-formed strings? Is true for 64-bit integral field types and float and
+ * double types, false for all other field types.
+ * @private {boolean}
+ */
+ this.deserializationConversionPermitted_ = false;
+
+ switch (this.fieldType_) {
+ case goog.proto2.FieldDescriptor.FieldType.INT64:
+ case goog.proto2.FieldDescriptor.FieldType.UINT64:
+ case goog.proto2.FieldDescriptor.FieldType.FIXED64:
+ case goog.proto2.FieldDescriptor.FieldType.SFIXED64:
+ case goog.proto2.FieldDescriptor.FieldType.SINT64:
+ case goog.proto2.FieldDescriptor.FieldType.FLOAT:
+ case goog.proto2.FieldDescriptor.FieldType.DOUBLE:
+ this.deserializationConversionPermitted_ = true;
+ break;
+ }
+
+ /**
+ * The default value of this field, if different from the default, default
+ * value.
+ * @private {*}
+ */
+ this.defaultValue_ = metadata.defaultValue;
+};
+
+
+/**
+ * An enumeration defining the possible field types.
+ * Should be a mirror of that defined in descriptor.h.
+ *
+ * @enum {number}
+ */
+goog.proto2.FieldDescriptor.FieldType = {
+ DOUBLE: 1,
+ FLOAT: 2,
+ INT64: 3,
+ UINT64: 4,
+ INT32: 5,
+ FIXED64: 6,
+ FIXED32: 7,
+ BOOL: 8,
+ STRING: 9,
+ GROUP: 10,
+ MESSAGE: 11,
+ BYTES: 12,
+ UINT32: 13,
+ ENUM: 14,
+ SFIXED32: 15,
+ SFIXED64: 16,
+ SINT32: 17,
+ SINT64: 18
+};
+
+
+/**
+ * Returns the tag of the field that this descriptor represents.
+ *
+ * @return {number} The tag number.
+ */
+goog.proto2.FieldDescriptor.prototype.getTag = function() {
+ return this.tag_;
+};
+
+
+/**
+ * Returns the descriptor describing the message that defined this field.
+ * @return {!goog.proto2.Descriptor} The descriptor.
+ */
+goog.proto2.FieldDescriptor.prototype.getContainingType = function() {
+ // Generated JS proto_library messages have getDescriptor() method which can
+ // be called with or without an instance.
+ return this.parent_.prototype.getDescriptor();
+};
+
+
+/**
+ * Returns the name of the field that this descriptor represents.
+ * @return {string} The name.
+ */
+goog.proto2.FieldDescriptor.prototype.getName = function() {
+ return this.name_;
+};
+
+
+/**
+ * Returns the default value of this field.
+ * @return {*} The default value.
+ */
+goog.proto2.FieldDescriptor.prototype.getDefaultValue = function() {
+ if (this.defaultValue_ === undefined) {
+ // Set the default value based on a new instance of the native type.
+ // This will be (0, false, "") for (number, boolean, string) and will
+ // be a new instance of a group/message if the field is a message type.
+ var nativeType = this.nativeType_;
+ if (nativeType === Boolean) {
+ this.defaultValue_ = false;
+ } else if (nativeType === Number) {
+ this.defaultValue_ = 0;
+ } else if (nativeType === String) {
+ if (this.deserializationConversionPermitted_) {
+ // This field is a 64 bit integer represented as a string.
+ this.defaultValue_ = '0';
+ } else {
+ this.defaultValue_ = '';
+ }
+ } else {
+ return new nativeType;
+ }
+ }
+
+ return this.defaultValue_;
+};
+
+
+/**
+ * Returns the field type of the field described by this descriptor.
+ * @return {goog.proto2.FieldDescriptor.FieldType} The field type.
+ */
+goog.proto2.FieldDescriptor.prototype.getFieldType = function() {
+ return this.fieldType_;
+};
+
+
+/**
+ * Returns the native (i.e. ECMAScript) type of the field described by this
+ * descriptor.
+ *
+ * @return {Object} The native type.
+ */
+goog.proto2.FieldDescriptor.prototype.getNativeType = function() {
+ return this.nativeType_;
+};
+
+
+/**
+ * Returns true if simple conversions between numbers and strings are permitted
+ * during deserialization for this field.
+ *
+ * @return {boolean} Whether conversion is permitted.
+ */
+goog.proto2.FieldDescriptor.prototype.deserializationConversionPermitted =
+ function() {
+ return this.deserializationConversionPermitted_;
+};
+
+
+/**
+ * Returns the descriptor of the message type of this field. Only valid
+ * for fields of type GROUP and MESSAGE.
+ *
+ * @return {!goog.proto2.Descriptor} The message descriptor.
+ */
+goog.proto2.FieldDescriptor.prototype.getFieldMessageType = function() {
+ // Generated JS proto_library messages have getDescriptor() method which can
+ // be called with or without an instance.
+ var messageClass =
+ /** @type {function(new:goog.proto2.Message)} */ (this.nativeType_);
+ return messageClass.prototype.getDescriptor();
+};
+
+
+/**
+ * @return {boolean} True if the field stores composite data or repeated
+ * composite data (message or group).
+ */
+goog.proto2.FieldDescriptor.prototype.isCompositeType = function() {
+ return this.fieldType_ == goog.proto2.FieldDescriptor.FieldType.MESSAGE ||
+ this.fieldType_ == goog.proto2.FieldDescriptor.FieldType.GROUP;
+};
+
+
+/**
+ * Returns whether the field described by this descriptor is packed.
+ * @return {boolean} Whether the field is packed.
+ */
+goog.proto2.FieldDescriptor.prototype.isPacked = function() {
+ return this.isPacked_;
+};
+
+
+/**
+ * Returns whether the field described by this descriptor is repeating.
+ * @return {boolean} Whether the field is repeated.
+ */
+goog.proto2.FieldDescriptor.prototype.isRepeated = function() {
+ return this.isRepeated_;
+};
+
+
+/**
+ * Returns whether the field described by this descriptor is required.
+ * @return {boolean} Whether the field is required.
+ */
+goog.proto2.FieldDescriptor.prototype.isRequired = function() {
+ return this.isRequired_;
+};
+
+
+/**
+ * Returns whether the field described by this descriptor is optional.
+ * @return {boolean} Whether the field is optional.
+ */
+goog.proto2.FieldDescriptor.prototype.isOptional = function() {
+ return !this.isRepeated_ && !this.isRequired_;
+};
diff --git a/chromium/third_party/ink/closure/proto2/message.js b/chromium/third_party/ink/closure/proto2/message.js
new file mode 100644
index 00000000000..031568078a7
--- /dev/null
+++ b/chromium/third_party/ink/closure/proto2/message.js
@@ -0,0 +1,733 @@
+// Copyright 2008 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Protocol Buffer Message base class.
+ * @author jschorr@google.com (Joseph Schorr)
+ * @author pallosp@google.com (Peter Pallos)
+ * @suppress {unusedPrivateMembers} For descriptor_ declaration.
+ */
+
+goog.provide('goog.proto2.Message');
+
+goog.require('goog.asserts');
+goog.require('goog.proto2.Descriptor');
+goog.require('goog.proto2.FieldDescriptor');
+
+goog.forwardDeclare('goog.proto2.LazyDeserializer'); // circular reference
+
+
+
+/**
+ * Abstract base class for all Protocol Buffer 2 messages. It will be
+ * subclassed in the code generated by the Protocol Compiler. Any other
+ * subclasses are prohibited.
+ * @constructor
+ */
+goog.proto2.Message = function() {
+ /**
+ * Stores the field values in this message. Keyed by the tag of the fields.
+ * @type {!Object}
+ * @private
+ */
+ this.values_ = {};
+
+ /**
+ * Stores the field information (i.e. metadata) about this message.
+ * @type {Object<number, !goog.proto2.FieldDescriptor>}
+ * @private
+ */
+ this.fields_ = this.getDescriptor().getFieldsMap();
+
+ /**
+ * The lazy deserializer for this message instance, if any.
+ * @type {goog.proto2.LazyDeserializer}
+ * @private
+ */
+ this.lazyDeserializer_ = null;
+
+ /**
+ * A map of those fields deserialized, from tag number to their deserialized
+ * value.
+ * @type {Object}
+ * @private
+ */
+ this.deserializedFields_ = null;
+};
+
+
+/**
+ * An enumeration defining the possible field types.
+ * Should be a mirror of that defined in descriptor.h.
+ *
+ * TODO(sra): Remove this alias. The code generator generates code that
+ * references this enum, so it needs to exist until the code generator is
+ * changed. The enum was moved to from Message to FieldDescriptor to avoid a
+ * dependency cycle.
+ *
+ * Use goog.proto2.FieldDescriptor.FieldType instead.
+ *
+ * @enum {number}
+ */
+goog.proto2.Message.FieldType = {
+ DOUBLE: 1,
+ FLOAT: 2,
+ INT64: 3,
+ UINT64: 4,
+ INT32: 5,
+ FIXED64: 6,
+ FIXED32: 7,
+ BOOL: 8,
+ STRING: 9,
+ GROUP: 10,
+ MESSAGE: 11,
+ BYTES: 12,
+ UINT32: 13,
+ ENUM: 14,
+ SFIXED32: 15,
+ SFIXED64: 16,
+ SINT32: 17,
+ SINT64: 18
+};
+
+
+/**
+ * All instances of goog.proto2.Message should have a static descriptor_
+ * property. The Descriptor will be deserialized lazily in the getDescriptor()
+ * method.
+ *
+ * This declaration is just here for documentation purposes.
+ * goog.proto2.Message does not have its own descriptor.
+ *
+ * @type {undefined}
+ * @private
+ */
+goog.proto2.Message.descriptor_;
+
+
+/**
+ * Initializes the message with a lazy deserializer and its associated data.
+ * This method should be called by internal methods ONLY.
+ *
+ * @param {goog.proto2.LazyDeserializer} deserializer The lazy deserializer to
+ * use to decode the data on the fly.
+ *
+ * @param {?} data The data to decode/deserialize.
+ */
+goog.proto2.Message.prototype.initializeForLazyDeserializer = function(
+ deserializer, data) {
+
+ this.lazyDeserializer_ = deserializer;
+ this.values_ = data;
+ this.deserializedFields_ = {};
+};
+
+
+/**
+ * Sets the value of an unknown field, by tag.
+ *
+ * @param {number} tag The tag of an unknown field (must be >= 1).
+ * @param {*} value The value for that unknown field.
+ */
+goog.proto2.Message.prototype.setUnknown = function(tag, value) {
+ goog.asserts.assert(
+ !this.fields_[tag], 'Field is not unknown in this message');
+ goog.asserts.assert(
+ tag >= 1, 'Tag ' + tag + ' has value "' + value + '" in descriptor ' +
+ this.getDescriptor().getName());
+
+ goog.asserts.assert(value !== null, 'Value cannot be null');
+
+ this.values_[tag] = value;
+ if (this.deserializedFields_) {
+ delete this.deserializedFields_[tag];
+ }
+};
+
+
+/**
+ * Iterates over all the unknown fields in the message.
+ *
+ * @param {function(this:T, number, *)} callback A callback method
+ * which gets invoked for each unknown field.
+ * @param {T=} opt_scope The scope under which to execute the callback.
+ * If not given, the current message will be used.
+ * @template T
+ */
+goog.proto2.Message.prototype.forEachUnknown = function(callback, opt_scope) {
+ var scope = opt_scope || this;
+ for (var key in this.values_) {
+ var keyNum = Number(key);
+ if (!this.fields_[keyNum]) {
+ callback.call(scope, keyNum, this.values_[key]);
+ }
+ }
+};
+
+
+/**
+ * Returns the descriptor which describes the current message.
+ *
+ * This only works if we assume people never subclass protobufs.
+ *
+ * @return {!goog.proto2.Descriptor} The descriptor.
+ */
+goog.proto2.Message.prototype.getDescriptor = goog.abstractMethod;
+
+
+/**
+ * Returns whether there is a value stored at the field specified by the
+ * given field descriptor.
+ *
+ * @param {goog.proto2.FieldDescriptor} field The field for which to check
+ * if there is a value.
+ *
+ * @return {boolean} True if a value was found.
+ */
+goog.proto2.Message.prototype.has = function(field) {
+ goog.asserts.assert(
+ field.getContainingType() == this.getDescriptor(),
+ 'The current message does not contain the given field');
+
+ return this.has$Value(field.getTag());
+};
+
+
+/**
+ * Returns the array of values found for the given repeated field.
+ *
+ * @param {goog.proto2.FieldDescriptor} field The field for which to
+ * return the values.
+ *
+ * @return {!Array<?>} The values found.
+ */
+goog.proto2.Message.prototype.arrayOf = function(field) {
+ goog.asserts.assert(
+ field.getContainingType() == this.getDescriptor(),
+ 'The current message does not contain the given field');
+
+ return this.array$Values(field.getTag());
+};
+
+
+/**
+ * Returns the number of values stored in the given field.
+ *
+ * @param {goog.proto2.FieldDescriptor} field The field for which to count
+ * the number of values.
+ *
+ * @return {number} The count of the values in the given field.
+ */
+goog.proto2.Message.prototype.countOf = function(field) {
+ goog.asserts.assert(
+ field.getContainingType() == this.getDescriptor(),
+ 'The current message does not contain the given field');
+
+ return this.count$Values(field.getTag());
+};
+
+
+/**
+ * Returns the value stored at the field specified by the
+ * given field descriptor.
+ *
+ * @param {goog.proto2.FieldDescriptor} field The field for which to get the
+ * value.
+ * @param {number=} opt_index If the field is repeated, the index to use when
+ * looking up the value.
+ *
+ * @return {?} The value found or null if none.
+ */
+goog.proto2.Message.prototype.get = function(field, opt_index) {
+ goog.asserts.assert(
+ field.getContainingType() == this.getDescriptor(),
+ 'The current message does not contain the given field');
+
+ return this.get$Value(field.getTag(), opt_index);
+};
+
+
+/**
+ * Returns the value stored at the field specified by the
+ * given field descriptor or the default value if none exists.
+ *
+ * @param {goog.proto2.FieldDescriptor} field The field for which to get the
+ * value.
+ * @param {number=} opt_index If the field is repeated, the index to use when
+ * looking up the value.
+ *
+ * @return {?} The value found or the default if none.
+ */
+goog.proto2.Message.prototype.getOrDefault = function(field, opt_index) {
+ goog.asserts.assert(
+ field.getContainingType() == this.getDescriptor(),
+ 'The current message does not contain the given field');
+
+ return this.get$ValueOrDefault(field.getTag(), opt_index);
+};
+
+
+/**
+ * Stores the given value to the field specified by the
+ * given field descriptor. Note that the field must not be repeated.
+ *
+ * @param {goog.proto2.FieldDescriptor} field The field for which to set
+ * the value.
+ * @param {*} value The new value for the field.
+ */
+goog.proto2.Message.prototype.set = function(field, value) {
+ goog.asserts.assert(
+ field.getContainingType() == this.getDescriptor(),
+ 'The current message does not contain the given field');
+
+ this.set$Value(field.getTag(), value);
+};
+
+
+/**
+ * Adds the given value to the field specified by the
+ * given field descriptor. Note that the field must be repeated.
+ *
+ * @param {goog.proto2.FieldDescriptor} field The field in which to add the
+ * the value.
+ * @param {*} value The new value to add to the field.
+ */
+goog.proto2.Message.prototype.add = function(field, value) {
+ goog.asserts.assert(
+ field.getContainingType() == this.getDescriptor(),
+ 'The current message does not contain the given field');
+
+ this.add$Value(field.getTag(), value);
+};
+
+
+/**
+ * Clears the field specified.
+ *
+ * @param {goog.proto2.FieldDescriptor} field The field to clear.
+ */
+goog.proto2.Message.prototype.clear = function(field) {
+ goog.asserts.assert(
+ field.getContainingType() == this.getDescriptor(),
+ 'The current message does not contain the given field');
+
+ this.clear$Field(field.getTag());
+};
+
+
+/**
+ * Compares this message with another one ignoring the unknown fields.
+ * @param {?} other The other message.
+ * @return {boolean} Whether they are equal. Returns false if the {@code other}
+ * argument is a different type of message or not a message.
+ */
+goog.proto2.Message.prototype.equals = function(other) {
+ if (!other || this.constructor != other.constructor) {
+ return false;
+ }
+
+ var fields = this.getDescriptor().getFields();
+ for (var i = 0; i < fields.length; i++) {
+ var field = fields[i];
+ var tag = field.getTag();
+ if (this.has$Value(tag) != other.has$Value(tag)) {
+ return false;
+ }
+
+ if (this.has$Value(tag)) {
+ var isComposite = field.isCompositeType();
+
+ var fieldsEqual = function(value1, value2) {
+ return isComposite ? value1.equals(value2) : value1 == value2;
+ };
+
+ var thisValue = this.getValueForTag_(tag);
+ var otherValue = other.getValueForTag_(tag);
+
+ if (field.isRepeated()) {
+ // In this case thisValue and otherValue are arrays.
+ if (thisValue.length != otherValue.length) {
+ return false;
+ }
+ for (var j = 0; j < thisValue.length; j++) {
+ if (!fieldsEqual(thisValue[j], otherValue[j])) {
+ return false;
+ }
+ }
+ } else if (!fieldsEqual(thisValue, otherValue)) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+};
+
+
+/**
+ * Recursively copies the known fields from the given message to this message.
+ * Removes the fields which are not present in the source message.
+ * @param {!goog.proto2.Message} message The source message.
+ */
+goog.proto2.Message.prototype.copyFrom = function(message) {
+ goog.asserts.assert(
+ this.constructor == message.constructor,
+ 'The source message must have the same type.');
+
+ if (this != message) {
+ this.values_ = {};
+ if (this.deserializedFields_) {
+ this.deserializedFields_ = {};
+ }
+ this.mergeFrom(message);
+ }
+};
+
+
+/**
+ * Merges the given message into this message.
+ *
+ * Singular fields will be overwritten, except for embedded messages which will
+ * be merged. Repeated fields will be concatenated.
+ * @param {!goog.proto2.Message} message The source message.
+ */
+goog.proto2.Message.prototype.mergeFrom = function(message) {
+ goog.asserts.assert(
+ this.constructor == message.constructor,
+ 'The source message must have the same type.');
+ var fields = this.getDescriptor().getFields();
+
+ for (var i = 0; i < fields.length; i++) {
+ var field = fields[i];
+ var tag = field.getTag();
+ if (message.has$Value(tag)) {
+ if (this.deserializedFields_) {
+ delete this.deserializedFields_[field.getTag()];
+ }
+
+ var isComposite = field.isCompositeType();
+ if (field.isRepeated()) {
+ var values = message.array$Values(tag);
+ for (var j = 0; j < values.length; j++) {
+ this.add$Value(tag, isComposite ? values[j].clone() : values[j]);
+ }
+ } else {
+ var value = message.getValueForTag_(tag);
+ if (isComposite) {
+ var child = this.getValueForTag_(tag);
+ if (child) {
+ child.mergeFrom(value);
+ } else {
+ this.set$Value(tag, value.clone());
+ }
+ } else {
+ this.set$Value(tag, value);
+ }
+ }
+ }
+ }
+};
+
+
+/**
+ * @return {!goog.proto2.Message} Recursive clone of the message only including
+ * the known fields.
+ */
+goog.proto2.Message.prototype.clone = function() {
+ /** @type {!goog.proto2.Message} */
+ var clone = new this.constructor;
+ clone.copyFrom(this);
+ return clone;
+};
+
+
+/**
+ * Fills in the protocol buffer with default values. Any fields that are
+ * already set will not be overridden.
+ * @param {boolean} simpleFieldsToo If true, all fields will be initialized;
+ * if false, only the nested messages and groups.
+ */
+goog.proto2.Message.prototype.initDefaults = function(simpleFieldsToo) {
+ var fields = this.getDescriptor().getFields();
+ for (var i = 0; i < fields.length; i++) {
+ var field = fields[i];
+ var tag = field.getTag();
+ var isComposite = field.isCompositeType();
+
+ // Initialize missing fields.
+ if (!this.has$Value(tag) && !field.isRepeated()) {
+ if (isComposite) {
+ this.values_[tag] = new /** @type {Function} */ (field.getNativeType());
+ } else if (simpleFieldsToo) {
+ this.values_[tag] = field.getDefaultValue();
+ }
+ }
+
+ // Fill in the existing composite fields recursively.
+ if (isComposite) {
+ if (field.isRepeated()) {
+ var values = this.array$Values(tag);
+ for (var j = 0; j < values.length; j++) {
+ values[j].initDefaults(simpleFieldsToo);
+ }
+ } else {
+ this.get$Value(tag).initDefaults(simpleFieldsToo);
+ }
+ }
+ }
+};
+
+
+/**
+ * Returns the whether or not the field indicated by the given tag
+ * has a value.
+ *
+ * GENERATED CODE USE ONLY. Basis of the has{Field} methods.
+ *
+ * @param {number} tag The tag.
+ *
+ * @return {boolean} Whether the message has a value for the field.
+ */
+goog.proto2.Message.prototype.has$Value = function(tag) {
+ return this.values_[tag] != null;
+};
+
+
+/**
+ * Returns the value for the given tag number. If a lazy deserializer is
+ * instantiated, lazily deserializes the field if required before returning the
+ * value.
+ *
+ * @param {number} tag The tag number.
+ * @return {?} The corresponding value, if any.
+ * @private
+ */
+goog.proto2.Message.prototype.getValueForTag_ = function(tag) {
+ // Retrieve the current value, which may still be serialized.
+ var value = this.values_[tag];
+ if (!goog.isDefAndNotNull(value)) {
+ return null;
+ }
+
+ // If we have a lazy deserializer, then ensure that the field is
+ // properly deserialized.
+ if (this.lazyDeserializer_) {
+ // If the tag is not deserialized, then we must do so now. Deserialize
+ // the field's value via the deserializer.
+ if (!(tag in /** @type {!Object} */ (this.deserializedFields_))) {
+ var deserializedValue = this.lazyDeserializer_.deserializeField(
+ this, this.fields_[tag], value);
+ this.deserializedFields_[tag] = deserializedValue;
+ return deserializedValue;
+ }
+
+ return this.deserializedFields_[tag];
+ }
+
+ // Otherwise, just return the value.
+ return value;
+};
+
+
+/**
+ * Gets the value at the field indicated by the given tag.
+ *
+ * GENERATED CODE USE ONLY. Basis of the get{Field} methods.
+ *
+ * @param {number} tag The field's tag index.
+ * @param {number=} opt_index If the field is a repeated field, the index
+ * at which to get the value.
+ *
+ * @return {?} The value found or null for none.
+ * @protected
+ */
+goog.proto2.Message.prototype.get$Value = function(tag, opt_index) {
+ var value = this.getValueForTag_(tag);
+
+ if (this.fields_[tag].isRepeated()) {
+ var index = opt_index || 0;
+ goog.asserts.assert(
+ index >= 0 && index < value.length,
+ 'Given index %s is out of bounds. Repeated field length: %s', index,
+ value.length);
+ return value[index];
+ }
+
+ return value;
+};
+
+
+/**
+ * Gets the value at the field indicated by the given tag or the default value
+ * if none.
+ *
+ * GENERATED CODE USE ONLY. Basis of the get{Field} methods.
+ *
+ * @param {number} tag The field's tag index.
+ * @param {number=} opt_index If the field is a repeated field, the index
+ * at which to get the value.
+ *
+ * @return {?} The value found or the default value if none set.
+ * @protected
+ */
+goog.proto2.Message.prototype.get$ValueOrDefault = function(tag, opt_index) {
+ if (!this.has$Value(tag)) {
+ // Return the default value.
+ var field = this.fields_[tag];
+ return field.getDefaultValue();
+ }
+
+ return this.get$Value(tag, opt_index);
+};
+
+
+/**
+ * Gets the values at the field indicated by the given tag.
+ *
+ * GENERATED CODE USE ONLY. Basis of the {field}Array methods.
+ *
+ * @param {number} tag The field's tag index.
+ *
+ * @return {!Array<?>} The values found. If none, returns an empty array.
+ * @protected
+ */
+goog.proto2.Message.prototype.array$Values = function(tag) {
+ var value = this.getValueForTag_(tag);
+ return value || [];
+};
+
+
+/**
+ * Returns the number of values stored in the field by the given tag.
+ *
+ * GENERATED CODE USE ONLY. Basis of the {field}Count methods.
+ *
+ * @param {number} tag The tag.
+ *
+ * @return {number} The number of values.
+ * @protected
+ */
+goog.proto2.Message.prototype.count$Values = function(tag) {
+ var field = this.fields_[tag];
+ if (field.isRepeated()) {
+ return this.has$Value(tag) ? this.values_[tag].length : 0;
+ } else {
+ return this.has$Value(tag) ? 1 : 0;
+ }
+};
+
+
+/**
+ * Sets the value of the *non-repeating* field indicated by the given tag.
+ *
+ * GENERATED CODE USE ONLY. Basis of the set{Field} methods.
+ *
+ * @param {number} tag The field's tag index.
+ * @param {*} value The field's value.
+ * @protected
+ */
+goog.proto2.Message.prototype.set$Value = function(tag, value) {
+ if (goog.asserts.ENABLE_ASSERTS) {
+ var field = this.fields_[tag];
+ this.checkFieldType_(field, value);
+ }
+
+ this.values_[tag] = value;
+ if (this.deserializedFields_) {
+ this.deserializedFields_[tag] = value;
+ }
+};
+
+
+/**
+ * Adds the value to the *repeating* field indicated by the given tag.
+ *
+ * GENERATED CODE USE ONLY. Basis of the add{Field} methods.
+ *
+ * @param {number} tag The field's tag index.
+ * @param {*} value The value to add.
+ * @protected
+ */
+goog.proto2.Message.prototype.add$Value = function(tag, value) {
+ if (goog.asserts.ENABLE_ASSERTS) {
+ var field = this.fields_[tag];
+ this.checkFieldType_(field, value);
+ }
+
+ if (!this.values_[tag]) {
+ this.values_[tag] = [];
+ }
+
+ this.values_[tag].push(value);
+ if (this.deserializedFields_) {
+ delete this.deserializedFields_[tag];
+ }
+};
+
+
+/**
+ * Ensures that the value being assigned to the given field
+ * is valid.
+ *
+ * @param {!goog.proto2.FieldDescriptor} field The field being assigned.
+ * @param {*} value The value being assigned.
+ * @private
+ */
+goog.proto2.Message.prototype.checkFieldType_ = function(field, value) {
+ if (field.getFieldType() == goog.proto2.FieldDescriptor.FieldType.ENUM) {
+ goog.asserts.assertNumber(value);
+ } else {
+ goog.asserts.assert(Object(value).constructor == field.getNativeType());
+ }
+};
+
+
+/**
+ * Clears the field specified by tag.
+ *
+ * GENERATED CODE USE ONLY. Basis of the clear{Field} methods.
+ *
+ * @param {number} tag The tag of the field to clear.
+ * @protected
+ */
+goog.proto2.Message.prototype.clear$Field = function(tag) {
+ delete this.values_[tag];
+ if (this.deserializedFields_) {
+ delete this.deserializedFields_[tag];
+ }
+};
+
+
+/**
+ * Creates the metadata descriptor representing the definition of this message.
+ *
+ * @param {function(new:goog.proto2.Message)} messageType Constructor for the
+ * message type to which this metadata applies.
+ * @param {!Object} metadataObj The object containing the metadata.
+ * @return {!goog.proto2.Descriptor} The new descriptor.
+ */
+goog.proto2.Message.createDescriptor = function(messageType, metadataObj) {
+ var fields = [];
+ var descriptorInfo = metadataObj[0];
+
+ for (var key in metadataObj) {
+ if (key != 0) {
+ // Create the field descriptor.
+ fields.push(
+ new goog.proto2.FieldDescriptor(messageType, key, metadataObj[key]));
+ }
+ }
+
+ return new goog.proto2.Descriptor(messageType, descriptorInfo, fields);
+};
diff --git a/chromium/third_party/ink/closure/proto2/objectserializer.js b/chromium/third_party/ink/closure/proto2/objectserializer.js
new file mode 100644
index 00000000000..95fa5d3fa1d
--- /dev/null
+++ b/chromium/third_party/ink/closure/proto2/objectserializer.js
@@ -0,0 +1,202 @@
+// Copyright 2008 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Protocol Buffer 2 Serializer which serializes messages
+ * into anonymous, simplified JSON objects.
+ *
+ * @author jschorr@google.com (Joseph Schorr)
+ */
+
+goog.provide('goog.proto2.ObjectSerializer');
+
+goog.require('goog.asserts');
+goog.require('goog.proto2.FieldDescriptor');
+goog.require('goog.proto2.Serializer');
+goog.require('goog.string');
+
+
+
+/**
+ * ObjectSerializer, a serializer which turns Messages into simplified
+ * ECMAScript objects.
+ *
+ * @param {goog.proto2.ObjectSerializer.KeyOption=} opt_keyOption If specified,
+ * which key option to use when serializing/deserializing.
+ * @param {boolean=} opt_serializeBooleanAsNumber If specified and true, the
+ * serializer will convert boolean values to 0/1 representation.
+ * @constructor
+ * @extends {goog.proto2.Serializer}
+ */
+goog.proto2.ObjectSerializer = function(
+ opt_keyOption, opt_serializeBooleanAsNumber) {
+ this.keyOption_ = opt_keyOption;
+ this.serializeBooleanAsNumber_ = opt_serializeBooleanAsNumber;
+};
+goog.inherits(goog.proto2.ObjectSerializer, goog.proto2.Serializer);
+
+
+/**
+ * An enumeration of the options for how to emit the keys in
+ * the generated simplified object.
+ *
+ * @enum {number}
+ */
+goog.proto2.ObjectSerializer.KeyOption = {
+ /**
+ * Use the tag of the field as the key (default)
+ */
+ TAG: 0,
+
+ /**
+ * Use the name of the field as the key. Unknown fields
+ * will still use their tags as keys.
+ */
+ NAME: 1
+};
+
+
+/**
+ * Serializes a message to an object.
+ *
+ * @param {goog.proto2.Message} message The message to be serialized.
+ * @return {!Object} The serialized form of the message.
+ * @override
+ */
+goog.proto2.ObjectSerializer.prototype.serialize = function(message) {
+ var descriptor = message.getDescriptor();
+ var fields = descriptor.getFields();
+
+ var objectValue = {};
+
+ // Add the defined fields, recursively.
+ for (var i = 0; i < fields.length; i++) {
+ var field = fields[i];
+
+ var key = this.keyOption_ == goog.proto2.ObjectSerializer.KeyOption.NAME ?
+ field.getName() :
+ field.getTag();
+
+
+ if (message.has(field)) {
+ if (field.isRepeated()) {
+ var array = [];
+ objectValue[key] = array;
+
+ for (var j = 0; j < message.countOf(field); j++) {
+ array.push(this.getSerializedValue(field, message.get(field, j)));
+ }
+
+ } else {
+ objectValue[key] = this.getSerializedValue(field, message.get(field));
+ }
+ }
+ }
+
+ // Add the unknown fields, if any.
+ message.forEachUnknown(function(tag, value) { objectValue[tag] = value; });
+
+ return objectValue;
+};
+
+
+/** @override */
+goog.proto2.ObjectSerializer.prototype.getSerializedValue = function(
+ field, value) {
+
+ // Handle the case where a boolean should be serialized as 0/1.
+ // Some deserialization libraries, such as GWT, can use this notation.
+ if (this.serializeBooleanAsNumber_ &&
+ field.getFieldType() == goog.proto2.FieldDescriptor.FieldType.BOOL &&
+ goog.isBoolean(value)) {
+ return value ? 1 : 0;
+ }
+
+ return goog.proto2.ObjectSerializer.base(
+ this, 'getSerializedValue', field, value);
+};
+
+
+/** @override */
+goog.proto2.ObjectSerializer.prototype.getDeserializedValue = function(
+ field, value) {
+
+ // Gracefully handle the case where a boolean is represented by 0/1.
+ // Some serialization libraries, such as GWT, can use this notation.
+ if (field.getFieldType() == goog.proto2.FieldDescriptor.FieldType.BOOL &&
+ goog.isNumber(value)) {
+ return Boolean(value);
+ }
+
+ return goog.proto2.ObjectSerializer.base(
+ this, 'getDeserializedValue', field, value);
+};
+
+
+/**
+ * Deserializes a message from an object and places the
+ * data in the message.
+ *
+ * @param {goog.proto2.Message} message The message in which to
+ * place the information.
+ * @param {*} data The data of the message.
+ * @override
+ */
+goog.proto2.ObjectSerializer.prototype.deserializeTo = function(message, data) {
+ var descriptor = message.getDescriptor();
+
+ for (var key in data) {
+ var field;
+ var value = data[key];
+
+ var isNumeric = goog.string.isNumeric(key);
+
+ if (isNumeric) {
+ field = descriptor.findFieldByTag(key);
+ } else {
+ // We must be in Key == NAME mode to lookup by name.
+ goog.asserts.assert(
+ this.keyOption_ == goog.proto2.ObjectSerializer.KeyOption.NAME,
+ 'Key mode ' + this.keyOption_ + 'for key ' + key + ' is not ' +
+ goog.proto2.ObjectSerializer.KeyOption.NAME);
+
+ field = descriptor.findFieldByName(key);
+ }
+
+ if (field) {
+ if (field.isRepeated()) {
+ goog.asserts.assert(
+ goog.isArray(value),
+ 'Value for repeated field ' + field + ' must be an array.');
+
+ for (var j = 0; j < value.length; j++) {
+ message.add(field, this.getDeserializedValue(field, value[j]));
+ }
+ } else {
+ goog.asserts.assert(
+ !goog.isArray(value),
+ 'Value for non-repeated field ' + field + ' must not be an array.');
+ message.set(field, this.getDeserializedValue(field, value));
+ }
+ } else {
+ if (isNumeric) {
+ // We have an unknown field.
+ message.setUnknown(Number(key), value);
+ } else {
+ // Named fields must be present.
+ goog.asserts.fail('Failed to find field: ' + key);
+ }
+ }
+ }
+};
diff --git a/chromium/third_party/ink/closure/proto2/serializer.js b/chromium/third_party/ink/closure/proto2/serializer.js
new file mode 100644
index 00000000000..eb976d74673
--- /dev/null
+++ b/chromium/third_party/ink/closure/proto2/serializer.js
@@ -0,0 +1,198 @@
+// Copyright 2008 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Base class for all Protocol Buffer 2 serializers.
+ * @author jschorr@google.com (Joseph Schorr)
+ */
+
+goog.provide('goog.proto2.Serializer');
+
+goog.require('goog.asserts');
+goog.require('goog.proto2.FieldDescriptor');
+goog.require('goog.proto2.Message');
+
+
+
+/**
+ * Abstract base class for PB2 serializers. A serializer is a class which
+ * implements the serialization and deserialization of a Protocol Buffer Message
+ * to/from a specific format.
+ *
+ * @constructor
+ */
+goog.proto2.Serializer = function() {};
+
+
+/**
+ * @define {boolean} Whether to decode and convert symbolic enum values to
+ * actual enum values or leave them as strings.
+ */
+goog.define('goog.proto2.Serializer.DECODE_SYMBOLIC_ENUMS', false);
+
+
+/**
+ * Serializes a message to the expected format.
+ *
+ * @param {goog.proto2.Message} message The message to be serialized.
+ *
+ * @return {*} The serialized form of the message.
+ */
+goog.proto2.Serializer.prototype.serialize = goog.abstractMethod;
+
+
+/**
+ * Returns the serialized form of the given value for the given field if the
+ * field is a Message or Group and returns the value unchanged otherwise, except
+ * for Infinity, -Infinity and NaN numerical values which are converted to
+ * string representation.
+ *
+ * @param {goog.proto2.FieldDescriptor} field The field from which this
+ * value came.
+ *
+ * @param {*} value The value of the field.
+ *
+ * @return {*} The value.
+ * @protected
+ */
+goog.proto2.Serializer.prototype.getSerializedValue = function(field, value) {
+ if (field.isCompositeType()) {
+ return this.serialize(/** @type {goog.proto2.Message} */ (value));
+ } else if (goog.isNumber(value) && !isFinite(value)) {
+ return value.toString();
+ } else {
+ return value;
+ }
+};
+
+
+/**
+ * Deserializes a message from the expected format.
+ *
+ * @param {goog.proto2.Descriptor} descriptor The descriptor of the message
+ * to be created.
+ * @param {*} data The data of the message.
+ *
+ * @return {!goog.proto2.Message} The message created.
+ */
+goog.proto2.Serializer.prototype.deserialize = function(descriptor, data) {
+ var message = descriptor.createMessageInstance();
+ this.deserializeTo(message, data);
+ goog.asserts.assert(message instanceof goog.proto2.Message);
+ return message;
+};
+
+
+/**
+ * Deserializes a message from the expected format and places the
+ * data in the message.
+ *
+ * @param {goog.proto2.Message} message The message in which to
+ * place the information.
+ * @param {*} data The data of the message.
+ */
+goog.proto2.Serializer.prototype.deserializeTo = goog.abstractMethod;
+
+
+/**
+ * Returns the deserialized form of the given value for the given field if the
+ * field is a Message or Group and returns the value, converted or unchanged,
+ * for primitive field types otherwise.
+ *
+ * @param {goog.proto2.FieldDescriptor} field The field from which this
+ * value came.
+ *
+ * @param {*} value The value of the field.
+ *
+ * @return {*} The value.
+ * @protected
+ */
+goog.proto2.Serializer.prototype.getDeserializedValue = function(field, value) {
+ // Composite types are deserialized recursively.
+ if (field.isCompositeType()) {
+ if (value instanceof goog.proto2.Message) {
+ return value;
+ }
+
+ return this.deserialize(field.getFieldMessageType(), value);
+ }
+
+ // Decode enum values.
+ if (field.getFieldType() == goog.proto2.FieldDescriptor.FieldType.ENUM) {
+ // If it's a string, get enum value by name.
+ // NB: In order this feature to work, property renaming should be turned off
+ // for the respective enums.
+ if (goog.proto2.Serializer.DECODE_SYMBOLIC_ENUMS && goog.isString(value)) {
+ // enumType is a regular Javascript enum as defined in field's metadata.
+ var enumType = field.getNativeType();
+ if (enumType.hasOwnProperty(value)) {
+ return enumType[value];
+ }
+ }
+
+ // If it's a string containing a positive integer, this looks like a viable
+ // enum int value. Return as numeric.
+ if (goog.isString(value) &&
+ goog.proto2.Serializer.INTEGER_REGEX.test(value)) {
+ var numeric = Number(value);
+ if (numeric > 0) {
+ return numeric;
+ }
+ }
+
+ // Return unknown values as is for backward compatibility.
+ return value;
+ }
+
+ // Return the raw value if the field does not allow the JSON input to be
+ // converted.
+ if (!field.deserializationConversionPermitted()) {
+ return value;
+ }
+
+ // Convert to native type of field. Return the converted value or fall
+ // through to return the raw value. The JSON encoding of int64 value 123
+ // might be either the number 123 or the string "123". The field native type
+ // could be either Number or String (depending on field options in the .proto
+ // file). All four combinations should work correctly.
+ var nativeType = field.getNativeType();
+ if (nativeType === String) {
+ // JSON numbers can be converted to strings.
+ if (goog.isNumber(value)) {
+ return String(value);
+ }
+ } else if (nativeType === Number) {
+ // JSON strings are sometimes used for large integer numeric values, as well
+ // as Infinity, -Infinity and NaN.
+ if (goog.isString(value)) {
+ // Handle +/- Infinity and NaN values.
+ if (value === 'Infinity' || value === '-Infinity' || value === 'NaN') {
+ return Number(value);
+ }
+
+ // Validate the string. If the string is not an integral number, we would
+ // rather have an assertion or error in the caller than a mysterious NaN
+ // value.
+ if (goog.proto2.Serializer.INTEGER_REGEX.test(value)) {
+ return Number(value);
+ }
+ }
+ }
+
+ return value;
+};
+
+
+/** @const {!RegExp} */
+goog.proto2.Serializer.INTEGER_REGEX = /^-?[0-9]+$/;
diff --git a/chromium/third_party/ink/closure/reflect/reflect.js b/chromium/third_party/ink/closure/reflect/reflect.js
new file mode 100644
index 00000000000..b846fba6825
--- /dev/null
+++ b/chromium/third_party/ink/closure/reflect/reflect.js
@@ -0,0 +1,139 @@
+// Copyright 2009 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Useful compiler idioms.
+ *
+ * @author mgoodman@google.com (Mark Goodman)
+ * @author johnlenz@google.com (John Lenz)
+ */
+
+goog.provide('goog.reflect');
+
+
+/**
+ * Syntax for object literal casts.
+ * @see http://go/jscompiler-renaming
+ * @see https://goo.gl/CRs09P
+ *
+ * Use this if you have an object literal whose keys need to have the same names
+ * as the properties of some class even after they are renamed by the compiler.
+ *
+ * @param {!Function} type Type to cast to.
+ * @param {Object} object Object literal to cast.
+ * @return {Object} The object literal.
+ */
+goog.reflect.object = function(type, object) {
+ return object;
+};
+
+/**
+ * Syntax for renaming property strings.
+ * @see http://go/jscompiler-renaming
+ * @see https://goo.gl/CRs09P
+ *
+ * Use this if you have an need to access a property as a string, but want
+ * to also have the property renamed by the compiler. In contrast to
+ * goog.reflect.object, this method takes an instance of an object.
+ *
+ * Properties must be simple names (not qualified names).
+ *
+ * @param {string} prop Name of the property
+ * @param {!Object} object Instance of the object whose type will be used
+ * for renaming
+ * @return {string} The renamed property.
+ */
+goog.reflect.objectProperty = function(prop, object) {
+ return prop;
+};
+
+/**
+ * To assert to the compiler that an operation is needed when it would
+ * otherwise be stripped. For example:
+ * <code>
+ * // Force a layout
+ * goog.reflect.sinkValue(dialog.offsetHeight);
+ * </code>
+ * @param {T} x
+ * @return {T}
+ * @template T
+ */
+goog.reflect.sinkValue = function(x) {
+ goog.reflect.sinkValue[' '](x);
+ return x;
+};
+
+
+/**
+ * The compiler should optimize this function away iff no one ever uses
+ * goog.reflect.sinkValue.
+ */
+goog.reflect.sinkValue[' '] = goog.nullFunction;
+
+
+/**
+ * Check if a property can be accessed without throwing an exception.
+ * @param {Object} obj The owner of the property.
+ * @param {string} prop The property name.
+ * @return {boolean} Whether the property is accessible. Will also return true
+ * if obj is null.
+ */
+goog.reflect.canAccessProperty = function(obj, prop) {
+
+ try {
+ goog.reflect.sinkValue(obj[prop]);
+ return true;
+ } catch (e) {
+ }
+ return false;
+};
+
+
+/**
+ * Retrieves a value from a cache given a key. The compiler provides special
+ * consideration for this call such that it is generally considered side-effect
+ * free. However, if the {@code opt_keyFn} or {@code valueFn} have side-effects
+ * then the entire call is considered to have side-effects.
+ *
+ * Conventionally storing the value on the cache would be considered a
+ * side-effect and preclude unused calls from being pruned, ie. even if
+ * the value was never used, it would still always be stored in the cache.
+ *
+ * Providing a side-effect free {@code valueFn} and {@code opt_keyFn}
+ * allows unused calls to {@code goog.reflect.cache} to be pruned.
+ *
+ * @param {!Object<K, V>} cacheObj The object that contains the cached values.
+ * @param {?} key The key to lookup in the cache. If it is not string or number
+ * then a {@code opt_keyFn} should be provided. The key is also used as the
+ * parameter to the {@code valueFn}.
+ * @param {function(?):V} valueFn The value provider to use to calculate the
+ * value to store in the cache. This function should be side-effect free
+ * to take advantage of the optimization.
+ * @param {function(?):K=} opt_keyFn The key provider to determine the cache
+ * map key. This should be used if the given key is not a string or number.
+ * If not provided then the given key is used. This function should be
+ * side-effect free to take advantage of the optimization.
+ * @return {V} The cached or calculated value.
+ * @template K
+ * @template V
+ */
+goog.reflect.cache = function(cacheObj, key, valueFn, opt_keyFn) {
+ var storedKey = opt_keyFn ? opt_keyFn(key) : key;
+
+ if (Object.prototype.hasOwnProperty.call(cacheObj, storedKey)) {
+ return cacheObj[storedKey];
+ }
+
+ return (cacheObj[storedKey] = valueFn(key));
+};
diff --git a/chromium/third_party/ink/closure/soy/data.js b/chromium/third_party/ink/closure/soy/data.js
new file mode 100644
index 00000000000..24e330745e9
--- /dev/null
+++ b/chromium/third_party/ink/closure/soy/data.js
@@ -0,0 +1,525 @@
+// Copyright 2012 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Soy data primitives.
+ *
+ * The goal is to encompass data types used by Soy, especially to mark content
+ * as known to be "safe".
+ *
+ * @author gboyer@google.com (Garrett Boyer)
+ */
+
+goog.provide('goog.soy.data.SanitizedContent');
+goog.provide('goog.soy.data.SanitizedContentKind');
+goog.provide('goog.soy.data.SanitizedCss');
+goog.provide('goog.soy.data.SanitizedHtml');
+goog.provide('goog.soy.data.SanitizedHtmlAttribute');
+goog.provide('goog.soy.data.SanitizedJs');
+goog.provide('goog.soy.data.SanitizedStyle');
+goog.provide('goog.soy.data.SanitizedTrustedResourceUri');
+goog.provide('goog.soy.data.SanitizedUri');
+goog.provide('goog.soy.data.UnsanitizedText');
+
+goog.require('goog.Uri');
+goog.require('goog.asserts');
+goog.require('goog.html.SafeHtml');
+goog.require('goog.html.SafeScript');
+goog.require('goog.html.SafeStyle');
+goog.require('goog.html.SafeStyleSheet');
+goog.require('goog.html.SafeUrl');
+goog.require('goog.html.TrustedResourceUrl');
+goog.require('goog.html.uncheckedconversions');
+goog.require('goog.i18n.bidi.Dir');
+goog.require('goog.string.Const');
+
+
+/**
+ * A type of textual content.
+ *
+ * This is an enum of type Object so that these values are unforgeable.
+ *
+ * @enum {!Object}
+ */
+goog.soy.data.SanitizedContentKind = {
+
+ /**
+ * A snippet of HTML that does not start or end inside a tag, comment, entity,
+ * or DOCTYPE; and that does not contain any executable code
+ * (JS, {@code <object>}s, etc.) from a different trust domain.
+ */
+ HTML: goog.DEBUG ? {sanitizedContentKindHtml: true} : {},
+
+ /**
+ * Executable Javascript code or expression, safe for insertion in a
+ * script-tag or event handler context, known to be free of any
+ * attacker-controlled scripts. This can either be side-effect-free
+ * Javascript (such as JSON) or Javascript that's entirely under Google's
+ * control.
+ */
+ JS: goog.DEBUG ? {sanitizedContentJsChars: true} : {},
+
+ /** A properly encoded portion of a URI. */
+ URI: goog.DEBUG ? {sanitizedContentUri: true} : {},
+
+ /** A resource URI not under attacker control. */
+ TRUSTED_RESOURCE_URI:
+ goog.DEBUG ? {sanitizedContentTrustedResourceUri: true} : {},
+
+ /**
+ * Repeated attribute names and values. For example,
+ * {@code dir="ltr" foo="bar" onclick="trustedFunction()" checked}.
+ */
+ ATTRIBUTES: goog.DEBUG ? {sanitizedContentHtmlAttribute: true} : {},
+
+ // TODO: Consider separating rules, declarations, and values into
+ // separate types, but for simplicity, we'll treat explicitly blessed
+ // SanitizedContent as allowed in all of these contexts.
+ /**
+ * A CSS3 declaration, property, value or group of semicolon separated
+ * declarations.
+ */
+ STYLE: goog.DEBUG ? {sanitizedContentStyle: true} : {},
+
+ /** A CSS3 style sheet (list of rules). */
+ CSS: goog.DEBUG ? {sanitizedContentCss: true} : {},
+
+ /**
+ * Unsanitized plain-text content.
+ *
+ * This is effectively the "null" entry of this enum, and is sometimes used
+ * to explicitly mark content that should never be used unescaped. Since any
+ * string is safe to use as text, being of ContentKind.TEXT makes no
+ * guarantees about its safety in any other context such as HTML.
+ */
+ TEXT: goog.DEBUG ? {sanitizedContentKindText: true} : {}
+};
+
+
+
+/**
+ * A string-like object that carries a content-type and a content direction.
+ *
+ * IMPORTANT! Do not create these directly, nor instantiate the subclasses.
+ * Instead, use a trusted, centrally reviewed library as endorsed by your team
+ * to generate these objects. Otherwise, you risk accidentally creating
+ * SanitizedContent that is attacker-controlled and gets evaluated unescaped in
+ * templates.
+ *
+ * @constructor
+ */
+goog.soy.data.SanitizedContent = function() {
+ throw new Error('Do not instantiate directly');
+};
+
+
+/**
+ * The context in which this content is safe from XSS attacks.
+ * @type {goog.soy.data.SanitizedContentKind}
+ */
+goog.soy.data.SanitizedContent.prototype.contentKind;
+
+
+/**
+ * The content's direction; null if unknown and thus to be estimated when
+ * necessary.
+ * @type {?goog.i18n.bidi.Dir}
+ */
+goog.soy.data.SanitizedContent.prototype.contentDir = null;
+
+
+/**
+ * The already-safe content.
+ * @protected {string}
+ */
+goog.soy.data.SanitizedContent.prototype.content;
+
+
+/**
+ * Gets the already-safe content.
+ * @return {string}
+ */
+goog.soy.data.SanitizedContent.prototype.getContent = function() {
+ return this.content;
+};
+
+
+/** @override */
+goog.soy.data.SanitizedContent.prototype.toString = function() {
+ return this.content;
+};
+
+
+/**
+ * Converts sanitized content of kind TEXT or HTML into SafeHtml. HTML content
+ * is converted without modification, while text content is HTML-escaped.
+ * @return {!goog.html.SafeHtml}
+ * @throws {Error} when the content kind is not TEXT or HTML.
+ */
+goog.soy.data.SanitizedContent.prototype.toSafeHtml = function() {
+ if (this.contentKind === goog.soy.data.SanitizedContentKind.TEXT) {
+ return goog.html.SafeHtml.htmlEscape(this.toString());
+ }
+ if (this.contentKind !== goog.soy.data.SanitizedContentKind.HTML) {
+ throw new Error('Sanitized content was not of kind TEXT or HTML.');
+ }
+ return goog.html.uncheckedconversions
+ .safeHtmlFromStringKnownToSatisfyTypeContract(
+ goog.string.Const.from(
+ 'Soy SanitizedContent of kind HTML produces ' +
+ 'SafeHtml-contract-compliant value.'),
+ this.toString(), this.contentDir);
+};
+
+
+/**
+ * Converts sanitized content of kind URI into SafeUrl without modification.
+ * @return {!goog.html.SafeUrl}
+ * @throws {Error} when the content kind is not URI.
+ */
+goog.soy.data.SanitizedContent.prototype.toSafeUrl = function() {
+ if (this.contentKind !== goog.soy.data.SanitizedContentKind.URI) {
+ throw new Error('Sanitized content was not of kind URI.');
+ }
+ return goog.html.uncheckedconversions
+ .safeUrlFromStringKnownToSatisfyTypeContract(
+ goog.string.Const.from(
+ 'Soy SanitizedContent of kind URI produces ' +
+ 'SafeHtml-contract-compliant value.'),
+ this.toString());
+};
+
+
+/**
+ * Unsanitized plain text string.
+ *
+ * While all strings are effectively safe to use as a plain text, there are no
+ * guarantees about safety in any other context such as HTML. This is
+ * sometimes used to mark that should never be used unescaped.
+ *
+ * @param {*} content Plain text with no guarantees.
+ * @param {?goog.i18n.bidi.Dir=} opt_contentDir The content direction; null if
+ * unknown and thus to be estimated when necessary. Default: null.
+ * @extends {goog.soy.data.SanitizedContent}
+ * @constructor
+ */
+goog.soy.data.UnsanitizedText = function(content, opt_contentDir) {
+ // Not calling the superclass constructor which just throws an exception.
+
+ /** @override */
+ this.content = String(content);
+ this.contentDir = opt_contentDir != null ? opt_contentDir : null;
+};
+goog.inherits(goog.soy.data.UnsanitizedText, goog.soy.data.SanitizedContent);
+
+
+/** @override */
+goog.soy.data.UnsanitizedText.prototype.contentKind =
+ goog.soy.data.SanitizedContentKind.TEXT;
+
+
+
+/**
+ * Content of type {@link goog.soy.data.SanitizedContentKind.HTML}.
+ *
+ * The content is a string of HTML that can safely be embedded in a PCDATA
+ * context in your app. If you would be surprised to find that an HTML
+ * sanitizer produced {@code s} (e.g. it runs code or fetches bad URLs) and
+ * you wouldn't write a template that produces {@code s} on security or privacy
+ * grounds, then don't pass {@code s} here. The default content direction is
+ * unknown, i.e. to be estimated when necessary.
+ *
+ * @extends {goog.soy.data.SanitizedContent}
+ * @constructor
+ */
+goog.soy.data.SanitizedHtml = function() {
+ goog.soy.data.SanitizedHtml.base(this, 'constructor');
+};
+goog.inherits(goog.soy.data.SanitizedHtml, goog.soy.data.SanitizedContent);
+
+/** @override */
+goog.soy.data.SanitizedHtml.prototype.contentKind =
+ goog.soy.data.SanitizedContentKind.HTML;
+
+/**
+ * Checks if the value could be used as the Soy type {html}.
+ * @param {*} value
+ * @return {boolean}
+ */
+goog.soy.data.SanitizedHtml.isCompatibleWith = function(value) {
+ return goog.isString(value) ||
+ value instanceof goog.soy.data.SanitizedHtml ||
+ value instanceof goog.soy.data.UnsanitizedText ||
+ value instanceof goog.html.SafeHtml;
+};
+
+
+
+/**
+ * Content of type {@link goog.soy.data.SanitizedContentKind.JS}.
+ *
+ * The content is JavaScript source that when evaluated does not execute any
+ * attacker-controlled scripts. The content direction is LTR.
+ *
+ * @extends {goog.soy.data.SanitizedContent}
+ * @constructor
+ */
+goog.soy.data.SanitizedJs = function() {
+ goog.soy.data.SanitizedJs.base(this, 'constructor');
+};
+goog.inherits(goog.soy.data.SanitizedJs, goog.soy.data.SanitizedContent);
+
+/** @override */
+goog.soy.data.SanitizedJs.prototype.contentKind =
+ goog.soy.data.SanitizedContentKind.JS;
+
+/** @override */
+goog.soy.data.SanitizedJs.prototype.contentDir = goog.i18n.bidi.Dir.LTR;
+
+/**
+ * Checks if the value could be used as the Soy type {js}.
+ * @param {*} value
+ * @return {boolean}
+ */
+goog.soy.data.SanitizedJs.isCompatibleWith = function(value) {
+ return goog.isString(value) ||
+ value instanceof goog.soy.data.SanitizedJs ||
+ value instanceof goog.soy.data.UnsanitizedText ||
+ value instanceof goog.html.SafeScript;
+};
+
+
+
+/**
+ * Content of type {@link goog.soy.data.SanitizedContentKind.URI}.
+ *
+ * The content is a URI chunk that the caller knows is safe to emit in a
+ * template. The content direction is LTR.
+ *
+ * @extends {goog.soy.data.SanitizedContent}
+ * @constructor
+ */
+goog.soy.data.SanitizedUri = function() {
+ goog.soy.data.SanitizedUri.base(this, 'constructor');
+};
+goog.inherits(goog.soy.data.SanitizedUri, goog.soy.data.SanitizedContent);
+
+/** @override */
+goog.soy.data.SanitizedUri.prototype.contentKind =
+ goog.soy.data.SanitizedContentKind.URI;
+
+/** @override */
+goog.soy.data.SanitizedUri.prototype.contentDir = goog.i18n.bidi.Dir.LTR;
+
+/**
+ * Checks if the value could be used as the Soy type {uri}.
+ * @param {*} value
+ * @return {boolean}
+ */
+goog.soy.data.SanitizedUri.isCompatibleWith = function(value) {
+ return goog.isString(value) ||
+ value instanceof goog.soy.data.SanitizedUri ||
+ value instanceof goog.soy.data.UnsanitizedText ||
+ value instanceof goog.html.SafeUrl ||
+ value instanceof goog.html.TrustedResourceUrl ||
+ value instanceof goog.Uri;
+};
+
+
+
+/**
+ * Content of type
+ * {@link goog.soy.data.SanitizedContentKind.TRUSTED_RESOURCE_URI}.
+ *
+ * The content is a TrustedResourceUri chunk that is not under attacker control.
+ * The content direction is LTR.
+ *
+ * @extends {goog.soy.data.SanitizedContent}
+ * @constructor
+ */
+goog.soy.data.SanitizedTrustedResourceUri = function() {
+ goog.soy.data.SanitizedTrustedResourceUri.base(this, 'constructor');
+};
+goog.inherits(
+ goog.soy.data.SanitizedTrustedResourceUri, goog.soy.data.SanitizedContent);
+
+/** @override */
+goog.soy.data.SanitizedTrustedResourceUri.prototype.contentKind =
+ goog.soy.data.SanitizedContentKind.TRUSTED_RESOURCE_URI;
+
+/** @override */
+goog.soy.data.SanitizedTrustedResourceUri.prototype.contentDir =
+ goog.i18n.bidi.Dir.LTR;
+
+/**
+ * Converts sanitized content into TrustedResourceUrl without modification.
+ * @return {!goog.html.TrustedResourceUrl}
+ */
+goog.soy.data.SanitizedTrustedResourceUri.prototype.toTrustedResourceUrl =
+ function() {
+ return goog.html.uncheckedconversions
+ .trustedResourceUrlFromStringKnownToSatisfyTypeContract(
+ goog.string.Const.from(
+ 'Soy SanitizedContent of kind TRUSTED_RESOURCE_URI produces ' +
+ 'TrustedResourceUrl-contract-compliant value.'),
+ this.toString());
+};
+
+/**
+ * Checks if the value could be used as the Soy type {trusted_resource_uri}.
+ * @param {*} value
+ * @return {boolean}
+ */
+goog.soy.data.SanitizedTrustedResourceUri.isCompatibleWith = function(value) {
+ return goog.isString(value) ||
+ value instanceof goog.soy.data.SanitizedTrustedResourceUri ||
+ value instanceof goog.soy.data.UnsanitizedText ||
+ value instanceof goog.html.TrustedResourceUrl;
+};
+
+
+
+/**
+ * Content of type {@link goog.soy.data.SanitizedContentKind.ATTRIBUTES}.
+ *
+ * The content should be safely embeddable within an open tag, such as a
+ * key="value" pair. The content direction is LTR.
+ *
+ * @extends {goog.soy.data.SanitizedContent}
+ * @constructor
+ */
+goog.soy.data.SanitizedHtmlAttribute = function() {
+ goog.soy.data.SanitizedHtmlAttribute.base(this, 'constructor');
+};
+goog.inherits(
+ goog.soy.data.SanitizedHtmlAttribute, goog.soy.data.SanitizedContent);
+
+/** @override */
+goog.soy.data.SanitizedHtmlAttribute.prototype.contentKind =
+ goog.soy.data.SanitizedContentKind.ATTRIBUTES;
+
+/** @override */
+goog.soy.data.SanitizedHtmlAttribute.prototype.contentDir =
+ goog.i18n.bidi.Dir.LTR;
+
+/**
+ * Checks if the value could be used as the Soy type {attribute}.
+ * @param {*} value
+ * @return {boolean}
+ */
+goog.soy.data.SanitizedHtmlAttribute.isCompatibleWith = function(value) {
+ return goog.isString(value) ||
+ value instanceof goog.soy.data.SanitizedHtmlAttribute ||
+ value instanceof goog.soy.data.UnsanitizedText;
+};
+
+
+
+/**
+ * Content of type {@link goog.soy.data.SanitizedContentKind.STYLE}.
+ *
+ * The content is non-attacker-exploitable CSS, such as {@code color:#c3d9ff}.
+ * The content direction is LTR.
+ *
+ * @extends {goog.soy.data.SanitizedContent}
+ * @constructor
+ */
+goog.soy.data.SanitizedStyle = function() {
+ goog.soy.data.SanitizedStyle.base(this, 'constructor');
+};
+goog.inherits(goog.soy.data.SanitizedStyle, goog.soy.data.SanitizedContent);
+
+
+/** @override */
+goog.soy.data.SanitizedStyle.prototype.contentKind =
+ goog.soy.data.SanitizedContentKind.STYLE;
+
+
+/** @override */
+goog.soy.data.SanitizedStyle.prototype.contentDir = goog.i18n.bidi.Dir.LTR;
+
+
+/**
+ * Checks if the value could be used as the Soy type {css}.
+ * @param {*} value
+ * @return {boolean}
+ */
+goog.soy.data.SanitizedStyle.isCompatibleWith = function(value) {
+ return goog.isString(value) ||
+ value instanceof goog.soy.data.SanitizedStyle ||
+ value instanceof goog.soy.data.UnsanitizedText ||
+ value instanceof goog.html.SafeStyle;
+};
+
+
+
+/**
+ * Content of type {@link goog.soy.data.SanitizedContentKind.CSS}.
+ *
+ * The content is non-attacker-exploitable CSS, such as {@code @import url(x)}.
+ * The content direction is LTR.
+ *
+ * @extends {goog.soy.data.SanitizedContent}
+ * @constructor
+ */
+goog.soy.data.SanitizedCss = function() {
+ goog.soy.data.SanitizedCss.base(this, 'constructor');
+};
+goog.inherits(goog.soy.data.SanitizedCss, goog.soy.data.SanitizedContent);
+
+
+/** @override */
+goog.soy.data.SanitizedCss.prototype.contentKind =
+ goog.soy.data.SanitizedContentKind.CSS;
+
+
+/** @override */
+goog.soy.data.SanitizedCss.prototype.contentDir = goog.i18n.bidi.Dir.LTR;
+
+
+/**
+ * Checks if the value could be used as the Soy type {css}.
+ * @param {*} value
+ * @return {boolean}
+ */
+goog.soy.data.SanitizedCss.isCompatibleWith = function(value) {
+ return goog.isString(value) ||
+ value instanceof goog.soy.data.SanitizedCss ||
+ value instanceof goog.soy.data.UnsanitizedText ||
+ value instanceof goog.html.SafeStyle || // TODO(jakubvrana): Delete.
+ value instanceof goog.html.SafeStyleSheet;
+};
+
+
+/**
+ * Converts SanitizedCss into SafeStyleSheet.
+ * Note: SanitizedCss in Soy represents both SafeStyle and SafeStyleSheet in
+ * Closure. It's about to be split so that SanitizedCss represents only
+ * SafeStyleSheet.
+ * @return {!goog.html.SafeStyleSheet}
+ */
+goog.soy.data.SanitizedCss.prototype.toSafeStyleSheet = function() {
+ var value = this.toString();
+ // TODO(jakubvrana): Remove this check when there's a separate type for style
+ // declaration.
+ goog.asserts.assert(
+ /[@{]|^\s*$/.test(value),
+ 'value doesn\'t look like style sheet: ' + value);
+ return goog.html.uncheckedconversions
+ .safeStyleSheetFromStringKnownToSatisfyTypeContract(
+ goog.string.Const.from(
+ 'Soy SanitizedCss produces SafeStyleSheet-contract-compliant ' +
+ 'value.'),
+ value);
+};
diff --git a/chromium/third_party/ink/closure/soy/soy.js b/chromium/third_party/ink/closure/soy/soy.js
new file mode 100644
index 00000000000..e10dd5de635
--- /dev/null
+++ b/chromium/third_party/ink/closure/soy/soy.js
@@ -0,0 +1,298 @@
+// Copyright 2011 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Provides utility methods to render soy template.
+ * @author kai@google.com (Kai Huang)
+ * @author ptucker@google.com (Philip Tucker)
+ * @author chrishenry@google.com (Chris Henry)
+ */
+
+goog.provide('goog.soy');
+
+goog.require('goog.asserts');
+goog.require('goog.dom');
+goog.require('goog.dom.NodeType');
+goog.require('goog.dom.TagName');
+goog.require('goog.html.legacyconversions');
+goog.require('goog.soy.data.SanitizedContent');
+goog.require('goog.soy.data.SanitizedContentKind');
+goog.require('goog.string');
+
+
+/**
+ * @define {boolean} Whether to require all Soy templates to be "strict html".
+ * Soy templates that use strict autoescaping forbid noAutoescape along with
+ * many dangerous directives, and return a runtime type SanitizedContent that
+ * marks them as safe.
+ *
+ * If this flag is enabled, Soy templates will fail to render if a template
+ * returns plain text -- indicating it is a non-strict template.
+ */
+goog.define('goog.soy.REQUIRE_STRICT_AUTOESCAPE', false);
+
+
+/**
+ * Type definition for strict Soy templates. Very useful when passing a template
+ * as an argument.
+ * @typedef {function(?, null=, ?Object<string, *>=):
+ * !goog.soy.data.SanitizedContent}
+ */
+goog.soy.StrictTemplate;
+
+
+/**
+ * Type definition for strict Soy HTML templates. Very useful when passing
+ * a template as an argument.
+ * @typedef {function(?, null=, ?Object<string, *>=):
+ * !goog.soy.data.SanitizedHtml}
+ */
+goog.soy.StrictHtmlTemplate;
+
+
+/**
+ * Sets the processed template as the innerHTML of an element. It is recommended
+ * to use this helper function instead of directly setting innerHTML in your
+ * hand-written code, so that it will be easier to audit the code for cross-site
+ * scripting vulnerabilities.
+ *
+ * @param {?Element} element The element whose content we are rendering into.
+ * @param {!goog.soy.data.SanitizedContent} templateResult The processed
+ * template of kind HTML or TEXT (which will be escaped).
+ * @template ARG_TYPES
+ */
+goog.soy.renderHtml = function(element, templateResult) {
+ element.innerHTML = goog.soy.ensureTemplateOutputHtml_(templateResult);
+};
+
+
+/**
+ * Renders a Soy template and then set the output string as
+ * the innerHTML of an element. It is recommended to use this helper function
+ * instead of directly setting innerHTML in your hand-written code, so that it
+ * will be easier to audit the code for cross-site scripting vulnerabilities.
+ *
+ * @param {Element} element The element whose content we are rendering into.
+ * @param {?function(ARG_TYPES, Object<string, *>=):*|
+ * ?function(ARG_TYPES, null=, Object<string, *>=):*} template
+ * The Soy template defining the element's content.
+ * @param {ARG_TYPES=} opt_templateData The data for the template.
+ * @param {Object=} opt_injectedData The injected data for the template.
+ * @template ARG_TYPES
+ */
+goog.soy.renderElement = function(
+ element, template, opt_templateData, opt_injectedData) {
+ // Soy template parameter is only nullable for historical reasons.
+ goog.asserts.assert(template, 'Soy template may not be null.');
+ element.innerHTML = goog.soy.ensureTemplateOutputHtml_(
+ template(
+ opt_templateData || goog.soy.defaultTemplateData_, undefined,
+ opt_injectedData));
+};
+
+
+/**
+ * Renders a Soy template into a single node or a document
+ * fragment. If the rendered HTML string represents a single node, then that
+ * node is returned (note that this is *not* a fragment, despite them name of
+ * the method). Otherwise a document fragment is returned containing the
+ * rendered nodes.
+ *
+ * @param {?function(ARG_TYPES, Object<string, *>=):*|
+ * ?function(ARG_TYPES, null=, Object<string, *>=):*} template
+ * The Soy template defining the element's content.
+ * @param {ARG_TYPES=} opt_templateData The data for the template.
+ * @param {Object=} opt_injectedData The injected data for the template.
+ * @param {goog.dom.DomHelper=} opt_domHelper The DOM helper used to
+ * create DOM nodes; defaults to {@code goog.dom.getDomHelper}.
+ * @return {!Node} The resulting node or document fragment.
+ * @template ARG_TYPES
+ */
+goog.soy.renderAsFragment = function(
+ template, opt_templateData, opt_injectedData, opt_domHelper) {
+ // Soy template parameter is only nullable for historical reasons.
+ goog.asserts.assert(template, 'Soy template may not be null.');
+ var dom = opt_domHelper || goog.dom.getDomHelper();
+ var output = template(
+ opt_templateData || goog.soy.defaultTemplateData_, undefined,
+ opt_injectedData);
+ var html = goog.soy.ensureTemplateOutputHtml_(output);
+ goog.soy.assertFirstTagValid_(html);
+ var safeHtml = output instanceof goog.soy.data.SanitizedContent ?
+ output.toSafeHtml() :
+ goog.html.legacyconversions.safeHtmlFromString(html);
+ return dom.safeHtmlToNode(safeHtml);
+};
+
+
+/**
+ * Renders a Soy template into a single node. If the rendered
+ * HTML string represents a single node, then that node is returned. Otherwise,
+ * a DIV element is returned containing the rendered nodes.
+ *
+ * @param {?function(ARG_TYPES, Object<string, *>=):*|
+ * ?function(ARG_TYPES, null=, Object<string, *>=):*} template
+ * The Soy template defining the element's content.
+ * @param {ARG_TYPES=} opt_templateData The data for the template.
+ * @param {Object=} opt_injectedData The injected data for the template.
+ * @param {goog.dom.DomHelper=} opt_domHelper The DOM helper used to
+ * create DOM nodes; defaults to {@code goog.dom.getDomHelper}.
+ * @return {!Element} Rendered template contents, wrapped in a parent DIV
+ * element if necessary.
+ * @template ARG_TYPES
+ */
+goog.soy.renderAsElement = function(
+ template, opt_templateData, opt_injectedData, opt_domHelper) {
+ // Soy template parameter is only nullable for historical reasons.
+ goog.asserts.assert(template, 'Soy template may not be null.');
+ return goog.soy.convertToElement_(
+ template(
+ opt_templateData || goog.soy.defaultTemplateData_, undefined,
+ opt_injectedData),
+ opt_domHelper);
+};
+
+
+/**
+ * Converts a processed Soy template into a single node. If the rendered
+ * HTML string represents a single node, then that node is returned. Otherwise,
+ * a DIV element is returned containing the rendered nodes.
+ *
+ * @param {!goog.soy.data.SanitizedContent} templateResult The processed
+ * template of kind HTML or TEXT (which will be escaped).
+ * @param {?goog.dom.DomHelper=} opt_domHelper The DOM helper used to
+ * create DOM nodes; defaults to {@code goog.dom.getDomHelper}.
+ * @return {!Element} Rendered template contents, wrapped in a parent DIV
+ * element if necessary.
+ */
+goog.soy.convertToElement = function(templateResult, opt_domHelper) {
+ return goog.soy.convertToElement_(templateResult, opt_domHelper);
+};
+
+
+/**
+ * Non-strict version of {@code goog.soy.convertToElement}.
+ *
+ * @param {*} templateResult The processed template.
+ * @param {?goog.dom.DomHelper=} opt_domHelper The DOM helper used to
+ * create DOM nodes; defaults to {@code goog.dom.getDomHelper}.
+ * @return {!Element} Rendered template contents, wrapped in a parent DIV
+ * element if necessary.
+ * @private
+ */
+goog.soy.convertToElement_ = function(templateResult, opt_domHelper) {
+ var dom = opt_domHelper || goog.dom.getDomHelper();
+ var wrapper = dom.createElement(goog.dom.TagName.DIV);
+ var html = goog.soy.ensureTemplateOutputHtml_(templateResult);
+ goog.soy.assertFirstTagValid_(html);
+ wrapper.innerHTML = html;
+
+ // If the template renders as a single element, return it.
+ if (wrapper.childNodes.length == 1) {
+ var firstChild = wrapper.firstChild;
+ if (firstChild.nodeType == goog.dom.NodeType.ELEMENT) {
+ return /** @type {!Element} */ (firstChild);
+ }
+ }
+
+ // Otherwise, return the wrapper DIV.
+ return wrapper;
+};
+
+
+/**
+ * Ensures the result is "safe" to insert as HTML.
+ *
+ * Note if the template has non-strict autoescape, the guarantees here are very
+ * weak. It is recommended applications switch to requiring strict
+ * autoescaping over time by tweaking goog.soy.REQUIRE_STRICT_AUTOESCAPE.
+ *
+ * In the case the argument is a SanitizedContent object, it either must
+ * already be of kind HTML, or if it is kind="text", the output will be HTML
+ * escaped.
+ *
+ * @param {*} templateResult The template result.
+ * @return {string} The assumed-safe HTML output string.
+ * @private
+ */
+goog.soy.ensureTemplateOutputHtml_ = function(templateResult) {
+ // Allow strings as long as strict autoescaping is not mandated. Note we
+ // allow everything that isn't an object, because some non-escaping templates
+ // end up returning non-strings if their only print statement is a
+ // non-escaped argument, plus some unit tests spoof templates.
+ // TODO(gboyer): Track down and fix these cases.
+ if (!goog.soy.REQUIRE_STRICT_AUTOESCAPE && !goog.isObject(templateResult)) {
+ return String(templateResult);
+ }
+
+ // Allow SanitizedContent of kind HTML.
+ if (templateResult instanceof goog.soy.data.SanitizedContent) {
+ templateResult =
+ /** @type {!goog.soy.data.SanitizedContent} */ (templateResult);
+ var ContentKind = goog.soy.data.SanitizedContentKind;
+ if (templateResult.contentKind === ContentKind.HTML) {
+ return goog.asserts.assertString(templateResult.getContent());
+ }
+ if (templateResult.contentKind === ContentKind.TEXT) {
+ // Allow text to be rendered, as long as we escape it. Other content
+ // kinds will fail, since we don't know what to do with them.
+ // TODO(gboyer): Perhaps also include URI in this case.
+ return goog.string.htmlEscape(templateResult.getContent());
+ }
+ }
+
+ goog.asserts.fail(
+ 'Soy template output is unsafe for use as HTML: ' + templateResult);
+
+ // In production, return a safe string, rather than failing hard.
+ return 'zSoyz';
+};
+
+
+/**
+ * Checks that the rendered HTML does not start with an invalid tag that would
+ * likely cause unexpected output from renderAsElement or renderAsFragment.
+ * See {@link http://www.w3.org/TR/html5/semantics.html#semantics} for reference
+ * as to which HTML elements can be parents of each other.
+ * @param {string} html The output of a template.
+ * @private
+ */
+goog.soy.assertFirstTagValid_ = function(html) {
+ if (goog.asserts.ENABLE_ASSERTS) {
+ var matches = html.match(goog.soy.INVALID_TAG_TO_RENDER_);
+ goog.asserts.assert(
+ !matches, 'This template starts with a %s, which ' +
+ 'cannot be a child of a <div>, as required by soy internals. ' +
+ 'Consider using goog.soy.renderElement instead.\nTemplate output: %s',
+ matches && matches[0], html);
+ }
+};
+
+
+/**
+ * A pattern to find templates that cannot be rendered by renderAsElement or
+ * renderAsFragment, as these elements cannot exist as the child of a <div>.
+ * @type {!RegExp}
+ * @private
+ */
+goog.soy.INVALID_TAG_TO_RENDER_ =
+ /^<(body|caption|col|colgroup|head|html|tr|td|th|tbody|thead|tfoot)>/i;
+
+
+/**
+ * Immutable object that is passed into templates that are rendered
+ * without any data.
+ * @private @const
+ */
+goog.soy.defaultTemplateData_ = {};
diff --git a/chromium/third_party/ink/closure/string/const.js b/chromium/third_party/ink/closure/string/const.js
new file mode 100644
index 00000000000..30bfc4e40af
--- /dev/null
+++ b/chromium/third_party/ink/closure/string/const.js
@@ -0,0 +1,186 @@
+// Copyright 2013 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+goog.provide('goog.string.Const');
+
+goog.require('goog.asserts');
+goog.require('goog.string.TypedString');
+
+
+
+/**
+ * Wrapper for compile-time-constant strings.
+ *
+ * Const is a wrapper for strings that can only be created from program
+ * constants (i.e., string literals). This property relies on a custom Closure
+ * compiler check that {@code goog.string.Const.from} is only invoked on
+ * compile-time-constant expressions.
+ *
+ * Const is useful in APIs whose correct and secure use requires that certain
+ * arguments are not attacker controlled: Compile-time constants are inherently
+ * under the control of the application and not under control of external
+ * attackers, and hence are safe to use in such contexts.
+ *
+ * Instances of this type must be created via its factory method
+ * {@code goog.string.Const.from} and not by invoking its constructor. The
+ * constructor intentionally takes no parameters and the type is immutable;
+ * hence only a default instance corresponding to the empty string can be
+ * obtained via constructor invocation.
+ *
+ * @see goog.string.Const#from
+ * @constructor
+ * @final
+ * @struct
+ * @implements {goog.string.TypedString}
+ */
+goog.string.Const = function() {
+ /**
+ * The wrapped value of this Const object. The field has a purposely ugly
+ * name to make (non-compiled) code that attempts to directly access this
+ * field stand out.
+ * @private {string}
+ */
+ this.stringConstValueWithSecurityContract__googStringSecurityPrivate_ = '';
+
+ /**
+ * A type marker used to implement additional run-time type checking.
+ * @see goog.string.Const#unwrap
+ * @const {!Object}
+ * @private
+ */
+ this.STRING_CONST_TYPE_MARKER__GOOG_STRING_SECURITY_PRIVATE_ =
+ goog.string.Const.TYPE_MARKER_;
+};
+
+
+/**
+ * @override
+ * @const
+ */
+goog.string.Const.prototype.implementsGoogStringTypedString = true;
+
+
+/**
+ * Returns this Const's value a string.
+ *
+ * IMPORTANT: In code where it is security-relevant that an object's type is
+ * indeed {@code goog.string.Const}, use {@code goog.string.Const.unwrap}
+ * instead of this method.
+ *
+ * @see goog.string.Const#unwrap
+ * @override
+ */
+goog.string.Const.prototype.getTypedStringValue = function() {
+ return this.stringConstValueWithSecurityContract__googStringSecurityPrivate_;
+};
+
+
+/**
+ * Returns a debug-string representation of this value.
+ *
+ * To obtain the actual string value wrapped inside an object of this type,
+ * use {@code goog.string.Const.unwrap}.
+ *
+ * @see goog.string.Const#unwrap
+ * @override
+ */
+goog.string.Const.prototype.toString = function() {
+ return 'Const{' +
+ this.stringConstValueWithSecurityContract__googStringSecurityPrivate_ +
+ '}';
+};
+
+
+/**
+ * Performs a runtime check that the provided object is indeed an instance
+ * of {@code goog.string.Const}, and returns its value.
+ * @param {!goog.string.Const} stringConst The object to extract from.
+ * @return {string} The Const object's contained string, unless the run-time
+ * type check fails. In that case, {@code unwrap} returns an innocuous
+ * string, or, if assertions are enabled, throws
+ * {@code goog.asserts.AssertionError}.
+ */
+goog.string.Const.unwrap = function(stringConst) {
+ // Perform additional run-time type-checking to ensure that stringConst is
+ // indeed an instance of the expected type. This provides some additional
+ // protection against security bugs due to application code that disables type
+ // checks.
+ if (stringConst instanceof goog.string.Const &&
+ stringConst.constructor === goog.string.Const &&
+ stringConst.STRING_CONST_TYPE_MARKER__GOOG_STRING_SECURITY_PRIVATE_ ===
+ goog.string.Const.TYPE_MARKER_) {
+ return stringConst
+ .stringConstValueWithSecurityContract__googStringSecurityPrivate_;
+ } else {
+ goog.asserts.fail(
+ 'expected object of type Const, got \'' + stringConst + '\'');
+ return 'type_error:Const';
+ }
+};
+
+
+/**
+ * Creates a Const object from a compile-time constant string.
+ *
+ * It is illegal to invoke this function on an expression whose
+ * compile-time-contant value cannot be determined by the Closure compiler.
+ *
+ * Correct invocations include,
+ * <pre>
+ * var s = goog.string.Const.from('hello');
+ * var t = goog.string.Const.from('hello' + 'world');
+ * </pre>
+ *
+ * In contrast, the following are illegal:
+ * <pre>
+ * var s = goog.string.Const.from(getHello());
+ * var t = goog.string.Const.from('hello' + world);
+ * </pre>
+ *
+ * @param {string} s A constant string from which to create a Const.
+ * @return {!goog.string.Const} A Const object initialized to stringConst.
+ */
+goog.string.Const.from = function(s) {
+ return goog.string.Const.create__googStringSecurityPrivate_(s);
+};
+
+
+/**
+ * Type marker for the Const type, used to implement additional run-time
+ * type checking.
+ * @const {!Object}
+ * @private
+ */
+goog.string.Const.TYPE_MARKER_ = {};
+
+
+/**
+ * Utility method to create Const instances.
+ * @param {string} s The string to initialize the Const object with.
+ * @return {!goog.string.Const} The initialized Const object.
+ * @private
+ */
+goog.string.Const.create__googStringSecurityPrivate_ = function(s) {
+ var stringConst = new goog.string.Const();
+ stringConst.stringConstValueWithSecurityContract__googStringSecurityPrivate_ =
+ s;
+ return stringConst;
+};
+
+
+/**
+ * A Const instance wrapping the empty string.
+ * @const {!goog.string.Const}
+ */
+goog.string.Const.EMPTY = goog.string.Const.from('');
diff --git a/chromium/third_party/ink/closure/string/string.js b/chromium/third_party/ink/closure/string/string.js
new file mode 100644
index 00000000000..8e8c1c4f90c
--- /dev/null
+++ b/chromium/third_party/ink/closure/string/string.js
@@ -0,0 +1,1642 @@
+// Copyright 2006 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Utilities for string manipulation.
+ * @author pupius@google.com (Daniel Pupius)
+ * @author arv@google.com (Erik Arvidsson)
+ */
+
+
+/**
+ * Namespace for string utilities
+ */
+goog.provide('goog.string');
+goog.provide('goog.string.Unicode');
+
+
+/**
+ * @define {boolean} Enables HTML escaping of lowercase letter "e" which helps
+ * with detection of double-escaping as this letter is frequently used.
+ */
+goog.define('goog.string.DETECT_DOUBLE_ESCAPING', false);
+
+
+/**
+ * @define {boolean} Whether to force non-dom html unescaping.
+ */
+goog.define('goog.string.FORCE_NON_DOM_HTML_UNESCAPING', false);
+
+
+/**
+ * Common Unicode string characters.
+ * @enum {string}
+ */
+goog.string.Unicode = {
+ NBSP: '\xa0'
+};
+
+
+/**
+ * Fast prefix-checker.
+ * @param {string} str The string to check.
+ * @param {string} prefix A string to look for at the start of {@code str}.
+ * @return {boolean} True if {@code str} begins with {@code prefix}.
+ */
+goog.string.startsWith = function(str, prefix) {
+ return str.lastIndexOf(prefix, 0) == 0;
+};
+
+
+/**
+ * Fast suffix-checker.
+ * @param {string} str The string to check.
+ * @param {string} suffix A string to look for at the end of {@code str}.
+ * @return {boolean} True if {@code str} ends with {@code suffix}.
+ */
+goog.string.endsWith = function(str, suffix) {
+ var l = str.length - suffix.length;
+ return l >= 0 && str.indexOf(suffix, l) == l;
+};
+
+
+/**
+ * Case-insensitive prefix-checker.
+ * @param {string} str The string to check.
+ * @param {string} prefix A string to look for at the end of {@code str}.
+ * @return {boolean} True if {@code str} begins with {@code prefix} (ignoring
+ * case).
+ */
+goog.string.caseInsensitiveStartsWith = function(str, prefix) {
+ return goog.string.caseInsensitiveCompare(
+ prefix, str.substr(0, prefix.length)) == 0;
+};
+
+
+/**
+ * Case-insensitive suffix-checker.
+ * @param {string} str The string to check.
+ * @param {string} suffix A string to look for at the end of {@code str}.
+ * @return {boolean} True if {@code str} ends with {@code suffix} (ignoring
+ * case).
+ */
+goog.string.caseInsensitiveEndsWith = function(str, suffix) {
+ return (
+ goog.string.caseInsensitiveCompare(
+ suffix, str.substr(str.length - suffix.length, suffix.length)) == 0);
+};
+
+
+/**
+ * Case-insensitive equality checker.
+ * @param {string} str1 First string to check.
+ * @param {string} str2 Second string to check.
+ * @return {boolean} True if {@code str1} and {@code str2} are the same string,
+ * ignoring case.
+ */
+goog.string.caseInsensitiveEquals = function(str1, str2) {
+ return str1.toLowerCase() == str2.toLowerCase();
+};
+
+
+/**
+ * Does simple python-style string substitution.
+ * subs("foo%s hot%s", "bar", "dog") becomes "foobar hotdog".
+ * @param {string} str The string containing the pattern.
+ * @param {...*} var_args The items to substitute into the pattern.
+ * @return {string} A copy of {@code str} in which each occurrence of
+ * {@code %s} has been replaced an argument from {@code var_args}.
+ */
+goog.string.subs = function(str, var_args) {
+ var splitParts = str.split('%s');
+ var returnString = '';
+
+ var subsArguments = Array.prototype.slice.call(arguments, 1);
+ while (subsArguments.length &&
+ // Replace up to the last split part. We are inserting in the
+ // positions between split parts.
+ splitParts.length > 1) {
+ returnString += splitParts.shift() + subsArguments.shift();
+ }
+
+ return returnString + splitParts.join('%s'); // Join unused '%s'
+};
+
+
+/**
+ * Converts multiple whitespace chars (spaces, non-breaking-spaces, new lines
+ * and tabs) to a single space, and strips leading and trailing whitespace.
+ * @param {string} str Input string.
+ * @return {string} A copy of {@code str} with collapsed whitespace.
+ */
+goog.string.collapseWhitespace = function(str) {
+ // Since IE doesn't include non-breaking-space (0xa0) in their \s character
+ // class (as required by section 7.2 of the ECMAScript spec), we explicitly
+ // include it in the regexp to enforce consistent cross-browser behavior.
+ return str.replace(/[\s\xa0]+/g, ' ').replace(/^\s+|\s+$/g, '');
+};
+
+
+/**
+ * Checks if a string is empty or contains only whitespaces.
+ * @param {string} str The string to check.
+ * @return {boolean} Whether {@code str} is empty or whitespace only.
+ */
+goog.string.isEmptyOrWhitespace = function(str) {
+ // testing length == 0 first is actually slower in all browsers (about the
+ // same in Opera).
+ // Since IE doesn't include non-breaking-space (0xa0) in their \s character
+ // class (as required by section 7.2 of the ECMAScript spec), we explicitly
+ // include it in the regexp to enforce consistent cross-browser behavior.
+ return /^[\s\xa0]*$/.test(str);
+};
+
+
+/**
+ * Checks if a string is empty.
+ * @param {string} str The string to check.
+ * @return {boolean} Whether {@code str} is empty.
+ */
+goog.string.isEmptyString = function(str) {
+ return str.length == 0;
+};
+
+
+/**
+ * Checks if a string is empty or contains only whitespaces.
+ *
+ * @param {string} str The string to check.
+ * @return {boolean} Whether {@code str} is empty or whitespace only.
+ * @deprecated Use goog.string.isEmptyOrWhitespace instead.
+ */
+goog.string.isEmpty = goog.string.isEmptyOrWhitespace;
+
+
+/**
+ * Checks if a string is null, undefined, empty or contains only whitespaces.
+ * @param {*} str The string to check.
+ * @return {boolean} Whether {@code str} is null, undefined, empty, or
+ * whitespace only.
+ * @deprecated Use goog.string.isEmptyOrWhitespace(goog.string.makeSafe(str))
+ * instead.
+ */
+goog.string.isEmptyOrWhitespaceSafe = function(str) {
+ return goog.string.isEmptyOrWhitespace(goog.string.makeSafe(str));
+};
+
+
+/**
+ * Checks if a string is null, undefined, empty or contains only whitespaces.
+ *
+ * @param {*} str The string to check.
+ * @return {boolean} Whether {@code str} is null, undefined, empty, or
+ * whitespace only.
+ * @deprecated Use goog.string.isEmptyOrWhitespace instead.
+ */
+goog.string.isEmptySafe = goog.string.isEmptyOrWhitespaceSafe;
+
+
+/**
+ * Checks if a string is all breaking whitespace.
+ * @param {string} str The string to check.
+ * @return {boolean} Whether the string is all breaking whitespace.
+ */
+goog.string.isBreakingWhitespace = function(str) {
+ return !/[^\t\n\r ]/.test(str);
+};
+
+
+/**
+ * Checks if a string contains all letters.
+ * @param {string} str string to check.
+ * @return {boolean} True if {@code str} consists entirely of letters.
+ */
+goog.string.isAlpha = function(str) {
+ return !/[^a-zA-Z]/.test(str);
+};
+
+
+/**
+ * Checks if a string contains only numbers.
+ * @param {*} str string to check. If not a string, it will be
+ * casted to one.
+ * @return {boolean} True if {@code str} is numeric.
+ */
+goog.string.isNumeric = function(str) {
+ return !/[^0-9]/.test(str);
+};
+
+
+/**
+ * Checks if a string contains only numbers or letters.
+ * @param {string} str string to check.
+ * @return {boolean} True if {@code str} is alphanumeric.
+ */
+goog.string.isAlphaNumeric = function(str) {
+ return !/[^a-zA-Z0-9]/.test(str);
+};
+
+
+/**
+ * Checks if a character is a space character.
+ * @param {string} ch Character to check.
+ * @return {boolean} True if {@code ch} is a space.
+ */
+goog.string.isSpace = function(ch) {
+ return ch == ' ';
+};
+
+
+/**
+ * Checks if a character is a valid unicode character.
+ * @param {string} ch Character to check.
+ * @return {boolean} True if {@code ch} is a valid unicode character.
+ */
+goog.string.isUnicodeChar = function(ch) {
+ return ch.length == 1 && ch >= ' ' && ch <= '~' ||
+ ch >= '\u0080' && ch <= '\uFFFD';
+};
+
+
+/**
+ * Takes a string and replaces newlines with a space. Multiple lines are
+ * replaced with a single space.
+ * @param {string} str The string from which to strip newlines.
+ * @return {string} A copy of {@code str} stripped of newlines.
+ */
+goog.string.stripNewlines = function(str) {
+ return str.replace(/(\r\n|\r|\n)+/g, ' ');
+};
+
+
+/**
+ * Replaces Windows and Mac new lines with unix style: \r or \r\n with \n.
+ * @param {string} str The string to in which to canonicalize newlines.
+ * @return {string} {@code str} A copy of {@code} with canonicalized newlines.
+ */
+goog.string.canonicalizeNewlines = function(str) {
+ return str.replace(/(\r\n|\r|\n)/g, '\n');
+};
+
+
+/**
+ * Normalizes whitespace in a string, replacing all whitespace chars with
+ * a space.
+ * @param {string} str The string in which to normalize whitespace.
+ * @return {string} A copy of {@code str} with all whitespace normalized.
+ */
+goog.string.normalizeWhitespace = function(str) {
+ return str.replace(/\xa0|\s/g, ' ');
+};
+
+
+/**
+ * Normalizes spaces in a string, replacing all consecutive spaces and tabs
+ * with a single space. Replaces non-breaking space with a space.
+ * @param {string} str The string in which to normalize spaces.
+ * @return {string} A copy of {@code str} with all consecutive spaces and tabs
+ * replaced with a single space.
+ */
+goog.string.normalizeSpaces = function(str) {
+ return str.replace(/\xa0|[ \t]+/g, ' ');
+};
+
+
+/**
+ * Removes the breaking spaces from the left and right of the string and
+ * collapses the sequences of breaking spaces in the middle into single spaces.
+ * The original and the result strings render the same way in HTML.
+ * @param {string} str A string in which to collapse spaces.
+ * @return {string} Copy of the string with normalized breaking spaces.
+ */
+goog.string.collapseBreakingSpaces = function(str) {
+ return str.replace(/[\t\r\n ]+/g, ' ')
+ .replace(/^[\t\r\n ]+|[\t\r\n ]+$/g, '');
+};
+
+
+/**
+ * Trims white spaces to the left and right of a string.
+ * @param {string} str The string to trim.
+ * @return {string} A trimmed copy of {@code str}.
+ */
+goog.string.trim =
+ (goog.TRUSTED_SITE && String.prototype.trim) ? function(str) {
+ return str.trim();
+ } : function(str) {
+ // Since IE doesn't include non-breaking-space (0xa0) in their \s
+ // character class (as required by section 7.2 of the ECMAScript spec),
+ // we explicitly include it in the regexp to enforce consistent
+ // cross-browser behavior.
+ return str.replace(/^[\s\xa0]+|[\s\xa0]+$/g, '');
+ };
+
+
+/**
+ * Trims whitespaces at the left end of a string.
+ * @param {string} str The string to left trim.
+ * @return {string} A trimmed copy of {@code str}.
+ */
+goog.string.trimLeft = function(str) {
+ // Since IE doesn't include non-breaking-space (0xa0) in their \s character
+ // class (as required by section 7.2 of the ECMAScript spec), we explicitly
+ // include it in the regexp to enforce consistent cross-browser behavior.
+ return str.replace(/^[\s\xa0]+/, '');
+};
+
+
+/**
+ * Trims whitespaces at the right end of a string.
+ * @param {string} str The string to right trim.
+ * @return {string} A trimmed copy of {@code str}.
+ */
+goog.string.trimRight = function(str) {
+ // Since IE doesn't include non-breaking-space (0xa0) in their \s character
+ // class (as required by section 7.2 of the ECMAScript spec), we explicitly
+ // include it in the regexp to enforce consistent cross-browser behavior.
+ return str.replace(/[\s\xa0]+$/, '');
+};
+
+
+/**
+ * A string comparator that ignores case.
+ * -1 = str1 less than str2
+ * 0 = str1 equals str2
+ * 1 = str1 greater than str2
+ *
+ * @param {string} str1 The string to compare.
+ * @param {string} str2 The string to compare {@code str1} to.
+ * @return {number} The comparator result, as described above.
+ */
+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;
+ }
+};
+
+
+/**
+ * Compares two strings interpreting their numeric substrings as numbers.
+ *
+ * @param {string} str1 First string.
+ * @param {string} str2 Second string.
+ * @param {!RegExp} tokenizerRegExp Splits a string into substrings of
+ * non-negative integers, non-numeric characters and optionally fractional
+ * numbers starting with a decimal point.
+ * @return {number} Negative if str1 < str2, 0 is str1 == str2, positive if
+ * str1 > str2.
+ * @private
+ */
+goog.string.numberAwareCompare_ = function(str1, str2, tokenizerRegExp) {
+ if (str1 == str2) {
+ return 0;
+ }
+ if (!str1) {
+ return -1;
+ }
+ if (!str2) {
+ return 1;
+ }
+
+ // Using match to split the entire string ahead of time turns out to be faster
+ // for most inputs than using RegExp.exec or iterating over each character.
+ var tokens1 = str1.toLowerCase().match(tokenizerRegExp);
+ var tokens2 = str2.toLowerCase().match(tokenizerRegExp);
+
+ var count = Math.min(tokens1.length, tokens2.length);
+
+ for (var i = 0; i < count; i++) {
+ var a = tokens1[i];
+ var b = tokens2[i];
+
+ // Compare pairs of tokens, returning if one token sorts before the other.
+ if (a != b) {
+ // Only if both tokens are integers is a special comparison required.
+ // Decimal numbers are sorted as strings (e.g., '.09' < '.1').
+ 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 one string is a substring of the other, the shorter string sorts first.
+ if (tokens1.length != tokens2.length) {
+ return tokens1.length - tokens2.length;
+ }
+
+ // The two strings must be equivalent except for case (perfect equality is
+ // tested at the head of the function.) Revert to default ASCII string
+ // comparison to stabilize the sort.
+ return str1 < str2 ? -1 : 1;
+};
+
+
+/**
+ * String comparison function that handles non-negative integer numbers in a
+ * way humans might expect. Using this function, the string 'File 2.jpg' sorts
+ * before 'File 10.jpg', and 'Version 1.9' before 'Version 1.10'. The comparison
+ * is mostly case-insensitive, though strings that are identical except for case
+ * are sorted with the upper-case strings before lower-case.
+ *
+ * This comparison function is up to 50x slower than either the default or the
+ * case-insensitive compare. It should not be used in time-critical code, but
+ * should be fast enough to sort several hundred short strings (like filenames)
+ * with a reasonable delay.
+ *
+ * @param {string} str1 The string to compare in a numerically sensitive way.
+ * @param {string} str2 The string to compare {@code str1} to.
+ * @return {number} less than 0 if str1 < str2, 0 if str1 == str2, greater than
+ * 0 if str1 > str2.
+ */
+goog.string.intAwareCompare = function(str1, str2) {
+ return goog.string.numberAwareCompare_(str1, str2, /\d+|\D+/g);
+};
+
+
+/**
+ * String comparison function that handles non-negative integer and fractional
+ * numbers in a way humans might expect. Using this function, the string
+ * 'File 2.jpg' sorts before 'File 10.jpg', and '3.14' before '3.2'. Equivalent
+ * to {@link goog.string.intAwareCompare} apart from the way how it interprets
+ * dots.
+ *
+ * @param {string} str1 The string to compare in a numerically sensitive way.
+ * @param {string} str2 The string to compare {@code str1} to.
+ * @return {number} less than 0 if str1 < str2, 0 if str1 == str2, greater than
+ * 0 if str1 > str2.
+ */
+goog.string.floatAwareCompare = function(str1, str2) {
+ return goog.string.numberAwareCompare_(str1, str2, /\d+|\.\d+|\D+/g);
+};
+
+
+/**
+ * Alias for {@link goog.string.floatAwareCompare}.
+ *
+ * @param {string} str1
+ * @param {string} str2
+ * @return {number}
+ */
+goog.string.numerateCompare = goog.string.floatAwareCompare;
+
+
+/**
+ * URL-encodes a string
+ * @param {*} str The string to url-encode.
+ * @return {string} An encoded copy of {@code str} that is safe for urls.
+ * Note that '#', ':', and other characters used to delimit portions
+ * of URLs *will* be encoded.
+ */
+goog.string.urlEncode = function(str) {
+ return encodeURIComponent(String(str));
+};
+
+
+/**
+ * URL-decodes the string. We need to specially handle '+'s because
+ * the javascript library doesn't convert them to spaces.
+ * @param {string} str The string to url decode.
+ * @return {string} The decoded {@code str}.
+ */
+goog.string.urlDecode = function(str) {
+ return decodeURIComponent(str.replace(/\+/g, ' '));
+};
+
+
+/**
+ * Converts \n to <br>s or <br />s.
+ * @param {string} str The string in which to convert newlines.
+ * @param {boolean=} opt_xml Whether to use XML compatible tags.
+ * @return {string} A copy of {@code str} with converted newlines.
+ */
+goog.string.newLineToBr = function(str, opt_xml) {
+ return str.replace(/(\r\n|\r|\n)/g, opt_xml ? '<br />' : '<br>');
+};
+
+
+/**
+ * Escapes double quote '"' and single quote '\'' characters in addition to
+ * '&', '<', and '>' so that a string can be included in an HTML tag attribute
+ * value within double or single quotes.
+ *
+ * It should be noted that > doesn't need to be escaped for the HTML or XML to
+ * be valid, but it has been decided to escape it for consistency with other
+ * implementations.
+ *
+ * With goog.string.DETECT_DOUBLE_ESCAPING, this function escapes also the
+ * lowercase letter "e".
+ *
+ * NOTE(pupius):
+ * HtmlEscape is often called during the generation of large blocks of HTML.
+ * Using statics for the regular expressions and strings is an optimization
+ * that can more than half the amount of time IE spends in this function for
+ * large apps, since strings and regexes both contribute to GC allocations.
+ *
+ * Testing for the presence of a character before escaping increases the number
+ * of function calls, but actually provides a speed increase for the average
+ * case -- since the average case often doesn't require the escaping of all 4
+ * characters and indexOf() is much cheaper than replace().
+ * The worst case does suffer slightly from the additional calls, therefore the
+ * opt_isLikelyToContainHtmlChars option has been included for situations
+ * where all 4 HTML entities are very likely to be present and need escaping.
+ *
+ * Some benchmarks (times tended to fluctuate +-0.05ms):
+ * FireFox IE6
+ * (no chars / average (mix of cases) / all 4 chars)
+ * no checks 0.13 / 0.22 / 0.22 0.23 / 0.53 / 0.80
+ * indexOf 0.08 / 0.17 / 0.26 0.22 / 0.54 / 0.84
+ * indexOf + re test 0.07 / 0.17 / 0.28 0.19 / 0.50 / 0.85
+ *
+ * An additional advantage of checking if replace actually needs to be called
+ * is a reduction in the number of object allocations, so as the size of the
+ * application grows the difference between the various methods would increase.
+ *
+ * @param {string} str string to be escaped.
+ * @param {boolean=} opt_isLikelyToContainHtmlChars Don't perform a check to see
+ * if the character needs replacing - use this option if you expect each of
+ * the characters to appear often. Leave false if you expect few html
+ * characters to occur in your strings, such as if you are escaping HTML.
+ * @return {string} An escaped copy of {@code str}.
+ */
+goog.string.htmlEscape = function(str, opt_isLikelyToContainHtmlChars) {
+
+ if (opt_isLikelyToContainHtmlChars) {
+ str = str.replace(goog.string.AMP_RE_, '&amp;')
+ .replace(goog.string.LT_RE_, '&lt;')
+ .replace(goog.string.GT_RE_, '&gt;')
+ .replace(goog.string.QUOT_RE_, '&quot;')
+ .replace(goog.string.SINGLE_QUOTE_RE_, '&#39;')
+ .replace(goog.string.NULL_RE_, '&#0;');
+ if (goog.string.DETECT_DOUBLE_ESCAPING) {
+ str = str.replace(goog.string.E_RE_, '&#101;');
+ }
+ return str;
+
+ } else {
+ // quick test helps in the case when there are no chars to replace, in
+ // worst case this makes barely a difference to the time taken
+ if (!goog.string.ALL_RE_.test(str)) return str;
+
+ // str.indexOf is faster than regex.test in this case
+ if (str.indexOf('&') != -1) {
+ str = str.replace(goog.string.AMP_RE_, '&amp;');
+ }
+ if (str.indexOf('<') != -1) {
+ str = str.replace(goog.string.LT_RE_, '&lt;');
+ }
+ if (str.indexOf('>') != -1) {
+ str = str.replace(goog.string.GT_RE_, '&gt;');
+ }
+ if (str.indexOf('"') != -1) {
+ str = str.replace(goog.string.QUOT_RE_, '&quot;');
+ }
+ if (str.indexOf('\'') != -1) {
+ str = str.replace(goog.string.SINGLE_QUOTE_RE_, '&#39;');
+ }
+ if (str.indexOf('\x00') != -1) {
+ str = str.replace(goog.string.NULL_RE_, '&#0;');
+ }
+ if (goog.string.DETECT_DOUBLE_ESCAPING && str.indexOf('e') != -1) {
+ str = str.replace(goog.string.E_RE_, '&#101;');
+ }
+ return str;
+ }
+};
+
+
+/**
+ * Regular expression that matches an ampersand, for use in escaping.
+ * @const {!RegExp}
+ * @private
+ */
+goog.string.AMP_RE_ = /&/g;
+
+
+/**
+ * Regular expression that matches a less than sign, for use in escaping.
+ * @const {!RegExp}
+ * @private
+ */
+goog.string.LT_RE_ = /</g;
+
+
+/**
+ * Regular expression that matches a greater than sign, for use in escaping.
+ * @const {!RegExp}
+ * @private
+ */
+goog.string.GT_RE_ = />/g;
+
+
+/**
+ * Regular expression that matches a double quote, for use in escaping.
+ * @const {!RegExp}
+ * @private
+ */
+goog.string.QUOT_RE_ = /"/g;
+
+
+/**
+ * Regular expression that matches a single quote, for use in escaping.
+ * @const {!RegExp}
+ * @private
+ */
+goog.string.SINGLE_QUOTE_RE_ = /'/g;
+
+
+/**
+ * Regular expression that matches null character, for use in escaping.
+ * @const {!RegExp}
+ * @private
+ */
+goog.string.NULL_RE_ = /\x00/g;
+
+
+/**
+ * Regular expression that matches a lowercase letter "e", for use in escaping.
+ * @const {!RegExp}
+ * @private
+ */
+goog.string.E_RE_ = /e/g;
+
+
+/**
+ * Regular expression that matches any character that needs to be escaped.
+ * @const {!RegExp}
+ * @private
+ */
+goog.string.ALL_RE_ =
+ (goog.string.DETECT_DOUBLE_ESCAPING ? /[\x00&<>"'e]/ : /[\x00&<>"']/);
+
+
+/**
+ * Unescapes an HTML string.
+ *
+ * @param {string} str The string to unescape.
+ * @return {string} An unescaped copy of {@code str}.
+ */
+goog.string.unescapeEntities = function(str) {
+ if (goog.string.contains(str, '&')) {
+ // We are careful not to use a DOM if we do not have one or we explicitly
+ // requested non-DOM html unescaping.
+ if (!goog.string.FORCE_NON_DOM_HTML_UNESCAPING &&
+ 'document' in goog.global) {
+ return goog.string.unescapeEntitiesUsingDom_(str);
+ } else {
+ // Fall back on pure XML entities
+ return goog.string.unescapePureXmlEntities_(str);
+ }
+ }
+ return str;
+};
+
+
+/**
+ * Unescapes a HTML string using the provided document.
+ *
+ * @param {string} str The string to unescape.
+ * @param {!Document} document A document to use in escaping the string.
+ * @return {string} An unescaped copy of {@code str}.
+ */
+goog.string.unescapeEntitiesWithDocument = function(str, document) {
+ if (goog.string.contains(str, '&')) {
+ return goog.string.unescapeEntitiesUsingDom_(str, document);
+ }
+ return str;
+};
+
+
+/**
+ * Unescapes an HTML string using a DOM to resolve non-XML, non-numeric
+ * entities. This function is XSS-safe and whitespace-preserving.
+ * @private
+ * @param {string} str The string to unescape.
+ * @param {Document=} opt_document An optional document to use for creating
+ * elements. If this is not specified then the default window.document
+ * will be used.
+ * @return {string} The unescaped {@code str} string.
+ */
+goog.string.unescapeEntitiesUsingDom_ = function(str, opt_document) {
+ /** @type {!Object<string, string>} */
+ var seen = {'&amp;': '&', '&lt;': '<', '&gt;': '>', '&quot;': '"'};
+ var div;
+ if (opt_document) {
+ div = opt_document.createElement('div');
+ } else {
+ div = goog.global.document.createElement('div');
+ }
+ // Match as many valid entity characters as possible. If the actual entity
+ // happens to be shorter, it will still work as innerHTML will return the
+ // trailing characters unchanged. Since the entity characters do not include
+ // open angle bracket, there is no chance of XSS from the innerHTML use.
+ // Since no whitespace is passed to innerHTML, whitespace is preserved.
+ return str.replace(goog.string.HTML_ENTITY_PATTERN_, function(s, entity) {
+ // Check for cached entity.
+ var value = seen[s];
+ if (value) {
+ return value;
+ }
+ // Check for numeric entity.
+ if (entity.charAt(0) == '#') {
+ // Prefix with 0 so that hex entities (e.g. &#x10) parse as hex numbers.
+ var n = Number('0' + entity.substr(1));
+ if (!isNaN(n)) {
+ value = String.fromCharCode(n);
+ }
+ }
+ // Fall back to innerHTML otherwise.
+ if (!value) {
+ // Append a non-entity character to avoid a bug in Webkit that parses
+ // an invalid entity at the end of innerHTML text as the empty string.
+ div.innerHTML = s + ' ';
+ // Then remove the trailing character from the result.
+ value = div.firstChild.nodeValue.slice(0, -1);
+ }
+ // Cache and return.
+ return seen[s] = value;
+ });
+};
+
+
+/**
+ * Unescapes XML entities.
+ * @private
+ * @param {string} str The string to unescape.
+ * @return {string} An unescaped copy of {@code 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) == '#') {
+ // Prefix with 0 so that hex entities (e.g. &#x10) parse as hex.
+ var n = Number('0' + entity.substr(1));
+ if (!isNaN(n)) {
+ return String.fromCharCode(n);
+ }
+ }
+ // For invalid entities we just return the entity
+ return s;
+ }
+ });
+};
+
+
+/**
+ * Regular expression that matches an HTML entity.
+ * See also HTML5: Tokenization / Tokenizing character references.
+ * @private
+ * @type {!RegExp}
+ */
+goog.string.HTML_ENTITY_PATTERN_ = /&([^;\s<&]+);?/g;
+
+
+/**
+ * Do escaping of whitespace to preserve spatial formatting. We use character
+ * entity #160 to make it safer for xml.
+ * @param {string} str The string in which to escape whitespace.
+ * @param {boolean=} opt_xml Whether to use XML compatible tags.
+ * @return {string} An escaped copy of {@code str}.
+ */
+goog.string.whitespaceEscape = function(str, opt_xml) {
+ // This doesn't use goog.string.preserveSpaces for backwards compatibility.
+ return goog.string.newLineToBr(str.replace(/ /g, ' &#160;'), opt_xml);
+};
+
+
+/**
+ * Preserve spaces that would be otherwise collapsed in HTML by replacing them
+ * with non-breaking space Unicode characters.
+ * @param {string} str The string in which to preserve whitespace.
+ * @return {string} A copy of {@code str} with preserved whitespace.
+ */
+goog.string.preserveSpaces = function(str) {
+ return str.replace(/(^|[\n ]) /g, '$1' + goog.string.Unicode.NBSP);
+};
+
+
+/**
+ * Strip quote characters around a string. The second argument is a string of
+ * characters to treat as quotes. This can be a single character or a string of
+ * multiple character and in that case each of those are treated as possible
+ * quote characters. For example:
+ *
+ * <pre>
+ * goog.string.stripQuotes('"abc"', '"`') --> 'abc'
+ * goog.string.stripQuotes('`abc`', '"`') --> 'abc'
+ * </pre>
+ *
+ * @param {string} str The string to strip.
+ * @param {string} quoteChars The quote characters to strip.
+ * @return {string} A copy of {@code str} without the quotes.
+ */
+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;
+};
+
+
+/**
+ * Truncates a string to a certain length and adds '...' if necessary. The
+ * length also accounts for the ellipsis, so a maximum length of 10 and a string
+ * 'Hello World!' produces 'Hello W...'.
+ * @param {string} str The string to truncate.
+ * @param {number} chars Max number of characters.
+ * @param {boolean=} opt_protectEscapedCharacters Whether to protect escaped
+ * characters from being cut off in the middle.
+ * @return {string} The truncated {@code str} string.
+ */
+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;
+};
+
+
+/**
+ * Truncate a string in the middle, adding "..." if necessary,
+ * and favoring the beginning of the string.
+ * @param {string} str The string to truncate the middle of.
+ * @param {number} chars Max number of characters.
+ * @param {boolean=} opt_protectEscapedCharacters Whether to protect escaped
+ * characters from being cutoff in the middle.
+ * @param {number=} opt_trailingChars Optional number of trailing characters to
+ * leave at the end of the string, instead of truncating as close to the
+ * middle as possible.
+ * @return {string} A truncated copy of {@code str}.
+ */
+goog.string.truncateMiddle = function(
+ str, chars, opt_protectEscapedCharacters, opt_trailingChars) {
+ if (opt_protectEscapedCharacters) {
+ str = goog.string.unescapeEntities(str);
+ }
+
+ if (opt_trailingChars && str.length > chars) {
+ if (opt_trailingChars > chars) {
+ opt_trailingChars = chars;
+ }
+ var endPoint = str.length - opt_trailingChars;
+ var startPoint = chars - opt_trailingChars;
+ str = str.substring(0, startPoint) + '...' + str.substring(endPoint);
+ } else if (str.length > chars) {
+ // Favor the beginning of the string:
+ 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;
+};
+
+
+/**
+ * Special chars that need to be escaped for goog.string.quote.
+ * @private {!Object<string, string>}
+ */
+goog.string.specialEscapeChars_ = {
+ '\0': '\\0',
+ '\b': '\\b',
+ '\f': '\\f',
+ '\n': '\\n',
+ '\r': '\\r',
+ '\t': '\\t',
+ '\x0B': '\\x0B', // '\v' is not supported in JScript
+ '"': '\\"',
+ '\\': '\\\\',
+ // To support the use case of embedding quoted strings inside of script
+ // tags, we have to make sure HTML comments and opening/closing script tags do
+ // not appear in the resulting string. The specific strings that must be
+ // escaped are documented at:
+ // http://www.w3.org/TR/html51/semantics.html#restrictions-for-contents-of-script-elements
+ '<': '\x3c'
+};
+
+
+/**
+ * Character mappings used internally for goog.string.escapeChar.
+ * @private {!Object<string, string>}
+ */
+goog.string.jsEscapeCache_ = {
+ '\'': '\\\''
+};
+
+
+/**
+ * Encloses a string in double quotes and escapes characters so that the
+ * string is a valid JS string. The resulting string is safe to embed in
+ * `<script>` tags as "<" is escaped.
+ * @param {string} s The string to quote.
+ * @return {string} A copy of {@code s} surrounded by double quotes.
+ */
+goog.string.quote = function(s) {
+ s = String(s);
+ 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('');
+};
+
+
+/**
+ * Takes a string and returns the escaped string for that input string.
+ * @param {string} str The string to escape.
+ * @return {string} An escaped string representing {@code str}.
+ */
+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('');
+};
+
+
+/**
+ * Takes a character and returns the escaped string for that character. For
+ * example escapeChar(String.fromCharCode(15)) -> "\\x0E".
+ * @param {string} c The character to escape.
+ * @return {string} An escaped string representing {@code c}.
+ */
+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 {
+ // tab is 9 but handled above
+ if (cc < 256) {
+ rv = '\\x';
+ if (cc < 16 || cc > 256) {
+ rv += '0';
+ }
+ } else {
+ rv = '\\u';
+ if (cc < 4096) { // \u1000
+ rv += '0';
+ }
+ }
+ rv += cc.toString(16).toUpperCase();
+ }
+
+ return goog.string.jsEscapeCache_[c] = rv;
+};
+
+
+/**
+ * Determines whether a string contains a substring.
+ * @param {string} str The string to search.
+ * @param {string} subString The substring to search for.
+ * @return {boolean} Whether {@code str} contains {@code subString}.
+ */
+goog.string.contains = function(str, subString) {
+ return str.indexOf(subString) != -1;
+};
+
+
+/**
+ * Determines whether a string contains a substring, ignoring case.
+ * @param {string} str The string to search.
+ * @param {string} subString The substring to search for.
+ * @return {boolean} Whether {@code str} contains {@code subString}.
+ */
+goog.string.caseInsensitiveContains = function(str, subString) {
+ return goog.string.contains(str.toLowerCase(), subString.toLowerCase());
+};
+
+
+/**
+ * Returns the non-overlapping occurrences of ss in s.
+ * If either s or ss evalutes to false, then returns zero.
+ * @param {string} s The string to look in.
+ * @param {string} ss The string to look for.
+ * @return {number} Number of occurrences of ss in s.
+ */
+goog.string.countOf = function(s, ss) {
+ return s && ss ? s.split(ss).length - 1 : 0;
+};
+
+
+/**
+ * Removes a substring of a specified length at a specific
+ * index in a string.
+ * @param {string} s The base string from which to remove.
+ * @param {number} index The index at which to remove the substring.
+ * @param {number} stringLength The length of the substring to remove.
+ * @return {string} A copy of {@code s} with the substring removed or the full
+ * string if nothing is removed or the input is invalid.
+ */
+goog.string.removeAt = function(s, index, stringLength) {
+ var resultStr = s;
+ // If the index is greater or equal to 0 then remove substring
+ if (index >= 0 && index < s.length && stringLength > 0) {
+ resultStr = s.substr(0, index) +
+ s.substr(index + stringLength, s.length - index - stringLength);
+ }
+ return resultStr;
+};
+
+
+/**
+ * Removes the first occurrence of a substring from a string.
+ * @param {string} str The base string from which to remove.
+ * @param {string} substr The string to remove.
+ * @return {string} A copy of {@code str} with {@code substr} removed or the
+ * full string if nothing is removed.
+ */
+goog.string.remove = function(str, substr) {
+ return str.replace(substr, '');
+};
+
+
+/**
+ * Removes all occurrences of a substring from a string.
+ * @param {string} s The base string from which to remove.
+ * @param {string} ss The string to remove.
+ * @return {string} A copy of {@code s} with {@code ss} removed or the full
+ * string if nothing is removed.
+ */
+goog.string.removeAll = function(s, ss) {
+ var re = new RegExp(goog.string.regExpEscape(ss), 'g');
+ return s.replace(re, '');
+};
+
+
+/**
+ * Replaces all occurrences of a substring of a string with a new substring.
+ * @param {string} s The base string from which to remove.
+ * @param {string} ss The string to replace.
+ * @param {string} replacement The replacement string.
+ * @return {string} A copy of {@code s} with {@code ss} replaced by
+ * {@code replacement} or the original string if nothing is replaced.
+ */
+goog.string.replaceAll = function(s, ss, replacement) {
+ var re = new RegExp(goog.string.regExpEscape(ss), 'g');
+ return s.replace(re, replacement.replace(/\$/g, '$$$$'));
+};
+
+
+/**
+ * Escapes characters in the string that are not safe to use in a RegExp.
+ * @param {*} s The string to escape. If not a string, it will be casted
+ * to one.
+ * @return {string} A RegExp safe, escaped copy of {@code s}.
+ */
+goog.string.regExpEscape = function(s) {
+ return String(s)
+ .replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1')
+ .replace(/\x08/g, '\\x08');
+};
+
+
+/**
+ * Repeats a string n times.
+ * @param {string} string The string to repeat.
+ * @param {number} length The number of times to repeat.
+ * @return {string} A string containing {@code length} repetitions of
+ * {@code string}.
+ */
+goog.string.repeat = (String.prototype.repeat) ? function(string, length) {
+ // The native method is over 100 times faster than the alternative.
+ return string.repeat(length);
+} : function(string, length) {
+ return new Array(length + 1).join(string);
+};
+
+
+/**
+ * Pads number to given length and optionally rounds it to a given precision.
+ * For example:
+ * <pre>padNumber(1.25, 2, 3) -> '01.250'
+ * padNumber(1.25, 2) -> '01.25'
+ * padNumber(1.25, 2, 1) -> '01.3'
+ * padNumber(1.25, 0) -> '1.25'</pre>
+ *
+ * @param {number} num The number to pad.
+ * @param {number} length The desired length.
+ * @param {number=} opt_precision The desired precision.
+ * @return {string} {@code num} as a string with the given options.
+ */
+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;
+};
+
+
+/**
+ * Returns a string representation of the given object, with
+ * null and undefined being returned as the empty string.
+ *
+ * @param {*} obj The object to convert.
+ * @return {string} A string representation of the {@code obj}.
+ */
+goog.string.makeSafe = function(obj) {
+ return obj == null ? '' : String(obj);
+};
+
+
+/**
+ * Concatenates string expressions. This is useful
+ * since some browsers are very inefficient when it comes to using plus to
+ * concat strings. Be careful when using null and undefined here since
+ * these will not be included in the result. If you need to represent these
+ * be sure to cast the argument to a String first.
+ * For example:
+ * <pre>buildString('a', 'b', 'c', 'd') -> 'abcd'
+ * buildString(null, undefined) -> ''
+ * </pre>
+ * @param {...*} var_args A list of strings to concatenate. If not a string,
+ * it will be casted to one.
+ * @return {string} The concatenation of {@code var_args}.
+ */
+goog.string.buildString = function(var_args) {
+ return Array.prototype.join.call(arguments, '');
+};
+
+
+/**
+ * Returns a string with at least 64-bits of randomness.
+ *
+ * Doesn't trust Javascript's random function entirely. Uses a combination of
+ * random and current timestamp, and then encodes the string in base-36 to
+ * make it shorter.
+ *
+ * @return {string} A random string, e.g. sn1s7vb4gcic.
+ */
+goog.string.getRandomString = function() {
+ var x = 2147483648;
+ return Math.floor(Math.random() * x).toString(36) +
+ Math.abs(Math.floor(Math.random() * x) ^ goog.now()).toString(36);
+};
+
+
+/**
+ * Compares two version numbers.
+ *
+ * @param {string|number} version1 Version of first item.
+ * @param {string|number} version2 Version of second item.
+ *
+ * @return {number} 1 if {@code version1} is higher.
+ * 0 if arguments are equal.
+ * -1 if {@code version2} is higher.
+ */
+goog.string.compareVersions = function(version1, version2) {
+ var order = 0;
+ // Trim leading and trailing whitespace and split the versions into
+ // subversions.
+ var v1Subs = goog.string.trim(String(version1)).split('.');
+ var v2Subs = goog.string.trim(String(version2)).split('.');
+ var subCount = Math.max(v1Subs.length, v2Subs.length);
+
+ // Iterate over the subversions, as long as they appear to be equivalent.
+ for (var subIdx = 0; order == 0 && subIdx < subCount; subIdx++) {
+ var v1Sub = v1Subs[subIdx] || '';
+ var v2Sub = v2Subs[subIdx] || '';
+
+ do {
+ // Split the subversions into pairs of numbers and qualifiers (like 'b').
+ // Two different RegExp objects are use to make it clear the code
+ // is side-effect free
+ var v1Comp = /(\d*)(\D*)(.*)/.exec(v1Sub) || ['', '', '', ''];
+ var v2Comp = /(\d*)(\D*)(.*)/.exec(v2Sub) || ['', '', '', ''];
+ // Break if there are no more matches.
+ if (v1Comp[0].length == 0 && v2Comp[0].length == 0) {
+ break;
+ }
+
+ // Parse the numeric part of the subversion. A missing number is
+ // equivalent to 0.
+ var v1CompNum = v1Comp[1].length == 0 ? 0 : parseInt(v1Comp[1], 10);
+ var v2CompNum = v2Comp[1].length == 0 ? 0 : parseInt(v2Comp[1], 10);
+
+ // Compare the subversion components. The number has the highest
+ // precedence. Next, if the numbers are equal, a subversion without any
+ // qualifier is always higher than a subversion with any qualifier. Next,
+ // the qualifiers are compared as strings.
+ order = goog.string.compareElements_(v1CompNum, v2CompNum) ||
+ goog.string.compareElements_(
+ v1Comp[2].length == 0, v2Comp[2].length == 0) ||
+ goog.string.compareElements_(v1Comp[2], v2Comp[2]);
+ // Stop as soon as an inequality is discovered.
+
+ v1Sub = v1Comp[3];
+ v2Sub = v2Comp[3];
+ } while (order == 0);
+ }
+
+ return order;
+};
+
+
+/**
+ * Compares elements of a version number.
+ *
+ * @param {string|number|boolean} left An element from a version number.
+ * @param {string|number|boolean} right An element from a version number.
+ *
+ * @return {number} 1 if {@code left} is higher.
+ * 0 if arguments are equal.
+ * -1 if {@code right} is higher.
+ * @private
+ */
+goog.string.compareElements_ = function(left, right) {
+ if (left < right) {
+ return -1;
+ } else if (left > right) {
+ return 1;
+ }
+ return 0;
+};
+
+
+/**
+ * String hash function similar to java.lang.String.hashCode().
+ * The hash code for a string is computed as
+ * s[0] * 31 ^ (n - 1) + s[1] * 31 ^ (n - 2) + ... + s[n - 1],
+ * where s[i] is the ith character of the string and n is the length of
+ * the string. We mod the result to make it between 0 (inclusive) and 2^32
+ * (exclusive).
+ * @param {string} str A string.
+ * @return {number} Hash value for {@code str}, between 0 (inclusive) and 2^32
+ * (exclusive). The empty string returns 0.
+ */
+goog.string.hashCode = function(str) {
+ var result = 0;
+ for (var i = 0; i < str.length; ++i) {
+ // Normalize to 4 byte range, 0 ... 2^32.
+ result = (31 * result + str.charCodeAt(i)) >>> 0;
+ }
+ return result;
+};
+
+
+/**
+ * The most recent unique ID. |0 is equivalent to Math.floor in this case.
+ * @type {number}
+ * @private
+ */
+goog.string.uniqueStringCounter_ = Math.random() * 0x80000000 | 0;
+
+
+/**
+ * Generates and returns a string which is unique in the current document.
+ * This is useful, for example, to create unique IDs for DOM elements.
+ * @return {string} A unique id.
+ */
+goog.string.createUniqueString = function() {
+ return 'goog_' + goog.string.uniqueStringCounter_++;
+};
+
+
+/**
+ * Converts the supplied string to a number, which may be Infinity or NaN.
+ * This function strips whitespace: (toNumber(' 123') === 123)
+ * This function accepts scientific notation: (toNumber('1e1') === 10)
+ *
+ * This is better than Javascript's built-in conversions because, sadly:
+ * (Number(' ') === 0) and (parseFloat('123a') === 123)
+ *
+ * @param {string} str The string to convert.
+ * @return {number} The number the supplied string represents, or NaN.
+ */
+goog.string.toNumber = function(str) {
+ var num = Number(str);
+ if (num == 0 && goog.string.isEmptyOrWhitespace(str)) {
+ return NaN;
+ }
+ return num;
+};
+
+
+/**
+ * Returns whether the given string is lower camel case (e.g. "isFooBar").
+ *
+ * Note that this assumes the string is entirely letters.
+ * @see http://en.wikipedia.org/wiki/CamelCase#Variations_and_synonyms
+ *
+ * @param {string} str String to test.
+ * @return {boolean} Whether the string is lower camel case.
+ */
+goog.string.isLowerCamelCase = function(str) {
+ return /^[a-z]+([A-Z][a-z]*)*$/.test(str);
+};
+
+
+/**
+ * Returns whether the given string is upper camel case (e.g. "FooBarBaz").
+ *
+ * Note that this assumes the string is entirely letters.
+ * @see http://en.wikipedia.org/wiki/CamelCase#Variations_and_synonyms
+ *
+ * @param {string} str String to test.
+ * @return {boolean} Whether the string is upper camel case.
+ */
+goog.string.isUpperCamelCase = function(str) {
+ return /^([A-Z][a-z]*)+$/.test(str);
+};
+
+
+/**
+ * Converts a string from selector-case to camelCase (e.g. from
+ * "multi-part-string" to "multiPartString"), useful for converting
+ * CSS selectors and HTML dataset keys to their equivalent JS properties.
+ * @param {string} str The string in selector-case form.
+ * @return {string} The string in camelCase form.
+ */
+goog.string.toCamelCase = function(str) {
+ return String(str).replace(
+ /\-([a-z])/g, function(all, match) { return match.toUpperCase(); });
+};
+
+
+/**
+ * Converts a string from camelCase to selector-case (e.g. from
+ * "multiPartString" to "multi-part-string"), useful for converting JS
+ * style and dataset properties to equivalent CSS selectors and HTML keys.
+ * @param {string} str The string in camelCase form.
+ * @return {string} The string in selector-case form.
+ */
+goog.string.toSelectorCase = function(str) {
+ return String(str).replace(/([A-Z])/g, '-$1').toLowerCase();
+};
+
+
+/**
+ * Converts a string into TitleCase. First character of the string is always
+ * capitalized in addition to the first letter of every subsequent word.
+ * Words are delimited by one or more whitespaces by default. Custom delimiters
+ * can optionally be specified to replace the default, which doesn't preserve
+ * whitespace delimiters and instead must be explicitly included if needed.
+ *
+ * Default delimiter => " ":
+ * goog.string.toTitleCase('oneTwoThree') => 'OneTwoThree'
+ * goog.string.toTitleCase('one two three') => 'One Two Three'
+ * goog.string.toTitleCase(' one two ') => ' One Two '
+ * goog.string.toTitleCase('one_two_three') => 'One_two_three'
+ * goog.string.toTitleCase('one-two-three') => 'One-two-three'
+ *
+ * Custom delimiter => "_-.":
+ * goog.string.toTitleCase('oneTwoThree', '_-.') => 'OneTwoThree'
+ * goog.string.toTitleCase('one two three', '_-.') => 'One two three'
+ * goog.string.toTitleCase(' one two ', '_-.') => ' one two '
+ * goog.string.toTitleCase('one_two_three', '_-.') => 'One_Two_Three'
+ * goog.string.toTitleCase('one-two-three', '_-.') => 'One-Two-Three'
+ * goog.string.toTitleCase('one...two...three', '_-.') => 'One...Two...Three'
+ * goog.string.toTitleCase('one. two. three', '_-.') => 'One. two. three'
+ * goog.string.toTitleCase('one-two.three', '_-.') => 'One-Two.Three'
+ *
+ * @param {string} str String value in camelCase form.
+ * @param {string=} opt_delimiters Custom delimiter character set used to
+ * distinguish words in the string value. Each character represents a
+ * single delimiter. When provided, default whitespace delimiter is
+ * overridden and must be explicitly included if needed.
+ * @return {string} String value in TitleCase form.
+ */
+goog.string.toTitleCase = function(str, opt_delimiters) {
+ var delimiters = goog.isString(opt_delimiters) ?
+ goog.string.regExpEscape(opt_delimiters) :
+ '\\s';
+
+ // For IE8, we need to prevent using an empty character set. Otherwise,
+ // incorrect matching will occur.
+ delimiters = delimiters ? '|[' + delimiters + ']+' : '';
+
+ var regexp = new RegExp('(^' + delimiters + ')([a-z])', 'g');
+ return str.replace(
+ regexp, function(all, p1, p2) { return p1 + p2.toUpperCase(); });
+};
+
+
+/**
+ * Capitalizes a string, i.e. converts the first letter to uppercase
+ * and all other letters to lowercase, e.g.:
+ *
+ * goog.string.capitalize('one') => 'One'
+ * goog.string.capitalize('ONE') => 'One'
+ * goog.string.capitalize('one two') => 'One two'
+ *
+ * Note that this function does not trim initial whitespace.
+ *
+ * @param {string} str String value to capitalize.
+ * @return {string} String value with first letter in uppercase.
+ */
+goog.string.capitalize = function(str) {
+ return String(str.charAt(0)).toUpperCase() +
+ String(str.substr(1)).toLowerCase();
+};
+
+
+/**
+ * Parse a string in decimal or hexidecimal ('0xFFFF') form.
+ *
+ * To parse a particular radix, please use parseInt(string, radix) directly. See
+ * https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/parseInt
+ *
+ * This is a wrapper for the built-in parseInt function that will only parse
+ * numbers as base 10 or base 16. Some JS implementations assume strings
+ * starting with "0" are intended to be octal. ES3 allowed but discouraged
+ * this behavior. ES5 forbids it. This function emulates the ES5 behavior.
+ *
+ * For more information, see Mozilla JS Reference: http://goo.gl/8RiFj
+ *
+ * @param {string|number|null|undefined} value The value to be parsed.
+ * @return {number} The number, parsed. If the string failed to parse, this
+ * will be NaN.
+ */
+goog.string.parseInt = function(value) {
+ // Force finite numbers to strings.
+ if (isFinite(value)) {
+ value = String(value);
+ }
+
+ if (goog.isString(value)) {
+ // If the string starts with '0x' or '-0x', parse as hex.
+ return /^\s*-?0x/i.test(value) ? parseInt(value, 16) : parseInt(value, 10);
+ }
+
+ return NaN;
+};
+
+
+/**
+ * Splits a string on a separator a limited number of times.
+ *
+ * This implementation is more similar to Python or Java, where the limit
+ * parameter specifies the maximum number of splits rather than truncating
+ * the number of results.
+ *
+ * See http://docs.python.org/2/library/stdtypes.html#str.split
+ * See JavaDoc: http://goo.gl/F2AsY
+ * See Mozilla reference: http://goo.gl/dZdZs
+ *
+ * @param {string} str String to split.
+ * @param {string} separator The separator.
+ * @param {number} limit The limit to the number of splits. The resulting array
+ * will have a maximum length of limit+1. Negative numbers are the same
+ * as zero.
+ * @return {!Array<string>} The string, split.
+ */
+goog.string.splitLimit = function(str, separator, limit) {
+ var parts = str.split(separator);
+ var returnVal = [];
+
+ // Only continue doing this while we haven't hit the limit and we have
+ // parts left.
+ while (limit > 0 && parts.length) {
+ returnVal.push(parts.shift());
+ limit--;
+ }
+
+ // If there are remaining parts, append them to the end.
+ if (parts.length) {
+ returnVal.push(parts.join(separator));
+ }
+
+ return returnVal;
+};
+
+
+/**
+ * Finds the characters to the right of the last instance of any separator
+ *
+ * This function is similar to goog.string.path.baseName, except it can take a
+ * list of characters to split the string on. It will return the rightmost
+ * grouping of characters to the right of any separator as a left-to-right
+ * oriented string.
+ *
+ * @see goog.string.path.baseName
+ * @param {string} str The string
+ * @param {string|!Array<string>} separators A list of separator characters
+ * @return {string} The last part of the string with respect to the separators
+ */
+goog.string.lastComponent = function(str, separators) {
+ if (!separators) {
+ return str;
+ } else if (typeof separators == 'string') {
+ separators = [separators];
+ }
+
+ var lastSeparatorIndex = -1;
+ for (var i = 0; i < separators.length; i++) {
+ if (separators[i] == '') {
+ continue;
+ }
+ var currentSeparatorIndex = str.lastIndexOf(separators[i]);
+ if (currentSeparatorIndex > lastSeparatorIndex) {
+ lastSeparatorIndex = currentSeparatorIndex;
+ }
+ }
+ if (lastSeparatorIndex == -1) {
+ return str;
+ }
+ return str.slice(lastSeparatorIndex + 1);
+};
+
+
+/**
+ * Computes the Levenshtein edit distance between two strings.
+ * @param {string} a
+ * @param {string} b
+ * @return {number} The edit distance between the two strings.
+ */
+goog.string.editDistance = function(a, b) {
+ var v0 = [];
+ var v1 = [];
+
+ if (a == b) {
+ return 0;
+ }
+
+ if (!a.length || !b.length) {
+ return Math.max(a.length, b.length);
+ }
+
+ for (var i = 0; i < b.length + 1; i++) {
+ v0[i] = i;
+ }
+
+ for (var i = 0; i < a.length; i++) {
+ v1[0] = i + 1;
+
+ for (var j = 0; j < b.length; j++) {
+ var cost = Number(a[i] != b[j]);
+ // Cost for the substring is the minimum of adding one character, removing
+ // one character, or a swap.
+ v1[j + 1] = Math.min(v1[j] + 1, v0[j + 1] + 1, v0[j] + cost);
+ }
+
+ for (var j = 0; j < v0.length; j++) {
+ v0[j] = v1[j];
+ }
+ }
+
+ return v1[b.length];
+};
diff --git a/chromium/third_party/ink/closure/string/typedstring.js b/chromium/third_party/ink/closure/string/typedstring.js
new file mode 100644
index 00000000000..d0d7bd911e4
--- /dev/null
+++ b/chromium/third_party/ink/closure/string/typedstring.js
@@ -0,0 +1,48 @@
+// Copyright 2013 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+goog.provide('goog.string.TypedString');
+
+
+
+/**
+ * Wrapper for strings that conform to a data type or language.
+ *
+ * Implementations of this interface are wrappers for strings, and typically
+ * associate a type contract with the wrapped string. Concrete implementations
+ * of this interface may choose to implement additional run-time type checking,
+ * see for example {@code goog.html.SafeHtml}. If available, client code that
+ * needs to ensure type membership of an object should use the type's function
+ * to assert type membership, such as {@code goog.html.SafeHtml.unwrap}.
+ * @interface
+ */
+goog.string.TypedString = function() {};
+
+
+/**
+ * Interface marker of the TypedString interface.
+ *
+ * This property can be used to determine at runtime whether or not an object
+ * implements this interface. All implementations of this interface set this
+ * property to {@code true}.
+ * @type {boolean}
+ */
+goog.string.TypedString.prototype.implementsGoogStringTypedString;
+
+
+/**
+ * Retrieves this wrapped string's value.
+ * @return {string} The wrapped string's value.
+ */
+goog.string.TypedString.prototype.getTypedStringValue;
diff --git a/chromium/third_party/ink/closure/structs/collection.js b/chromium/third_party/ink/closure/structs/collection.js
new file mode 100644
index 00000000000..267862c6df1
--- /dev/null
+++ b/chromium/third_party/ink/closure/structs/collection.js
@@ -0,0 +1,55 @@
+// Copyright 2011 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Defines the collection interface.
+ *
+ * @author nnaze@google.com (Nathan Naze)
+ */
+
+goog.provide('goog.structs.Collection');
+
+
+
+/**
+ * An interface for a collection of values.
+ * @interface
+ * @template T
+ */
+goog.structs.Collection = function() {};
+
+
+/**
+ * @param {T} value Value to add to the collection.
+ */
+goog.structs.Collection.prototype.add;
+
+
+/**
+ * @param {T} value Value to remove from the collection.
+ */
+goog.structs.Collection.prototype.remove;
+
+
+/**
+ * @param {T} value Value to find in the collection.
+ * @return {boolean} Whether the collection contains the specified value.
+ */
+goog.structs.Collection.prototype.contains;
+
+
+/**
+ * @return {number} The number of values stored in the collection.
+ */
+goog.structs.Collection.prototype.getCount;
diff --git a/chromium/third_party/ink/closure/structs/inversionmap.js b/chromium/third_party/ink/closure/structs/inversionmap.js
new file mode 100644
index 00000000000..009c02aaeab
--- /dev/null
+++ b/chromium/third_party/ink/closure/structs/inversionmap.js
@@ -0,0 +1,158 @@
+// Copyright 2008 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Provides inversion and inversion map functionality for storing
+ * integer ranges and corresponding values.
+ *
+ * @author cibu@google.com (Cibu Johny)
+ * @author markdavis@google.com (Mark Davis)
+ */
+
+goog.provide('goog.structs.InversionMap');
+
+goog.require('goog.array');
+goog.require('goog.asserts');
+
+
+
+/**
+ * Maps ranges to values.
+ * @param {Array<number>} rangeArray An array of monotonically
+ * increasing integer values, with at least one instance.
+ * @param {Array<T>} valueArray An array of corresponding values.
+ * Length must be the same as rangeArray.
+ * @param {boolean=} opt_delta If true, saves only delta from previous value.
+ * @constructor
+ * @template T
+ */
+goog.structs.InversionMap = function(rangeArray, valueArray, opt_delta) {
+ /**
+ * @protected {Array<number>}
+ */
+ this.rangeArray = null;
+
+ goog.asserts.assert(
+ rangeArray.length == valueArray.length,
+ 'rangeArray and valueArray must have the same length.');
+ this.storeInversion_(rangeArray, opt_delta);
+
+ /** @protected {Array<T>} */
+ this.values = valueArray;
+};
+
+
+/**
+ * Stores the integers as ranges (half-open).
+ * If delta is true, the integers are delta from the previous value and
+ * will be restored to the absolute value.
+ * When used as a set, even indices are IN, and odd are OUT.
+ * @param {Array<number>} rangeArray An array of monotonically
+ * increasing integer values, with at least one instance.
+ * @param {boolean=} opt_delta If true, saves only delta from previous value.
+ * @private
+ */
+goog.structs.InversionMap.prototype.storeInversion_ = function(
+ rangeArray, opt_delta) {
+ this.rangeArray = rangeArray;
+
+ for (var i = 1; i < rangeArray.length; i++) {
+ if (rangeArray[i] == null) {
+ rangeArray[i] = rangeArray[i - 1] + 1;
+ } else if (opt_delta) {
+ rangeArray[i] += rangeArray[i - 1];
+ }
+ }
+};
+
+
+/**
+ * Splices a range -> value map into this inversion map.
+ * @param {Array<number>} rangeArray An array of monotonically
+ * increasing integer values, with at least one instance.
+ * @param {Array<T>} valueArray An array of corresponding values.
+ * Length must be the same as rangeArray.
+ * @param {boolean=} opt_delta If true, saves only delta from previous value.
+ */
+goog.structs.InversionMap.prototype.spliceInversion = function(
+ rangeArray, valueArray, opt_delta) {
+ // By building another inversion map, we build the arrays that we need
+ // to splice in.
+ var otherMap =
+ new goog.structs.InversionMap(rangeArray, valueArray, opt_delta);
+
+ // Figure out where to splice those arrays.
+ var startRange = otherMap.rangeArray[0];
+ var endRange =
+ /** @type {number} */ (goog.array.peek(otherMap.rangeArray));
+ var startSplice = this.getLeast(startRange);
+ var endSplice = this.getLeast(endRange);
+
+ // The inversion map works by storing the start points of ranges...
+ if (startRange != this.rangeArray[startSplice]) {
+ // ...if we're splicing in a start point that isn't already here,
+ // then we need to insert it after the insertion point.
+ startSplice++;
+ } // otherwise we overwrite the insertion point.
+
+ this.rangeArray = this.rangeArray.slice(0, startSplice)
+ .concat(otherMap.rangeArray)
+ .concat(this.rangeArray.slice(endSplice + 1));
+ this.values = this.values.slice(0, startSplice)
+ .concat(otherMap.values)
+ .concat(this.values.slice(endSplice + 1));
+};
+
+
+/**
+ * Gets the value corresponding to a number from the inversion map.
+ * @param {number} intKey The number for which value needs to be retrieved
+ * from inversion map.
+ * @return {T|null} Value retrieved from inversion map; null if not found.
+ */
+goog.structs.InversionMap.prototype.at = function(intKey) {
+ var index = this.getLeast(intKey);
+ if (index < 0) {
+ return null;
+ }
+ return this.values[index];
+};
+
+
+/**
+ * Gets the largest index such that rangeArray[index] <= intKey from the
+ * inversion map.
+ * @param {number} intKey The probe for which rangeArray is searched.
+ * @return {number} Largest index such that rangeArray[index] <= intKey.
+ * @protected
+ */
+goog.structs.InversionMap.prototype.getLeast = function(intKey) {
+ var arr = this.rangeArray;
+ var low = 0;
+ var high = arr.length;
+ while (high - low > 8) {
+ var mid = (high + low) >> 1;
+ if (arr[mid] <= intKey) {
+ low = mid;
+ } else {
+ high = mid;
+ }
+ }
+ for (; low < high; ++low) {
+ if (intKey < arr[low]) {
+ break;
+ }
+ }
+ return low - 1;
+};
diff --git a/chromium/third_party/ink/closure/structs/map.js b/chromium/third_party/ink/closure/structs/map.js
new file mode 100644
index 00000000000..ccc81842127
--- /dev/null
+++ b/chromium/third_party/ink/closure/structs/map.js
@@ -0,0 +1,485 @@
+// Copyright 2006 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Datastructure: Hash Map.
+ *
+ * @author arv@google.com (Erik Arvidsson)
+ * @author jonp@google.com (Jon Perlow) Optimized for IE6
+ *
+ * This file contains an implementation of a Map structure. It implements a lot
+ * of the methods used in goog.structs so those functions work on hashes. This
+ * is best suited for complex key types. For simple keys such as numbers and
+ * strings consider using the lighter-weight utilities in goog.object.
+ * MOE:begin_intracomment_strip
+ *
+ * NOTE(flan): Internally, key types are NOT actually cast to
+ * strings. Some people actually rely on this behavior even though it
+ * is incorrect. For more information, see http://b/5622311.
+ *
+ * NOTE(flan): Erik Corry (erikcorry) from the V8 team went over this
+ * class with me to help look for simplifications and
+ * optimizations. In the end, he didn't come up with very much. Erik
+ * explained that "for (k in o)" is not optimized in Crankshaft
+ * because it needs to look up properties in the whole prototype
+ * chain. It also needs to return the keys in order. Thus keeping an
+ * array of keys is actually much more efficient.
+ *
+ * Likewise, one option to iterate safely with "for (k in o)" is to
+ * prefix the keys with some character, like ':'. This can create a
+ * lot of strings that didn't exist before. In Closure Labs,
+ * goog.labs.structs.Map uses extra arrays to store non-safe keys and
+ * values.
+ *
+ * Thus, there are not a lot of reasonable simplifications that can be
+ * done here without impacting performance.
+ *
+ * TODO(chrishenry): Create some performance benchmarks for common
+ * operations.
+ * MOE:end_intracomment_strip
+ */
+
+
+goog.provide('goog.structs.Map');
+
+goog.require('goog.iter.Iterator');
+goog.require('goog.iter.StopIteration');
+goog.require('goog.object');
+
+
+
+/**
+ * Class for Hash Map datastructure.
+ * @param {*=} opt_map Map or Object to initialize the map with.
+ * @param {...*} var_args If 2 or more arguments are present then they
+ * will be used as key-value pairs.
+ * @constructor
+ * @template K, V
+ * @deprecated This type is misleading: use ES6 Map instead.
+ */
+goog.structs.Map = function(opt_map, var_args) {
+
+ /**
+ * Underlying JS object used to implement the map.
+ * @private {!Object}
+ */
+ this.map_ = {};
+
+ /**
+ * An array of keys. This is necessary for two reasons:
+ * 1. Iterating the keys using for (var key in this.map_) allocates an
+ * object for every key in IE which is really bad for IE6 GC perf.
+ * 2. Without a side data structure, we would need to escape all the keys
+ * as that would be the only way we could tell during iteration if the
+ * key was an internal key or a property of the object.
+ *
+ * This array can contain deleted keys so it's necessary to check the map
+ * as well to see if the key is still in the map (this doesn't require a
+ * memory allocation in IE).
+ * @private {!Array<string>}
+ */
+ this.keys_ = [];
+
+ /**
+ * The number of key value pairs in the map.
+ * @private {number}
+ */
+ this.count_ = 0;
+
+ /**
+ * Version used to detect changes while iterating.
+ * @private {number}
+ */
+ this.version_ = 0;
+
+ var argLength = arguments.length;
+
+ if (argLength > 1) {
+ if (argLength % 2) {
+ throw new 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(/** @type {Object} */ (opt_map));
+ }
+};
+
+
+/**
+ * @return {number} The number of key-value pairs in the map.
+ */
+goog.structs.Map.prototype.getCount = function() {
+ return this.count_;
+};
+
+
+/**
+ * Returns the values of the map.
+ * @return {!Array<V>} The values in the map.
+ */
+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;
+};
+
+
+/**
+ * Returns the keys of the map.
+ * @return {!Array<string>} Array of string values.
+ */
+goog.structs.Map.prototype.getKeys = function() {
+ this.cleanupKeysArray_();
+ return /** @type {!Array<string>} */ (this.keys_.concat());
+};
+
+
+/**
+ * Whether the map contains the given key.
+ * @param {*} key The key to check for.
+ * @return {boolean} Whether the map contains the key.
+ */
+goog.structs.Map.prototype.containsKey = function(key) {
+ return goog.structs.Map.hasKey_(this.map_, key);
+};
+
+
+/**
+ * Whether the map contains the given value. This is O(n).
+ * @param {V} val The value to check for.
+ * @return {boolean} Whether the map contains the value.
+ */
+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;
+};
+
+
+/**
+ * Whether this map is equal to the argument map.
+ * @param {goog.structs.Map} otherMap The map against which to test equality.
+ * @param {function(V, V): boolean=} opt_equalityFn Optional equality function
+ * to test equality of values. If not specified, this will test whether
+ * the values contained in each map are identical objects.
+ * @return {boolean} Whether the maps are equal.
+ */
+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;
+};
+
+
+/**
+ * Default equality test for values.
+ * @param {*} a The first value.
+ * @param {*} b The second value.
+ * @return {boolean} Whether a and b reference the same object.
+ */
+goog.structs.Map.defaultEquals = function(a, b) {
+ return a === b;
+};
+
+
+/**
+ * @return {boolean} Whether the map is empty.
+ */
+goog.structs.Map.prototype.isEmpty = function() {
+ return this.count_ == 0;
+};
+
+
+/**
+ * Removes all key-value pairs from the map.
+ */
+goog.structs.Map.prototype.clear = function() {
+ this.map_ = {};
+ this.keys_.length = 0;
+ this.count_ = 0;
+ this.version_ = 0;
+};
+
+
+/**
+ * Removes a key-value pair based on the key. This is O(logN) amortized due to
+ * updating the keys array whenever the count becomes half the size of the keys
+ * in the keys array.
+ * @param {*} key The key to remove.
+ * @return {boolean} Whether object was removed.
+ */
+goog.structs.Map.prototype.remove = function(key) {
+ if (goog.structs.Map.hasKey_(this.map_, key)) {
+ delete this.map_[key];
+ this.count_--;
+ this.version_++;
+
+ // clean up the keys array if the threshold is hit
+ if (this.keys_.length > 2 * this.count_) {
+ this.cleanupKeysArray_();
+ }
+
+ return true;
+ }
+ return false;
+};
+
+
+/**
+ * Cleans up the temp keys array by removing entries that are no longer in the
+ * map.
+ * @private
+ */
+goog.structs.Map.prototype.cleanupKeysArray_ = function() {
+ if (this.count_ != this.keys_.length) {
+ // First remove keys that are no longer in the map.
+ 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) {
+ // If the count still isn't correct, that means we have duplicates. This can
+ // happen when the same key is added and removed multiple times. Now we have
+ // to allocate one extra Object to remove the duplicates. This could have
+ // been done in the first pass, but in the common case, we can avoid
+ // allocating an extra object by only doing this when necessary.
+ 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;
+ }
+};
+
+
+/**
+ * Returns the value for the given key. If the key is not found and the default
+ * value is not given this will return {@code undefined}.
+ * @param {*} key The key to get the value for.
+ * @param {DEFAULT=} opt_val The value to return if no item is found for the
+ * given key, defaults to undefined.
+ * @return {V|DEFAULT} The value for the given key.
+ * @template DEFAULT
+ */
+goog.structs.Map.prototype.get = function(key, opt_val) {
+ if (goog.structs.Map.hasKey_(this.map_, key)) {
+ return this.map_[key];
+ }
+ return opt_val;
+};
+
+
+/**
+ * Adds a key-value pair to the map.
+ * @param {*} key The key.
+ * @param {V} value The value to add.
+ * @return {*} Some subclasses return a value.
+ */
+goog.structs.Map.prototype.set = function(key, value) {
+ if (!(goog.structs.Map.hasKey_(this.map_, key))) {
+ this.count_++;
+ // TODO(johnlenz): This class lies, it claims to return an array of string
+ // keys, but instead returns the original object used.
+ this.keys_.push(/** @type {?} */ (key));
+ // Only change the version if we add a new key.
+ this.version_++;
+ }
+ this.map_[key] = value;
+};
+
+
+/**
+ * Adds multiple key-value pairs from another goog.structs.Map or Object.
+ * @param {Object} map Object containing the data to add.
+ */
+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);
+ }
+ // we could use goog.array.forEach here but I don't want to introduce that
+ // dependency just for this.
+ for (var i = 0; i < keys.length; i++) {
+ this.set(keys[i], values[i]);
+ }
+};
+
+
+/**
+ * Calls the given function on each entry in the map.
+ * @param {function(this:T, V, K, goog.structs.Map<K,V>)} f
+ * @param {T=} opt_obj The value of "this" inside f.
+ * @template T
+ */
+goog.structs.Map.prototype.forEach = function(f, opt_obj) {
+ var keys = this.getKeys();
+ for (var i = 0; i < keys.length; i++) {
+ var key = keys[i];
+ var value = this.get(key);
+ f.call(opt_obj, value, key, this);
+ }
+};
+
+
+/**
+ * Clones a map and returns a new map.
+ * @return {!goog.structs.Map} A new map with the same key-value pairs.
+ */
+goog.structs.Map.prototype.clone = function() {
+ return new goog.structs.Map(this);
+};
+
+
+/**
+ * Returns a new map in which all the keys and values are interchanged
+ * (keys become values and values become keys). If multiple keys map to the
+ * same value, the chosen transposed value is implementation-dependent.
+ *
+ * It acts very similarly to {goog.object.transpose(Object)}.
+ *
+ * @return {!goog.structs.Map} The transposed map.
+ */
+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;
+};
+
+
+/**
+ * @return {!Object} Object representation of the map.
+ */
+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;
+};
+
+
+/**
+ * Returns an iterator that iterates over the keys in the map. Removal of keys
+ * while iterating might have undesired side effects.
+ * @return {!goog.iter.Iterator} An iterator over the keys in the map.
+ */
+goog.structs.Map.prototype.getKeyIterator = function() {
+ return this.__iterator__(true);
+};
+
+
+/**
+ * Returns an iterator that iterates over the values in the map. Removal of
+ * keys while iterating might have undesired side effects.
+ * @return {!goog.iter.Iterator} An iterator over the values in the map.
+ */
+goog.structs.Map.prototype.getValueIterator = function() {
+ return this.__iterator__(false);
+};
+
+
+/**
+ * Returns an iterator that iterates over the values or the keys in the map.
+ * This throws an exception if the map was mutated since the iterator was
+ * created.
+ * @param {boolean=} opt_keys True to iterate over the keys. False to iterate
+ * over the values. The default value is false.
+ * @return {!goog.iter.Iterator} An iterator over the values or keys in the map.
+ */
+goog.structs.Map.prototype.__iterator__ = function(opt_keys) {
+ // Clean up keys to minimize the risk of iterating over dead keys.
+ this.cleanupKeysArray_();
+
+ var i = 0;
+ var version = this.version_;
+ var selfObj = this;
+
+ var newIter = new goog.iter.Iterator;
+ newIter.next = function() {
+ if (version != selfObj.version_) {
+ throw new Error('The map has changed since the iterator was created');
+ }
+ if (i >= selfObj.keys_.length) {
+ throw goog.iter.StopIteration;
+ }
+ var key = selfObj.keys_[i++];
+ return opt_keys ? key : selfObj.map_[key];
+ };
+ return newIter;
+};
+
+
+/**
+ * Safe way to test for hasOwnProperty. It even allows testing for
+ * 'hasOwnProperty'.
+ * @param {Object} obj The object to test for presence of the given key.
+ * @param {*} key The key to check for.
+ * @return {boolean} Whether the object has the key.
+ * @private
+ */
+goog.structs.Map.hasKey_ = function(obj, key) {
+ return Object.prototype.hasOwnProperty.call(obj, key);
+};
diff --git a/chromium/third_party/ink/closure/structs/set.js b/chromium/third_party/ink/closure/structs/set.js
new file mode 100644
index 00000000000..e8470ab8c90
--- /dev/null
+++ b/chromium/third_party/ink/closure/structs/set.js
@@ -0,0 +1,280 @@
+// Copyright 2006 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Datastructure: Set.
+ *
+ * @author arv@google.com (Erik Arvidsson)
+ * @author pallosp@google.com (Peter Pallos)
+ *
+ * This class implements a set data structure. Adding and removing is O(1). It
+ * supports both object and primitive values. Be careful because you can add
+ * both 1 and new Number(1), because these are not the same. You can even add
+ * multiple new Number(1) because these are not equal.
+ */
+
+
+goog.provide('goog.structs.Set');
+
+goog.require('goog.structs');
+goog.require('goog.structs.Collection');
+goog.require('goog.structs.Map');
+
+
+
+/**
+ * A set that can contain both primitives and objects. Adding and removing
+ * elements is O(1). Primitives are treated as identical if they have the same
+ * type and convert to the same string. Objects are treated as identical only
+ * if they are references to the same object. WARNING: A goog.structs.Set can
+ * contain both 1 and (new Number(1)), because they are not the same. WARNING:
+ * Adding (new Number(1)) twice will yield two distinct elements, because they
+ * are two different objects. WARNING: Any object that is added to a
+ * goog.structs.Set will be modified! Because goog.getUid() is used to
+ * identify objects, every object in the set will be mutated.
+ * @param {Array<T>|Object<?,T>=} opt_values Initial values to start with.
+ * @constructor
+ * @implements {goog.structs.Collection<T>}
+ * @final
+ * @template T
+ * @deprecated This type is misleading: use ES6 Set instead.
+ */
+goog.structs.Set = function(opt_values) {
+ this.map_ = new goog.structs.Map;
+ if (opt_values) {
+ this.addAll(opt_values);
+ }
+};
+
+
+/**
+ * Obtains a unique key for an element of the set. Primitives will yield the
+ * same key if they have the same type and convert to the same string. Object
+ * references will yield the same key only if they refer to the same object.
+ * @param {*} val Object or primitive value to get a key for.
+ * @return {string} A unique key for this value/object.
+ * @private
+ */
+goog.structs.Set.getKey_ = function(val) {
+ var type = typeof val;
+ if (type == 'object' && val || type == 'function') {
+ return 'o' + goog.getUid(/** @type {Object} */ (val));
+ } else {
+ return type.substr(0, 1) + val;
+ }
+};
+
+
+/**
+ * @return {number} The number of elements in the set.
+ * @override
+ */
+goog.structs.Set.prototype.getCount = function() {
+ return this.map_.getCount();
+};
+
+
+/**
+ * Add a primitive or an object to the set.
+ * @param {T} element The primitive or object to add.
+ * @override
+ */
+goog.structs.Set.prototype.add = function(element) {
+ this.map_.set(goog.structs.Set.getKey_(element), element);
+};
+
+
+/**
+ * Adds all the values in the given collection to this set.
+ * @param {Array<T>|goog.structs.Collection<T>|Object<?,T>} col A collection
+ * containing the elements to add.
+ */
+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]);
+ }
+};
+
+
+/**
+ * Removes all values in the given collection from this set.
+ * @param {Array<T>|goog.structs.Collection<T>|Object<?,T>} col A collection
+ * containing the elements to remove.
+ */
+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]);
+ }
+};
+
+
+/**
+ * Removes the given element from this set.
+ * @param {T} element The primitive or object to remove.
+ * @return {boolean} Whether the element was found and removed.
+ * @override
+ */
+goog.structs.Set.prototype.remove = function(element) {
+ return this.map_.remove(goog.structs.Set.getKey_(element));
+};
+
+
+/**
+ * Removes all elements from this set.
+ */
+goog.structs.Set.prototype.clear = function() {
+ this.map_.clear();
+};
+
+
+/**
+ * Tests whether this set is empty.
+ * @return {boolean} True if there are no elements in this set.
+ */
+goog.structs.Set.prototype.isEmpty = function() {
+ return this.map_.isEmpty();
+};
+
+
+/**
+ * Tests whether this set contains the given element.
+ * @param {T} element The primitive or object to test for.
+ * @return {boolean} True if this set contains the given element.
+ * @override
+ */
+goog.structs.Set.prototype.contains = function(element) {
+ return this.map_.containsKey(goog.structs.Set.getKey_(element));
+};
+
+
+/**
+ * Tests whether this set contains all the values in a given collection.
+ * Repeated elements in the collection are ignored, e.g. (new
+ * goog.structs.Set([1, 2])).containsAll([1, 1]) is True.
+ * @param {goog.structs.Collection<T>|Object} col A collection-like object.
+ * @return {boolean} True if the set contains all elements.
+ */
+goog.structs.Set.prototype.containsAll = function(col) {
+ return goog.structs.every(col, this.contains, this);
+};
+
+
+/**
+ * Finds all values that are present in both this set and the given collection.
+ * @param {Array<S>|Object<?,S>} col A collection.
+ * @return {!goog.structs.Set<T|S>} A new set containing all the values
+ * (primitives or objects) present in both this set and the given
+ * collection.
+ * @template S
+ */
+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;
+};
+
+
+/**
+ * Finds all values that are present in this set and not in the given
+ * collection.
+ * @param {Array<T>|goog.structs.Collection<T>|Object<?,T>} col A collection.
+ * @return {!goog.structs.Set} A new set containing all the values
+ * (primitives or objects) present in this set but not in the given
+ * collection.
+ */
+goog.structs.Set.prototype.difference = function(col) {
+ var result = this.clone();
+ result.removeAll(col);
+ return result;
+};
+
+
+/**
+ * Returns an array containing all the elements in this set.
+ * @return {!Array<T>} An array containing all the elements in this set.
+ */
+goog.structs.Set.prototype.getValues = function() {
+ return this.map_.getValues();
+};
+
+
+/**
+ * Creates a shallow clone of this set.
+ * @return {!goog.structs.Set<T>} A new set containing all the same elements as
+ * this set.
+ */
+goog.structs.Set.prototype.clone = function() {
+ return new goog.structs.Set(this);
+};
+
+
+/**
+ * Tests whether the given collection consists of the same elements as this set,
+ * regardless of order, without repetition. Primitives are treated as equal if
+ * they have the same type and convert to the same string; objects are treated
+ * as equal if they are references to the same object. This operation is O(n).
+ * @param {goog.structs.Collection<T>|Object} col A collection.
+ * @return {boolean} True if the given collection consists of the same elements
+ * as this set, regardless of order, without repetition.
+ */
+goog.structs.Set.prototype.equals = function(col) {
+ return this.getCount() == goog.structs.getCount(col) && this.isSubsetOf(col);
+};
+
+
+/**
+ * Tests whether the given collection contains all the elements in this set.
+ * Primitives are treated as equal if they have the same type and convert to the
+ * same string; objects are treated as equal if they are references to the same
+ * object. This operation is O(n).
+ * @param {goog.structs.Collection<T>|Object} col A collection.
+ * @return {boolean} True if this set is a subset of the given collection.
+ */
+goog.structs.Set.prototype.isSubsetOf = function(col) {
+ var colCount = goog.structs.getCount(col);
+ if (this.getCount() > colCount) {
+ return false;
+ }
+ // TODO(pallosp) Find the minimal collection size where the conversion makes
+ // the contains() method faster.
+ if (!(col instanceof goog.structs.Set) && colCount > 5) {
+ // Convert to a goog.structs.Set so that goog.structs.contains runs in
+ // O(1) time instead of O(n) time.
+ col = new goog.structs.Set(col);
+ }
+ return goog.structs.every(
+ this, function(value) { return goog.structs.contains(col, value); });
+};
+
+
+/**
+ * Returns an iterator that iterates over the elements in this set.
+ * @param {boolean=} opt_keys This argument is ignored.
+ * @return {!goog.iter.Iterator} An iterator over the elements in this set.
+ */
+goog.structs.Set.prototype.__iterator__ = function(opt_keys) {
+ return this.map_.__iterator__(false);
+};
diff --git a/chromium/third_party/ink/closure/structs/structs.js b/chromium/third_party/ink/closure/structs/structs.js
new file mode 100644
index 00000000000..684ddfe4151
--- /dev/null
+++ b/chromium/third_party/ink/closure/structs/structs.js
@@ -0,0 +1,354 @@
+// Copyright 2006 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Generics method for collection-like classes and objects.
+ *
+ * @author arv@google.com (Erik Arvidsson)
+ *
+ * This file contains functions to work with collections. It supports using
+ * Map, Set, Array and Object and other classes that implement collection-like
+ * methods.
+ */
+
+
+goog.provide('goog.structs');
+
+goog.require('goog.array');
+goog.require('goog.object');
+
+
+// We treat an object as a dictionary if it has getKeys or it is an object that
+// isn't arrayLike.
+
+
+/**
+ * Returns the number of values in the collection-like object.
+ * @param {Object} col The collection-like object.
+ * @return {number} The number of values in the collection-like object.
+ */
+goog.structs.getCount = function(col) {
+ if (col.getCount && typeof col.getCount == 'function') {
+ return col.getCount();
+ }
+ if (goog.isArrayLike(col) || goog.isString(col)) {
+ return col.length;
+ }
+ return goog.object.getCount(col);
+};
+
+
+/**
+ * Returns the values of the collection-like object.
+ * @param {Object} col The collection-like object.
+ * @return {!Array<?>} The values in the collection-like object.
+ */
+goog.structs.getValues = function(col) {
+ if (col.getValues && 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);
+};
+
+
+/**
+ * Returns the keys of the collection. Some collections have no notion of
+ * keys/indexes and this function will return undefined in those cases.
+ * @param {Object} col The collection-like object.
+ * @return {!Array|undefined} The keys in the collection.
+ */
+goog.structs.getKeys = function(col) {
+ if (col.getKeys && typeof col.getKeys == 'function') {
+ return col.getKeys();
+ }
+ // if we have getValues but no getKeys we know this is a key-less collection
+ if (col.getValues && 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);
+};
+
+
+/**
+ * Whether the collection contains the given value. This is O(n) and uses
+ * equals (==) to test the existence.
+ * @param {Object} col The collection-like object.
+ * @param {*} val The value to check for.
+ * @return {boolean} True if the map contains the value.
+ */
+goog.structs.contains = function(col, val) {
+ if (col.contains && typeof col.contains == 'function') {
+ return col.contains(val);
+ }
+ if (col.containsValue && typeof col.containsValue == 'function') {
+ return col.containsValue(val);
+ }
+ if (goog.isArrayLike(col) || goog.isString(col)) {
+ return goog.array.contains(/** @type {!Array<?>} */ (col), val);
+ }
+ return goog.object.containsValue(col, val);
+};
+
+
+/**
+ * Whether the collection is empty.
+ * @param {Object} col The collection-like object.
+ * @return {boolean} True if empty.
+ */
+goog.structs.isEmpty = function(col) {
+ if (col.isEmpty && typeof col.isEmpty == 'function') {
+ return col.isEmpty();
+ }
+
+ // We do not use goog.string.isEmptyOrWhitespace because here we treat the
+ // string as
+ // collection and as such even whitespace matters
+
+ if (goog.isArrayLike(col) || goog.isString(col)) {
+ return goog.array.isEmpty(/** @type {!Array<?>} */ (col));
+ }
+ return goog.object.isEmpty(col);
+};
+
+
+/**
+ * Removes all the elements from the collection.
+ * @param {Object} col The collection-like object.
+ */
+goog.structs.clear = function(col) {
+ // NOTE(arv): This should not contain strings because strings are immutable
+ if (col.clear && typeof col.clear == 'function') {
+ col.clear();
+ } else if (goog.isArrayLike(col)) {
+ goog.array.clear(/** @type {IArrayLike<?>} */ (col));
+ } else {
+ goog.object.clear(col);
+ }
+};
+
+
+/**
+ * Calls a function for each value in a collection. The function takes
+ * three arguments; the value, the key and the collection.
+ *
+ * @param {S} col The collection-like object.
+ * @param {function(this:T,?,?,S):?} f The function to call for every value.
+ * This function takes
+ * 3 arguments (the value, the key or undefined if the collection has no
+ * notion of keys, and the collection) and the return value is irrelevant.
+ * @param {T=} opt_obj The object to be used as the value of 'this'
+ * within {@code f}.
+ * @template T,S
+ * @deprecated Use a more specific method, e.g. goog.array.forEach,
+ * goog.object.forEach, or for-of.
+ */
+goog.structs.forEach = function(col, f, opt_obj) {
+ if (col.forEach && typeof col.forEach == 'function') {
+ col.forEach(f, opt_obj);
+ } else if (goog.isArrayLike(col) || goog.isString(col)) {
+ goog.array.forEach(/** @type {!Array<?>} */ (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(/** @type {?} */ (opt_obj), values[i], keys && keys[i], col);
+ }
+ }
+};
+
+
+/**
+ * Calls a function for every value in the collection. When a call returns true,
+ * adds the value to a new collection (Array is returned by default).
+ *
+ * @param {S} col The collection-like object.
+ * @param {function(this:T,?,?,S):boolean} f The function to call for every
+ * value. This function takes
+ * 3 arguments (the value, the key or undefined if the collection has no
+ * notion of keys, and the collection) and should return a Boolean. If the
+ * return value is true the value is added to the result collection. If it
+ * is false the value is not included.
+ * @param {T=} opt_obj The object to be used as the value of 'this'
+ * within {@code f}.
+ * @return {!Object|!Array<?>} A new collection where the passed values are
+ * present. If col is a key-less collection an array is returned. If col
+ * has keys and values a plain old JS object is returned.
+ * @template T,S
+ */
+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(/** @type {!Array<?>} */ (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(/** @type {?} */ (opt_obj), values[i], keys[i], col)) {
+ rv[keys[i]] = values[i];
+ }
+ }
+ } else {
+ // We should not use goog.array.filter here since we want to make sure that
+ // the index is undefined as well as make sure that col is passed to the
+ // function.
+ rv = [];
+ for (var i = 0; i < l; i++) {
+ if (f.call(opt_obj, values[i], undefined, col)) {
+ rv.push(values[i]);
+ }
+ }
+ }
+ return rv;
+};
+
+
+/**
+ * Calls a function for every value in the collection and adds the result into a
+ * new collection (defaults to creating a new Array).
+ *
+ * @param {S} col The collection-like object.
+ * @param {function(this:T,?,?,S):V} f The function to call for every value.
+ * This function takes 3 arguments (the value, the key or undefined if the
+ * collection has no notion of keys, and the collection) and should return
+ * something. The result will be used as the value in the new collection.
+ * @param {T=} opt_obj The object to be used as the value of 'this'
+ * within {@code f}.
+ * @return {!Object<V>|!Array<V>} A new collection with the new values. If
+ * col is a key-less collection an array is returned. If col has keys and
+ * values a plain old JS object is returned.
+ * @template T,S,V
+ */
+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(/** @type {!Array<?>} */ (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(/** @type {?} */ (opt_obj), values[i], keys[i], col);
+ }
+ } else {
+ // We should not use goog.array.map here since we want to make sure that
+ // the index is undefined as well as make sure that col is passed to the
+ // function.
+ rv = [];
+ for (var i = 0; i < l; i++) {
+ rv[i] = f.call(/** @type {?} */ (opt_obj), values[i], undefined, col);
+ }
+ }
+ return rv;
+};
+
+
+/**
+ * Calls f for each value in a collection. If any call returns true this returns
+ * true (without checking the rest). If all returns false this returns false.
+ *
+ * @param {S} col The collection-like object.
+ * @param {function(this:T,?,?,S):boolean} f The function to call for every
+ * value. This function takes 3 arguments (the value, the key or undefined
+ * if the collection has no notion of keys, and the collection) and should
+ * return a boolean.
+ * @param {T=} opt_obj The object to be used as the value of 'this'
+ * within {@code f}.
+ * @return {boolean} True if any value passes the test.
+ * @template T,S
+ */
+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(/** @type {!Array<?>} */ (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(/** @type {?} */ (opt_obj), values[i], keys && keys[i], col)) {
+ return true;
+ }
+ }
+ return false;
+};
+
+
+/**
+ * Calls f for each value in a collection. If all calls return true this return
+ * true this returns true. If any returns false this returns false at this point
+ * and does not continue to check the remaining values.
+ *
+ * @param {S} col The collection-like object.
+ * @param {function(this:T,?,?,S):boolean} f The function to call for every
+ * value. This function takes 3 arguments (the value, the key or
+ * undefined if the collection has no notion of keys, and the collection)
+ * and should return a boolean.
+ * @param {T=} opt_obj The object to be used as the value of 'this'
+ * within {@code f}.
+ * @return {boolean} True if all key-value pairs pass the test.
+ * @template T,S
+ */
+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(/** @type {!Array<?>} */ (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(/** @type {?} */ (opt_obj), values[i], keys && keys[i], col)) {
+ return false;
+ }
+ }
+ return true;
+};
diff --git a/chromium/third_party/ink/closure/style/style.js b/chromium/third_party/ink/closure/style/style.js
new file mode 100644
index 00000000000..ff44b0997df
--- /dev/null
+++ b/chromium/third_party/ink/closure/style/style.js
@@ -0,0 +1,2046 @@
+// Copyright 2006 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Utilities for element styles.
+ *
+ * @author pupius@google.com (Daniel Pupius)
+ * @author arv@google.com (Erik Arvidsson)
+ * @author eae@google.com (Emil A Eklund)
+ * @author pallosp@google.com (Peter Pallos)
+ * @see ../demos/inline_block_quirks.html
+ * @see ../demos/inline_block_standards.html
+ * @see ../demos/style_viewport.html
+ */
+
+goog.provide('goog.style');
+
+
+goog.require('goog.array');
+goog.require('goog.asserts');
+goog.require('goog.dom');
+goog.require('goog.dom.NodeType');
+goog.require('goog.dom.TagName');
+goog.require('goog.dom.vendor');
+goog.require('goog.html.SafeStyleSheet');
+goog.require('goog.math.Box');
+goog.require('goog.math.Coordinate');
+goog.require('goog.math.Rect');
+goog.require('goog.math.Size');
+goog.require('goog.object');
+goog.require('goog.reflect');
+goog.require('goog.string');
+goog.require('goog.userAgent');
+
+goog.forwardDeclare('goog.events.Event');
+
+
+/**
+ * Sets a style value on an element.
+ *
+ * This function is not indended to patch issues in the browser's style
+ * handling, but to allow easy programmatic access to setting dash-separated
+ * style properties. An example is setting a batch of properties from a data
+ * object without overwriting old styles. When possible, use native APIs:
+ * elem.style.propertyKey = 'value' or (if obliterating old styles is fine)
+ * elem.style.cssText = 'property1: value1; property2: value2'.
+ *
+ * @param {Element} element The element to change.
+ * @param {string|Object} style If a string, a style name. If an object, a hash
+ * of style names to style values.
+ * @param {string|number|boolean=} opt_value If style was a string, then this
+ * should be the value.
+ */
+goog.style.setStyle = function(element, style, opt_value) {
+ if (goog.isString(style)) {
+ goog.style.setStyle_(element, opt_value, style);
+ } else {
+ for (var key in style) {
+ goog.style.setStyle_(element, style[key], key);
+ }
+ }
+};
+
+
+/**
+ * Sets a style value on an element, with parameters swapped to work with
+ * {@code goog.object.forEach()}. Prepends a vendor-specific prefix when
+ * necessary.
+ * @param {Element} element The element to change.
+ * @param {string|number|boolean|undefined} value Style value.
+ * @param {string} style Style name.
+ * @private
+ */
+goog.style.setStyle_ = function(element, value, style) {
+ var propertyName = goog.style.getVendorJsStyleName_(element, style);
+
+ if (propertyName) {
+ // TODO(johnlenz): coerce to string?
+ element.style[propertyName] = /** @type {?} */ (value);
+ }
+};
+
+
+/**
+ * Style name cache that stores previous property name lookups.
+ *
+ * This is used by setStyle to speed up property lookups, entries look like:
+ * { StyleName: ActualPropertyName }
+ *
+ * @private {!Object<string, string>}
+ */
+goog.style.styleNameCache_ = {};
+
+
+/**
+ * Returns the style property name in camel-case. If it does not exist and a
+ * vendor-specific version of the property does exist, then return the vendor-
+ * specific property name instead.
+ * @param {Element} element The element to change.
+ * @param {string} style Style name.
+ * @return {string} Vendor-specific style.
+ * @private
+ */
+goog.style.getVendorJsStyleName_ = function(element, style) {
+ var propertyName = goog.style.styleNameCache_[style];
+ if (!propertyName) {
+ var camelStyle = goog.string.toCamelCase(style);
+ propertyName = camelStyle;
+
+ if (element.style[camelStyle] === undefined) {
+ var prefixedStyle = goog.dom.vendor.getVendorJsPrefix() +
+ goog.string.toTitleCase(camelStyle);
+
+ if (element.style[prefixedStyle] !== undefined) {
+ propertyName = prefixedStyle;
+ }
+ }
+ goog.style.styleNameCache_[style] = propertyName;
+ }
+
+ return propertyName;
+};
+
+
+/**
+ * Returns the style property name in CSS notation. If it does not exist and a
+ * vendor-specific version of the property does exist, then return the vendor-
+ * specific property name instead.
+ * @param {Element} element The element to change.
+ * @param {string} style Style name.
+ * @return {string} Vendor-specific style.
+ * @private
+ */
+goog.style.getVendorStyleName_ = function(element, style) {
+ var camelStyle = goog.string.toCamelCase(style);
+
+ if (element.style[camelStyle] === undefined) {
+ var prefixedStyle = goog.dom.vendor.getVendorJsPrefix() +
+ goog.string.toTitleCase(camelStyle);
+
+ if (element.style[prefixedStyle] !== undefined) {
+ return goog.dom.vendor.getVendorPrefix() + '-' + style;
+ }
+ }
+
+ return style;
+};
+
+
+/**
+ * Retrieves an explicitly-set style value of a node. This returns '' if there
+ * isn't a style attribute on the element or if this style property has not been
+ * explicitly set in script.
+ *
+ * @param {Element} element Element to get style of.
+ * @param {string} property Property to get, css-style (if you have a camel-case
+ * property, use element.style[style]).
+ * @return {string} Style value.
+ */
+goog.style.getStyle = function(element, property) {
+ // element.style is '' for well-known properties which are unset.
+ // For for browser specific styles as 'filter' is undefined
+ // so we need to return '' explicitly to make it consistent across
+ // browsers.
+ var styleValue = element.style[goog.string.toCamelCase(property)];
+
+ // Using typeof here because of a bug in Safari 5.1, where this value
+ // was undefined, but === undefined returned false.
+ if (typeof(styleValue) !== 'undefined') {
+ return styleValue;
+ }
+
+ return element.style[goog.style.getVendorJsStyleName_(element, property)] ||
+ '';
+};
+
+
+/**
+ * Retrieves a computed style value of a node. It returns empty string if the
+ * value cannot be computed (which will be the case in Internet Explorer) or
+ * "none" if the property requested is an SVG one and it has not been
+ * explicitly set (firefox and webkit).
+ *
+ * @param {Element} element Element to get style of.
+ * @param {string} property Property to get (camel-case).
+ * @return {string} Style value.
+ */
+goog.style.getComputedStyle = function(element, property) {
+ var doc = goog.dom.getOwnerDocument(element);
+ if (doc.defaultView && doc.defaultView.getComputedStyle) {
+ var styles = doc.defaultView.getComputedStyle(element, null);
+ if (styles) {
+ // element.style[..] is undefined for browser specific styles
+ // as 'filter'.
+ return styles[property] || styles.getPropertyValue(property) || '';
+ }
+ }
+
+ return '';
+};
+
+
+/**
+ * Gets the cascaded style value of a node, or null if the value cannot be
+ * computed (only Internet Explorer can do this).
+ *
+ * @param {Element} element Element to get style of.
+ * @param {string} style Property to get (camel-case).
+ * @return {string} Style value.
+ */
+goog.style.getCascadedStyle = function(element, style) {
+ // TODO(nicksantos): This should be documented to return null. #fixTypes
+ return /** @type {string} */ (
+ element.currentStyle ? element.currentStyle[style] : null);
+};
+
+
+/**
+ * Cross-browser pseudo get computed style. It returns the computed style where
+ * available. If not available it tries the cascaded style value (IE
+ * currentStyle) and in worst case the inline style value. It shouldn't be
+ * called directly, see http://wiki/Main/ComputedStyleVsCascadedStyle for
+ * discussion.
+ *
+ * @param {Element} element Element to get style of.
+ * @param {string} style Property to get (must be camelCase, not css-style.).
+ * @return {string} Style value.
+ * @private
+ */
+goog.style.getStyle_ = function(element, style) {
+ return goog.style.getComputedStyle(element, style) ||
+ goog.style.getCascadedStyle(element, style) ||
+ (element.style && element.style[style]);
+};
+
+
+/**
+ * Retrieves the computed value of the box-sizing CSS attribute.
+ * Browser support: http://caniuse.com/css3-boxsizing.
+ * @param {!Element} element The element whose box-sizing to get.
+ * @return {?string} 'content-box', 'border-box' or 'padding-box'. null if
+ * box-sizing is not supported (IE7 and below).
+ */
+goog.style.getComputedBoxSizing = function(element) {
+ return goog.style.getStyle_(element, 'boxSizing') ||
+ goog.style.getStyle_(element, 'MozBoxSizing') ||
+ goog.style.getStyle_(element, 'WebkitBoxSizing') || null;
+};
+
+
+/**
+ * Retrieves the computed value of the position CSS attribute.
+ * @param {Element} element The element to get the position of.
+ * @return {string} Position value.
+ */
+goog.style.getComputedPosition = function(element) {
+ return goog.style.getStyle_(element, 'position');
+};
+
+
+/**
+ * Retrieves the computed background color string for a given element. The
+ * string returned is suitable for assigning to another element's
+ * background-color, but is not guaranteed to be in any particular string
+ * format. Accessing the color in a numeric form may not be possible in all
+ * browsers or with all input.
+ *
+ * If the background color for the element is defined as a hexadecimal value,
+ * the resulting string can be parsed by goog.color.parse in all supported
+ * browsers.
+ *
+ * Whether named colors like "red" or "lightblue" get translated into a
+ * format which can be parsed is browser dependent. Calling this function on
+ * transparent elements will return "transparent" in most browsers or
+ * "rgba(0, 0, 0, 0)" in WebKit.
+ * @param {Element} element The element to get the background color of.
+ * @return {string} The computed string value of the background color.
+ */
+goog.style.getBackgroundColor = function(element) {
+ return goog.style.getStyle_(element, 'backgroundColor');
+};
+
+
+/**
+ * Retrieves the computed value of the overflow-x CSS attribute.
+ * @param {Element} element The element to get the overflow-x of.
+ * @return {string} The computed string value of the overflow-x attribute.
+ */
+goog.style.getComputedOverflowX = function(element) {
+ return goog.style.getStyle_(element, 'overflowX');
+};
+
+
+/**
+ * Retrieves the computed value of the overflow-y CSS attribute.
+ * @param {Element} element The element to get the overflow-y of.
+ * @return {string} The computed string value of the overflow-y attribute.
+ */
+goog.style.getComputedOverflowY = function(element) {
+ return goog.style.getStyle_(element, 'overflowY');
+};
+
+
+/**
+ * Retrieves the computed value of the z-index CSS attribute.
+ * @param {Element} element The element to get the z-index of.
+ * @return {string|number} The computed value of the z-index attribute.
+ */
+goog.style.getComputedZIndex = function(element) {
+ return goog.style.getStyle_(element, 'zIndex');
+};
+
+
+/**
+ * Retrieves the computed value of the text-align CSS attribute.
+ * @param {Element} element The element to get the text-align of.
+ * @return {string} The computed string value of the text-align attribute.
+ */
+goog.style.getComputedTextAlign = function(element) {
+ return goog.style.getStyle_(element, 'textAlign');
+};
+
+
+/**
+ * Retrieves the computed value of the cursor CSS attribute.
+ * @param {Element} element The element to get the cursor of.
+ * @return {string} The computed string value of the cursor attribute.
+ */
+goog.style.getComputedCursor = function(element) {
+ return goog.style.getStyle_(element, 'cursor');
+};
+
+
+/**
+ * Retrieves the computed value of the CSS transform attribute.
+ * @param {Element} element The element to get the transform of.
+ * @return {string} The computed string representation of the transform matrix.
+ */
+goog.style.getComputedTransform = function(element) {
+ var property = goog.style.getVendorStyleName_(element, 'transform');
+ return goog.style.getStyle_(element, property) ||
+ goog.style.getStyle_(element, 'transform');
+};
+
+
+/**
+ * Sets the top/left values of an element. If no unit is specified in the
+ * argument then it will add px. The second argument is required if the first
+ * argument is a string or number and is ignored if the first argument
+ * is a coordinate.
+ * @param {Element} el Element to move.
+ * @param {string|number|goog.math.Coordinate} arg1 Left position or coordinate.
+ * @param {string|number=} opt_arg2 Top position.
+ */
+goog.style.setPosition = function(el, arg1, opt_arg2) {
+ var x, y;
+
+ if (arg1 instanceof goog.math.Coordinate) {
+ x = arg1.x;
+ y = arg1.y;
+ } else {
+ x = arg1;
+ y = opt_arg2;
+ }
+
+ el.style.left = goog.style.getPixelStyleValue_(
+ /** @type {number|string} */ (x), false);
+ el.style.top = goog.style.getPixelStyleValue_(
+ /** @type {number|string} */ (y), false);
+};
+
+
+/**
+ * Gets the offsetLeft and offsetTop properties of an element and returns them
+ * in a Coordinate object
+ * @param {Element} element Element.
+ * @return {!goog.math.Coordinate} The position.
+ */
+goog.style.getPosition = function(element) {
+ return new goog.math.Coordinate(
+ /** @type {!HTMLElement} */ (element).offsetLeft,
+ /** @type {!HTMLElement} */ (element).offsetTop);
+};
+
+
+/**
+ * Returns the viewport element for a particular document
+ * @param {Node=} opt_node DOM node (Document is OK) to get the viewport element
+ * of.
+ * @return {Element} document.documentElement or document.body.
+ */
+goog.style.getClientViewportElement = function(opt_node) {
+ var doc;
+ if (opt_node) {
+ doc = goog.dom.getOwnerDocument(opt_node);
+ } else {
+ doc = goog.dom.getDocument();
+ }
+
+ // In old IE versions the document.body represented the viewport
+ if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9) &&
+ !goog.dom.getDomHelper(doc).isCss1CompatMode()) {
+ return doc.body;
+ }
+ return doc.documentElement;
+};
+
+
+/**
+ * Calculates the viewport coordinates relative to the page/document
+ * containing the node. The viewport may be the browser viewport for
+ * non-iframe document, or the iframe container for iframe'd document.
+ * @param {!Document} doc The document to use as the reference point.
+ * @return {!goog.math.Coordinate} The page offset of the viewport.
+ */
+goog.style.getViewportPageOffset = function(doc) {
+ var body = doc.body;
+ var documentElement = doc.documentElement;
+ var scrollLeft = body.scrollLeft || documentElement.scrollLeft;
+ var scrollTop = body.scrollTop || documentElement.scrollTop;
+ return new goog.math.Coordinate(scrollLeft, scrollTop);
+};
+
+
+/**
+ * Gets the client rectangle of the DOM element.
+ *
+ * getBoundingClientRect is part of a new CSS object model draft (with a
+ * long-time presence in IE), replacing the error-prone parent offset
+ * computation and the now-deprecated Gecko getBoxObjectFor.
+ *
+ * This utility patches common browser bugs in getBoundingClientRect. It
+ * will fail if getBoundingClientRect is unsupported.
+ *
+ * If the element is not in the DOM, the result is undefined, and an error may
+ * be thrown depending on user agent.
+ *
+ * @param {!Element} el The element whose bounding rectangle is being queried.
+ * @return {Object} A native bounding rectangle with numerical left, top,
+ * right, and bottom. Reported by Firefox to be of object type ClientRect.
+ * @private
+ */
+goog.style.getBoundingClientRect_ = function(el) {
+ var rect;
+ try {
+ rect = el.getBoundingClientRect();
+ } catch (e) {
+ // In IE < 9, calling getBoundingClientRect on an orphan element raises an
+ // "Unspecified Error". All other browsers return zeros.
+ return {'left': 0, 'top': 0, 'right': 0, 'bottom': 0};
+ }
+
+ // Patch the result in IE only, so that this function can be inlined if
+ // compiled for non-IE.
+ if (goog.userAgent.IE && el.ownerDocument.body) {
+ // In IE, most of the time, 2 extra pixels are added to the top and left
+ // due to the implicit 2-pixel inset border. In IE6/7 quirks mode and
+ // IE6 standards mode, this border can be overridden by setting the
+ // document element's border to zero -- thus, we cannot rely on the
+ // offset always being 2 pixels.
+
+ // In quirks mode, the offset can be determined by querying the body's
+ // clientLeft/clientTop, but in standards mode, it is found by querying
+ // the document element's clientLeft/clientTop. Since we already called
+ // getBoundingClientRect we have already forced a reflow, so it is not
+ // too expensive just to query them all.
+
+ // See: http://msdn.microsoft.com/en-us/library/ms536433(VS.85).aspx
+ var doc = el.ownerDocument;
+ rect.left -= doc.documentElement.clientLeft + doc.body.clientLeft;
+ rect.top -= doc.documentElement.clientTop + doc.body.clientTop;
+ }
+ return rect;
+};
+
+
+/**
+ * Returns the first parent that could affect the position of a given element.
+ * @param {Element} element The element to get the offset parent for.
+ * @return {Element} The first offset parent or null if one cannot be found.
+ */
+goog.style.getOffsetParent = function(element) {
+ // element.offsetParent does the right thing in IE7 and below. In other
+ // browsers it only includes elements with position absolute, relative or
+ // fixed, not elements with overflow set to auto or scroll.
+ if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(8)) {
+ goog.asserts.assert(element && 'offsetParent' in element);
+ return element.offsetParent;
+ }
+
+ var doc = goog.dom.getOwnerDocument(element);
+ var positionStyle = goog.style.getStyle_(element, 'position');
+ var skipStatic = positionStyle == 'fixed' || positionStyle == 'absolute';
+ for (var parent = element.parentNode; parent && parent != doc;
+ parent = parent.parentNode) {
+ // Skip shadowDOM roots.
+ if (parent.nodeType == goog.dom.NodeType.DOCUMENT_FRAGMENT && parent.host) {
+ parent = parent.host;
+ }
+ positionStyle =
+ goog.style.getStyle_(/** @type {!Element} */ (parent), 'position');
+ skipStatic = skipStatic && positionStyle == 'static' &&
+ parent != doc.documentElement && parent != doc.body;
+ if (!skipStatic &&
+ (parent.scrollWidth > parent.clientWidth ||
+ parent.scrollHeight > parent.clientHeight ||
+ positionStyle == 'fixed' || positionStyle == 'absolute' ||
+ positionStyle == 'relative')) {
+ return /** @type {!Element} */ (parent);
+ }
+ }
+ return null;
+};
+
+
+/**
+ * Calculates and returns the visible rectangle for a given element. Returns a
+ * box describing the visible portion of the nearest scrollable offset ancestor.
+ * Coordinates are given relative to the document.
+ *
+ * @param {Element} element Element to get the visible rect for.
+ * @return {goog.math.Box} Bounding elementBox describing the visible rect or
+ * null if scrollable ancestor isn't inside the visible viewport.
+ */
+goog.style.getVisibleRectForElement = function(element) {
+ var visibleRect = new goog.math.Box(0, Infinity, Infinity, 0);
+ var dom = goog.dom.getDomHelper(element);
+ var body = dom.getDocument().body;
+ var documentElement = dom.getDocument().documentElement;
+ var scrollEl = dom.getDocumentScrollElement();
+
+ // Determine the size of the visible rect by climbing the dom accounting for
+ // all scrollable containers.
+ for (var el = element; el = goog.style.getOffsetParent(el);) {
+ // clientWidth is zero for inline block elements in IE.
+ // on WEBKIT, body element can have clientHeight = 0 and scrollHeight > 0
+ if ((!goog.userAgent.IE || el.clientWidth != 0) &&
+ (!goog.userAgent.WEBKIT || el.clientHeight != 0 || el != body) &&
+ // body may have overflow set on it, yet we still get the entire
+ // viewport. In some browsers, el.offsetParent may be
+ // document.documentElement, so check for that too.
+ (el != body && el != documentElement &&
+ goog.style.getStyle_(el, 'overflow') != 'visible')) {
+ var pos = goog.style.getPageOffset(el);
+ var client = goog.style.getClientLeftTop(el);
+ pos.x += client.x;
+ pos.y += client.y;
+
+ visibleRect.top = Math.max(visibleRect.top, pos.y);
+ visibleRect.right = Math.min(visibleRect.right, pos.x + el.clientWidth);
+ visibleRect.bottom =
+ Math.min(visibleRect.bottom, pos.y + el.clientHeight);
+ visibleRect.left = Math.max(visibleRect.left, pos.x);
+ }
+ }
+
+ // Clip by window's viewport.
+ var scrollX = scrollEl.scrollLeft, scrollY = scrollEl.scrollTop;
+ visibleRect.left = Math.max(visibleRect.left, scrollX);
+ visibleRect.top = Math.max(visibleRect.top, scrollY);
+ var winSize = dom.getViewportSize();
+ visibleRect.right = Math.min(visibleRect.right, scrollX + winSize.width);
+ visibleRect.bottom = Math.min(visibleRect.bottom, scrollY + winSize.height);
+ return visibleRect.top >= 0 && visibleRect.left >= 0 &&
+ visibleRect.bottom > visibleRect.top &&
+ visibleRect.right > visibleRect.left ?
+ visibleRect :
+ null;
+};
+
+
+/**
+ * Calculate the scroll position of {@code container} with the minimum amount so
+ * that the content and the borders of the given {@code element} become visible.
+ * If the element is bigger than the container, its top left corner will be
+ * aligned as close to the container's top left corner as possible.
+ *
+ * @param {Element} element The element to make visible.
+ * @param {Element=} opt_container The container to scroll. If not set, then the
+ * document scroll element will be used.
+ * @param {boolean=} opt_center Whether to center the element in the container.
+ * Defaults to false.
+ * @return {!goog.math.Coordinate} The new scroll position of the container,
+ * in form of goog.math.Coordinate(scrollLeft, scrollTop).
+ */
+goog.style.getContainerOffsetToScrollInto = function(
+ element, opt_container, opt_center) {
+ var container = opt_container || goog.dom.getDocumentScrollElement();
+ // Absolute position of the element's border's top left corner.
+ var elementPos = goog.style.getPageOffset(element);
+ // Absolute position of the container's border's top left corner.
+ var containerPos = goog.style.getPageOffset(container);
+ var containerBorder = goog.style.getBorderBox(container);
+ if (container == goog.dom.getDocumentScrollElement()) {
+ // The element position is calculated based on the page offset, and the
+ // document scroll element holds the scroll position within the page. We can
+ // use the scroll position to calculate the relative position from the
+ // element.
+ var relX = elementPos.x - container.scrollLeft;
+ var relY = elementPos.y - container.scrollTop;
+ if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(10)) {
+ // In older versions of IE getPageOffset(element) does not include the
+ // container border so it has to be added to accommodate.
+ relX += containerBorder.left;
+ relY += containerBorder.top;
+ }
+ } else {
+ // Relative pos. of the element's border box to the container's content box.
+ var relX = elementPos.x - containerPos.x - containerBorder.left;
+ var relY = elementPos.y - containerPos.y - containerBorder.top;
+ }
+ // How much the element can move in the container, i.e. the difference between
+ // the element's bottom-right-most and top-left-most position where it's
+ // fully visible.
+ var elementSize = goog.style.getSizeWithDisplay_(element);
+ var spaceX = container.clientWidth - elementSize.width;
+ var spaceY = container.clientHeight - elementSize.height;
+ var scrollLeft = container.scrollLeft;
+ var scrollTop = container.scrollTop;
+ if (opt_center) {
+ // All browsers round non-integer scroll positions down.
+ scrollLeft += relX - spaceX / 2;
+ scrollTop += relY - spaceY / 2;
+ } else {
+ // This formula was designed to give the correct scroll values in the
+ // following cases:
+ // - element is higher than container (spaceY < 0) => scroll down by relY
+ // - element is not higher that container (spaceY >= 0):
+ // - it is above container (relY < 0) => scroll up by abs(relY)
+ // - it is below container (relY > spaceY) => scroll down by relY - spaceY
+ // - it is in the container => don't scroll
+ scrollLeft += Math.min(relX, Math.max(relX - spaceX, 0));
+ scrollTop += Math.min(relY, Math.max(relY - spaceY, 0));
+ }
+ return new goog.math.Coordinate(scrollLeft, scrollTop);
+};
+
+
+/**
+ * Changes the scroll position of {@code container} with the minimum amount so
+ * that the content and the borders of the given {@code element} become visible.
+ * If the element is bigger than the container, its top left corner will be
+ * aligned as close to the container's top left corner as possible.
+ *
+ * @param {Element} element The element to make visible.
+ * @param {Element=} opt_container The container to scroll. If not set, then the
+ * document scroll element will be used.
+ * @param {boolean=} opt_center Whether to center the element in the container.
+ * Defaults to false.
+ */
+goog.style.scrollIntoContainerView = function(
+ element, opt_container, opt_center) {
+ var container = opt_container || goog.dom.getDocumentScrollElement();
+ var offset =
+ goog.style.getContainerOffsetToScrollInto(element, container, opt_center);
+ container.scrollLeft = offset.x;
+ container.scrollTop = offset.y;
+};
+
+
+/**
+ * Returns clientLeft (width of the left border and, if the directionality is
+ * right to left, the vertical scrollbar) and clientTop as a coordinate object.
+ *
+ * @param {Element} el Element to get clientLeft for.
+ * @return {!goog.math.Coordinate} Client left and top.
+ */
+goog.style.getClientLeftTop = function(el) {
+ return new goog.math.Coordinate(el.clientLeft, el.clientTop);
+};
+
+
+/**
+ * Returns a Coordinate object relative to the top-left of the HTML document.
+ * Implemented as a single function to save having to do two recursive loops in
+ * opera and safari just to get both coordinates. If you just want one value do
+ * use goog.style.getPageOffsetLeft() and goog.style.getPageOffsetTop(), but
+ * note if you call both those methods the tree will be analysed twice.
+ *
+ * @param {Element} el Element to get the page offset for.
+ * @return {!goog.math.Coordinate} The page offset.
+ */
+goog.style.getPageOffset = function(el) {
+ var doc = goog.dom.getOwnerDocument(el);
+ // TODO(gboyer): Update the jsdoc in a way that doesn't break the universe.
+ goog.asserts.assertObject(el, 'Parameter is required');
+
+ // NOTE(arv): If element is hidden (display none or disconnected or any the
+ // ancestors are hidden) we get (0,0) by default but we still do the
+ // accumulation of scroll position.
+
+ // TODO(arv): Should we check if the node is disconnected and in that case
+ // return (0,0)?
+
+ var pos = new goog.math.Coordinate(0, 0);
+ var viewportElement = goog.style.getClientViewportElement(doc);
+ if (el == viewportElement) {
+ // viewport is always at 0,0 as that defined the coordinate system for this
+ // function - this avoids special case checks in the code below
+ return pos;
+ }
+
+ var box = goog.style.getBoundingClientRect_(el);
+ // Must add the scroll coordinates in to get the absolute page offset
+ // of element since getBoundingClientRect returns relative coordinates to
+ // the viewport.
+ var scrollCoord = goog.dom.getDomHelper(doc).getDocumentScroll();
+ pos.x = box.left + scrollCoord.x;
+ pos.y = box.top + scrollCoord.y;
+
+ return pos;
+};
+
+
+/**
+ * Returns the left coordinate of an element relative to the HTML document
+ * @param {Element} el Elements.
+ * @return {number} The left coordinate.
+ */
+goog.style.getPageOffsetLeft = function(el) {
+ return goog.style.getPageOffset(el).x;
+};
+
+
+/**
+ * Returns the top coordinate of an element relative to the HTML document
+ * @param {Element} el Elements.
+ * @return {number} The top coordinate.
+ */
+goog.style.getPageOffsetTop = function(el) {
+ return goog.style.getPageOffset(el).y;
+};
+
+
+/**
+ * Returns a Coordinate object relative to the top-left of an HTML document
+ * in an ancestor frame of this element. Used for measuring the position of
+ * an element inside a frame relative to a containing frame.
+ *
+ * @param {Element} el Element to get the page offset for.
+ * @param {Window} relativeWin The window to measure relative to. If relativeWin
+ * is not in the ancestor frame chain of the element, we measure relative to
+ * the top-most window.
+ * @return {!goog.math.Coordinate} The page offset.
+ */
+goog.style.getFramedPageOffset = function(el, relativeWin) {
+ var position = new goog.math.Coordinate(0, 0);
+
+ // Iterate up the ancestor frame chain, keeping track of the current window
+ // and the current element in that window.
+ var currentWin = goog.dom.getWindow(goog.dom.getOwnerDocument(el));
+
+ // MS Edge throws when accessing "parent" if el's containing iframe has been
+ // deleted.
+ if (!goog.reflect.canAccessProperty(currentWin, 'parent')) {
+ return position;
+ }
+
+ var currentEl = el;
+ do {
+ // if we're at the top window, we want to get the page offset.
+ // if we're at an inner frame, we only want to get the window position
+ // so that we can determine the actual page offset in the context of
+ // the outer window.
+ var offset = currentWin == relativeWin ?
+ goog.style.getPageOffset(currentEl) :
+ goog.style.getClientPositionForElement_(goog.asserts.assert(currentEl));
+
+ position.x += offset.x;
+ position.y += offset.y;
+ } while (currentWin && currentWin != relativeWin &&
+ currentWin != currentWin.parent &&
+ (currentEl = currentWin.frameElement) &&
+ (currentWin = currentWin.parent));
+
+ return position;
+};
+
+
+/**
+ * Translates the specified rect relative to origBase page, for newBase page.
+ * If origBase and newBase are the same, this function does nothing.
+ *
+ * @param {goog.math.Rect} rect The source rectangle relative to origBase page,
+ * and it will have the translated result.
+ * @param {goog.dom.DomHelper} origBase The DomHelper for the input rectangle.
+ * @param {goog.dom.DomHelper} newBase The DomHelper for the resultant
+ * coordinate. This must be a DOM for an ancestor frame of origBase
+ * or the same as origBase.
+ */
+goog.style.translateRectForAnotherFrame = function(rect, origBase, newBase) {
+ if (origBase.getDocument() != newBase.getDocument()) {
+ var body = origBase.getDocument().body;
+ var pos = goog.style.getFramedPageOffset(body, newBase.getWindow());
+
+ // Adjust Body's margin.
+ pos = goog.math.Coordinate.difference(pos, goog.style.getPageOffset(body));
+
+ if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9) &&
+ !origBase.isCss1CompatMode()) {
+ pos = goog.math.Coordinate.difference(pos, origBase.getDocumentScroll());
+ }
+
+ rect.left += pos.x;
+ rect.top += pos.y;
+ }
+};
+
+
+/**
+ * Returns the position of an element relative to another element in the
+ * document. A relative to B
+ * @param {Element|Event|goog.events.Event} a Element or mouse event whose
+ * position we're calculating.
+ * @param {Element|Event|goog.events.Event} b Element or mouse event position
+ * is relative to.
+ * @return {!goog.math.Coordinate} The relative position.
+ */
+goog.style.getRelativePosition = function(a, b) {
+ var ap = goog.style.getClientPosition(a);
+ var bp = goog.style.getClientPosition(b);
+ return new goog.math.Coordinate(ap.x - bp.x, ap.y - bp.y);
+};
+
+
+/**
+ * Returns the position of the event or the element's border box relative to
+ * the client viewport.
+ * @param {!Element} el Element whose position to get.
+ * @return {!goog.math.Coordinate} The position.
+ * @private
+ */
+goog.style.getClientPositionForElement_ = function(el) {
+ var box = goog.style.getBoundingClientRect_(el);
+ return new goog.math.Coordinate(box.left, box.top);
+};
+
+
+/**
+ * Returns the position of the event or the element's border box relative to
+ * the client viewport. If an event is passed, and if this event is a "touch"
+ * event, then the position of the first changedTouches will be returned.
+ * @param {Element|Event|goog.events.Event} el Element or a mouse / touch event.
+ * @return {!goog.math.Coordinate} The position.
+ */
+goog.style.getClientPosition = function(el) {
+ goog.asserts.assert(el);
+ if (el.nodeType == goog.dom.NodeType.ELEMENT) {
+ return goog.style.getClientPositionForElement_(
+ /** @type {!Element} */ (el));
+ } else {
+ var targetEvent = el.changedTouches ? el.changedTouches[0] : el;
+ return new goog.math.Coordinate(targetEvent.clientX, targetEvent.clientY);
+ }
+};
+
+
+/**
+ * Moves an element to the given coordinates relative to the client viewport.
+ * @param {Element} el Absolutely positioned element to set page offset for.
+ * It must be in the document.
+ * @param {number|goog.math.Coordinate} x Left position of the element's margin
+ * box or a coordinate object.
+ * @param {number=} opt_y Top position of the element's margin box.
+ */
+goog.style.setPageOffset = function(el, x, opt_y) {
+ // Get current pageoffset
+ var cur = goog.style.getPageOffset(el);
+
+ if (x instanceof goog.math.Coordinate) {
+ opt_y = x.y;
+ x = x.x;
+ }
+
+ // NOTE(arv): We cannot allow strings for x and y. We could but that would
+ // require us to manually transform between different units
+
+ // Work out deltas
+ var dx = goog.asserts.assertNumber(x) - cur.x;
+ var dy = Number(opt_y) - cur.y;
+
+ // Set position to current left/top + delta
+ goog.style.setPosition(
+ el, /** @type {!HTMLElement} */ (el).offsetLeft + dx,
+ /** @type {!HTMLElement} */ (el).offsetTop + dy);
+};
+
+
+/**
+ * Sets the width/height values of an element. If an argument is numeric,
+ * or a goog.math.Size is passed, it is assumed to be pixels and will add
+ * 'px' after converting it to an integer in string form. (This just sets the
+ * CSS width and height properties so it might set content-box or border-box
+ * size depending on the box model the browser is using.)
+ *
+ * @param {Element} element Element to set the size of.
+ * @param {string|number|goog.math.Size} w Width of the element, or a
+ * size object.
+ * @param {string|number=} opt_h Height of the element. Required if w is not a
+ * size object.
+ */
+goog.style.setSize = function(element, w, opt_h) {
+ var h;
+ if (w instanceof goog.math.Size) {
+ h = w.height;
+ w = w.width;
+ } else {
+ if (opt_h == undefined) {
+ throw new Error('missing height argument');
+ }
+ h = opt_h;
+ }
+
+ goog.style.setWidth(element, /** @type {string|number} */ (w));
+ goog.style.setHeight(element, h);
+};
+
+
+/**
+ * Helper function to create a string to be set into a pixel-value style
+ * property of an element. Can round to the nearest integer value.
+ *
+ * @param {string|number} value The style value to be used. If a number,
+ * 'px' will be appended, otherwise the value will be applied directly.
+ * @param {boolean} round Whether to round the nearest integer (if property
+ * is a number).
+ * @return {string} The string value for the property.
+ * @private
+ */
+goog.style.getPixelStyleValue_ = function(value, round) {
+ if (typeof value == 'number') {
+ value = (round ? Math.round(value) : value) + 'px';
+ }
+
+ return value;
+};
+
+
+/**
+ * Set the height of an element. Sets the element's style property.
+ * @param {Element} element Element to set the height of.
+ * @param {string|number} height The height value to set. If a number, 'px'
+ * will be appended, otherwise the value will be applied directly.
+ */
+goog.style.setHeight = function(element, height) {
+ element.style.height = goog.style.getPixelStyleValue_(height, true);
+};
+
+
+/**
+ * Set the width of an element. Sets the element's style property.
+ * @param {Element} element Element to set the width of.
+ * @param {string|number} width The width value to set. If a number, 'px'
+ * will be appended, otherwise the value will be applied directly.
+ */
+goog.style.setWidth = function(element, width) {
+ element.style.width = goog.style.getPixelStyleValue_(width, true);
+};
+
+
+/**
+ * Gets the height and width of an element, even if its display is none.
+ *
+ * Specifically, this returns the height and width of the border box,
+ * irrespective of the box model in effect.
+ *
+ * Note that this function does not take CSS transforms into account. Please see
+ * {@code goog.style.getTransformedSize}.
+ * @param {Element} element Element to get size of.
+ * @return {!goog.math.Size} Object with width/height properties.
+ */
+goog.style.getSize = function(element) {
+ return goog.style.evaluateWithTemporaryDisplay_(
+ goog.style.getSizeWithDisplay_, /** @type {!Element} */ (element));
+};
+
+
+/**
+ * Call {@code fn} on {@code element} such that {@code element}'s dimensions are
+ * accurate when it's passed to {@code fn}.
+ * @param {function(!Element): T} fn Function to call with {@code element} as
+ * an argument after temporarily changing {@code element}'s display such
+ * that its dimensions are accurate.
+ * @param {!Element} element Element (which may have display none) to use as
+ * argument to {@code fn}.
+ * @return {T} Value returned by calling {@code fn} with {@code element}.
+ * @template T
+ * @private
+ */
+goog.style.evaluateWithTemporaryDisplay_ = function(fn, element) {
+ if (goog.style.getStyle_(element, 'display') != 'none') {
+ return fn(element);
+ }
+
+ var style = element.style;
+ var originalDisplay = style.display;
+ var originalVisibility = style.visibility;
+ var originalPosition = style.position;
+
+ style.visibility = 'hidden';
+ style.position = 'absolute';
+ style.display = 'inline';
+
+ var retVal = fn(element);
+
+ style.display = originalDisplay;
+ style.position = originalPosition;
+ style.visibility = originalVisibility;
+
+ return retVal;
+};
+
+
+/**
+ * Gets the height and width of an element when the display is not none.
+ * @param {Element} element Element to get size of.
+ * @return {!goog.math.Size} Object with width/height properties.
+ * @private
+ */
+goog.style.getSizeWithDisplay_ = function(element) {
+ var offsetWidth = /** @type {!HTMLElement} */ (element).offsetWidth;
+ var offsetHeight = /** @type {!HTMLElement} */ (element).offsetHeight;
+ var webkitOffsetsZero =
+ goog.userAgent.WEBKIT && !offsetWidth && !offsetHeight;
+ if ((!goog.isDef(offsetWidth) || webkitOffsetsZero) &&
+ element.getBoundingClientRect) {
+ // Fall back to calling getBoundingClientRect when offsetWidth or
+ // offsetHeight are not defined, or when they are zero in WebKit browsers.
+ // This makes sure that we return for the correct size for SVG elements, but
+ // will still return 0 on Webkit prior to 534.8, see
+ // http://trac.webkit.org/changeset/67252.
+ var clientRect = goog.style.getBoundingClientRect_(element);
+ return new goog.math.Size(
+ clientRect.right - clientRect.left, clientRect.bottom - clientRect.top);
+ }
+ return new goog.math.Size(offsetWidth, offsetHeight);
+};
+
+
+/**
+ * Gets the height and width of an element, post transform, even if its display
+ * is none.
+ *
+ * This is like {@code goog.style.getSize}, except:
+ * <ol>
+ * <li>Takes webkitTransforms such as rotate and scale into account.
+ * <li>Will return null if {@code element} doesn't respond to
+ * {@code getBoundingClientRect}.
+ * <li>Currently doesn't make sense on non-WebKit browsers which don't support
+ * webkitTransforms.
+ * </ol>
+ * @param {!Element} element Element to get size of.
+ * @return {goog.math.Size} Object with width/height properties.
+ */
+goog.style.getTransformedSize = function(element) {
+ if (!element.getBoundingClientRect) {
+ return null;
+ }
+
+ var clientRect = goog.style.evaluateWithTemporaryDisplay_(
+ goog.style.getBoundingClientRect_, element);
+ return new goog.math.Size(
+ clientRect.right - clientRect.left, clientRect.bottom - clientRect.top);
+};
+
+
+/**
+ * Returns a bounding rectangle for a given element in page space.
+ * @param {Element} element Element to get bounds of. Must not be display none.
+ * @return {!goog.math.Rect} Bounding rectangle for the element.
+ */
+goog.style.getBounds = function(element) {
+ var o = goog.style.getPageOffset(element);
+ var s = goog.style.getSize(element);
+ return new goog.math.Rect(o.x, o.y, s.width, s.height);
+};
+
+
+/**
+ * Converts a CSS selector in the form style-property to styleProperty.
+ * @param {*} selector CSS Selector.
+ * @return {string} Camel case selector.
+ * @deprecated Use goog.string.toCamelCase instead.
+ */
+goog.style.toCamelCase = function(selector) {
+ return goog.string.toCamelCase(String(selector));
+};
+
+
+/**
+ * Converts a CSS selector in the form styleProperty to style-property.
+ * @param {string} selector Camel case selector.
+ * @return {string} Selector cased.
+ * @deprecated Use goog.string.toSelectorCase instead.
+ */
+goog.style.toSelectorCase = function(selector) {
+ return goog.string.toSelectorCase(selector);
+};
+
+
+/**
+ * Gets the opacity of a node (x-browser). This gets the inline style opacity
+ * of the node, and does not take into account the cascaded or the computed
+ * style for this node.
+ * @param {Element} el Element whose opacity has to be found.
+ * @return {number|string} Opacity between 0 and 1 or an empty string {@code ''}
+ * if the opacity is not set.
+ */
+goog.style.getOpacity = function(el) {
+ goog.asserts.assert(el);
+ var style = el.style;
+ var result = '';
+ if ('opacity' in style) {
+ result = style.opacity;
+ } else if ('MozOpacity' in style) {
+ result = style.MozOpacity;
+ } else if ('filter' in style) {
+ var match = style.filter.match(/alpha\(opacity=([\d.]+)\)/);
+ if (match) {
+ result = String(match[1] / 100);
+ }
+ }
+ return result == '' ? result : Number(result);
+};
+
+
+/**
+ * Sets the opacity of a node (x-browser).
+ * @param {Element} el Elements whose opacity has to be set.
+ * @param {number|string} alpha Opacity between 0 and 1 or an empty string
+ * {@code ''} to clear the opacity.
+ */
+goog.style.setOpacity = function(el, alpha) {
+ goog.asserts.assert(el);
+ var style = el.style;
+ if ('opacity' in style) {
+ style.opacity = alpha;
+ } else if ('MozOpacity' in style) {
+ style.MozOpacity = alpha;
+ } else if ('filter' in style) {
+ // TODO(arv): Overwriting the filter might have undesired side effects.
+ if (alpha === '') {
+ style.filter = '';
+ } else {
+ style.filter = 'alpha(opacity=' + (Number(alpha) * 100) + ')';
+ }
+ }
+};
+
+
+/**
+ * Sets the background of an element to a transparent image in a browser-
+ * independent manner.
+ *
+ * This function does not support repeating backgrounds or alternate background
+ * positions to match the behavior of Internet Explorer. It also does not
+ * support sizingMethods other than crop since they cannot be replicated in
+ * browsers other than Internet Explorer.
+ *
+ * @param {Element} el The element to set background on.
+ * @param {string} src The image source URL.
+ */
+goog.style.setTransparentBackgroundImage = function(el, src) {
+ var style = el.style;
+ // It is safe to use the style.filter in IE only. In Safari 'filter' is in
+ // style object but access to style.filter causes it to throw an exception.
+ // Note: IE8 supports images with an alpha channel.
+ if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('8')) {
+ // See TODO in setOpacity.
+ style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(' +
+ 'src="' + src + '", sizingMethod="crop")';
+ } else {
+ // Set style properties individually instead of using background shorthand
+ // to prevent overwriting a pre-existing background color.
+ style.backgroundImage = 'url(' + src + ')';
+ style.backgroundPosition = 'top left';
+ style.backgroundRepeat = 'no-repeat';
+ }
+};
+
+
+/**
+ * Clears the background image of an element in a browser independent manner.
+ * @param {Element} el The element to clear background image for.
+ */
+goog.style.clearTransparentBackgroundImage = function(el) {
+ var style = el.style;
+ if ('filter' in style) {
+ // See TODO in setOpacity.
+ style.filter = '';
+ } else {
+ // Set style properties individually instead of using background shorthand
+ // to prevent overwriting a pre-existing background color.
+ style.backgroundImage = 'none';
+ }
+};
+
+
+/**
+ * Shows or hides an element from the page. Hiding the element is done by
+ * setting the display property to "none", removing the element from the
+ * rendering hierarchy so it takes up no space. To show the element, the default
+ * inherited display property is restored (defined either in stylesheets or by
+ * the browser's default style rules.)
+ *
+ * Caveat 1: if the inherited display property for the element is set to "none"
+ * by the stylesheets, that is the property that will be restored by a call to
+ * showElement(), effectively toggling the display between "none" and "none".
+ *
+ * Caveat 2: if the element display style is set inline (by setting either
+ * element.style.display or a style attribute in the HTML), a call to
+ * showElement will clear that setting and defer to the inherited style in the
+ * stylesheet.
+ * @param {Element} el Element to show or hide.
+ * @param {*} display True to render the element in its default style,
+ * false to disable rendering the element.
+ * @deprecated Use goog.style.setElementShown instead.
+ */
+goog.style.showElement = function(el, display) {
+ goog.style.setElementShown(el, display);
+};
+
+
+/**
+ * Shows or hides an element from the page. Hiding the element is done by
+ * setting the display property to "none", removing the element from the
+ * rendering hierarchy so it takes up no space. To show the element, the default
+ * inherited display property is restored (defined either in stylesheets or by
+ * the browser's default style rules).
+ *
+ * Caveat 1: if the inherited display property for the element is set to "none"
+ * by the stylesheets, that is the property that will be restored by a call to
+ * setElementShown(), effectively toggling the display between "none" and
+ * "none".
+ *
+ * Caveat 2: if the element display style is set inline (by setting either
+ * element.style.display or a style attribute in the HTML), a call to
+ * setElementShown will clear that setting and defer to the inherited style in
+ * the stylesheet.
+ * @param {Element} el Element to show or hide.
+ * @param {*} isShown True to render the element in its default style,
+ * false to disable rendering the element.
+ */
+goog.style.setElementShown = function(el, isShown) {
+ el.style.display = isShown ? '' : 'none';
+};
+
+
+/**
+ * Test whether the given element has been shown or hidden via a call to
+ * {@link #setElementShown}.
+ *
+ * Note this is strictly a companion method for a call
+ * to {@link #setElementShown} and the same caveats apply; in particular, this
+ * method does not guarantee that the return value will be consistent with
+ * whether or not the element is actually visible.
+ *
+ * @param {Element} el The element to test.
+ * @return {boolean} Whether the element has been shown.
+ * @see #setElementShown
+ */
+goog.style.isElementShown = function(el) {
+ return el.style.display != 'none';
+};
+
+
+/**
+ * Installs the style sheet into the window that contains opt_node. If
+ * opt_node is null, the main window is used.
+ * @param {!goog.html.SafeStyleSheet} safeStyleSheet The style sheet to install.
+ * @param {?Node=} opt_node Node whose parent document should have the
+ * styles installed.
+ * @return {!HTMLStyleElement|!StyleSheet} In IE<11, a StyleSheet object with no
+ * owning <style> tag (this is how IE creates style sheets). In every other
+ * browser, a <style> element with an attached style. This doesn't return a
+ * StyleSheet object so that setSafeStyleSheet can replace it (otherwise, if
+ * you pass a StyleSheet to setSafeStyleSheet, it will make a new StyleSheet
+ * and leave the original StyleSheet orphaned).
+ */
+goog.style.installSafeStyleSheet = function(safeStyleSheet, opt_node) {
+ var dh = goog.dom.getDomHelper(opt_node);
+
+ // IE < 11 requires createStyleSheet. Note that doc.createStyleSheet will be
+ // undefined as of IE 11.
+ var doc = dh.getDocument();
+ if (goog.userAgent.IE && doc.createStyleSheet) {
+ var styleSheet = doc.createStyleSheet();
+ goog.style.setSafeStyleSheet(styleSheet, safeStyleSheet);
+ return styleSheet;
+ } else {
+ var head = dh.getElementsByTagNameAndClass(goog.dom.TagName.HEAD)[0];
+
+ // In opera documents are not guaranteed to have a head element, thus we
+ // have to make sure one exists before using it.
+ if (!head) {
+ var body = dh.getElementsByTagNameAndClass(goog.dom.TagName.BODY)[0];
+ head = dh.createDom(goog.dom.TagName.HEAD);
+ body.parentNode.insertBefore(head, body);
+ }
+ var el = dh.createDom(goog.dom.TagName.STYLE);
+ // NOTE(vkarun): Setting styles after the style element has been appended
+ // to the head results in a nasty Webkit bug in certain scenarios. Please
+ // refer to https://bugs.webkit.org/show_bug.cgi?id=26307 for additional
+ // details.
+ goog.style.setSafeStyleSheet(el, safeStyleSheet);
+ dh.appendChild(head, el);
+ return el;
+ }
+};
+
+
+/**
+ * Removes the styles added by {@link #installStyles}.
+ * @param {!HTMLStyleElement|!StyleSheet} styleSheet The value returned by
+ * {@link #installStyles}.
+ */
+goog.style.uninstallStyles = function(styleSheet) {
+ var node = styleSheet.ownerNode || styleSheet.owningElement ||
+ /** @type {Element} */ (styleSheet);
+ goog.dom.removeNode(node);
+};
+
+
+/**
+ * Sets the content of a style element. The style element can be any valid
+ * style element. This element will have its content completely replaced by
+ * the safeStyleSheet.
+ * @param {!HTMLStyleElement|!StyleSheet} element A <style> element, as returned
+ * by installStyles (or a Stylesheet in IE<11).
+ * @param {!goog.html.SafeStyleSheet} safeStyleSheet The new content of the
+ * stylesheet.
+ */
+goog.style.setSafeStyleSheet = function(element, safeStyleSheet) {
+ var stylesString = goog.html.SafeStyleSheet.unwrap(safeStyleSheet);
+ if (goog.userAgent.IE && goog.isDef(element.cssText)) {
+ // Adding the selectors individually caused the browser to hang if the
+ // selector was invalid or there were CSS comments. Setting the cssText of
+ // the style node works fine and ignores CSS that IE doesn't understand.
+ // However IE >= 11 doesn't support cssText any more, so we make sure that
+ // cssText is a defined property and otherwise fall back to innerHTML.
+ element.cssText = stylesString;
+ } else {
+ // Setting textContent doesn't work in Safari, see b/29340337.
+ element.innerHTML = stylesString;
+ }
+};
+
+
+/**
+ * Sets 'white-space: pre-wrap' for a node (x-browser).
+ *
+ * There are as many ways of specifying pre-wrap as there are browsers.
+ *
+ * CSS3/IE8: white-space: pre-wrap;
+ * Mozilla: white-space: -moz-pre-wrap;
+ * Opera: white-space: -o-pre-wrap;
+ * IE6/7: white-space: pre; word-wrap: break-word;
+ *
+ * @param {Element} el Element to enable pre-wrap for.
+ */
+goog.style.setPreWrap = function(el) {
+ var style = el.style;
+ if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('8')) {
+ style.whiteSpace = 'pre';
+ style.wordWrap = 'break-word';
+ } else if (goog.userAgent.GECKO) {
+ style.whiteSpace = '-moz-pre-wrap';
+ } else {
+ style.whiteSpace = 'pre-wrap';
+ }
+};
+
+
+/**
+ * Sets 'display: inline-block' for an element (cross-browser).
+ * @param {Element} el Element to which the inline-block display style is to be
+ * applied.
+ * @see ../demos/inline_block_quirks.html
+ * @see ../demos/inline_block_standards.html
+ */
+goog.style.setInlineBlock = function(el) {
+ var style = el.style;
+ // Without position:relative, weirdness ensues. Just accept it and move on.
+ style.position = 'relative';
+
+ if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('8')) {
+ // IE8 supports inline-block so fall through to the else
+ // Zoom:1 forces hasLayout, display:inline gives inline behavior.
+ style.zoom = '1';
+ style.display = 'inline';
+ } else {
+ // Opera, Webkit, and Safari seem to do OK with the standard inline-block
+ // style.
+ style.display = 'inline-block';
+ }
+};
+
+
+/**
+ * Returns true if the element is using right to left (rtl) direction.
+ * @param {Element} el The element to test.
+ * @return {boolean} True for right to left, false for left to right.
+ */
+goog.style.isRightToLeft = function(el) {
+ return 'rtl' == goog.style.getStyle_(el, 'direction');
+};
+
+
+/**
+ * The CSS style property corresponding to an element being
+ * unselectable on the current browser platform (null if none).
+ * Opera and IE instead use a DOM attribute 'unselectable'. MS Edge uses
+ * the Webkit prefix.
+ * @type {?string}
+ * @private
+ */
+goog.style.unselectableStyle_ = goog.userAgent.GECKO ?
+ 'MozUserSelect' :
+ goog.userAgent.WEBKIT || goog.userAgent.EDGE ? 'WebkitUserSelect' : null;
+
+
+/**
+ * Returns true if the element is set to be unselectable, false otherwise.
+ * Note that on some platforms (e.g. Mozilla), even if an element isn't set
+ * to be unselectable, it will behave as such if any of its ancestors is
+ * unselectable.
+ * @param {Element} el Element to check.
+ * @return {boolean} Whether the element is set to be unselectable.
+ */
+goog.style.isUnselectable = function(el) {
+ if (goog.style.unselectableStyle_) {
+ return el.style[goog.style.unselectableStyle_].toLowerCase() == 'none';
+ } else if (goog.userAgent.IE || goog.userAgent.OPERA) {
+ return el.getAttribute('unselectable') == 'on';
+ }
+ return false;
+};
+
+
+/**
+ * Makes the element and its descendants selectable or unselectable. Note
+ * that on some platforms (e.g. Mozilla), even if an element isn't set to
+ * be unselectable, it will behave as such if any of its ancestors is
+ * unselectable.
+ * @param {Element} el The element to alter.
+ * @param {boolean} unselectable Whether the element and its descendants
+ * should be made unselectable.
+ * @param {boolean=} opt_noRecurse Whether to only alter the element's own
+ * selectable state, and leave its descendants alone; defaults to false.
+ */
+goog.style.setUnselectable = function(el, unselectable, opt_noRecurse) {
+ // TODO(attila): Do we need all of TR_DomUtil.makeUnselectable() in Closure?
+ var descendants = !opt_noRecurse ? el.getElementsByTagName('*') : null;
+ var name = goog.style.unselectableStyle_;
+ if (name) {
+ // Add/remove the appropriate CSS style to/from the element and its
+ // descendants.
+ var value = unselectable ? 'none' : '';
+ // MathML elements do not have a style property. Verify before setting.
+ if (el.style) {
+ el.style[name] = value;
+ }
+ if (descendants) {
+ for (var i = 0, descendant; descendant = descendants[i]; i++) {
+ if (descendant.style) {
+ descendant.style[name] = value;
+ }
+ }
+ }
+ } else if (goog.userAgent.IE || goog.userAgent.OPERA) {
+ // Toggle the 'unselectable' attribute on the element and its descendants.
+ var value = unselectable ? 'on' : '';
+ el.setAttribute('unselectable', value);
+ if (descendants) {
+ for (var i = 0, descendant; descendant = descendants[i]; i++) {
+ descendant.setAttribute('unselectable', value);
+ }
+ }
+ }
+};
+
+
+/**
+ * Gets the border box size for an element.
+ * @param {Element} element The element to get the size for.
+ * @return {!goog.math.Size} The border box size.
+ */
+goog.style.getBorderBoxSize = function(element) {
+ return new goog.math.Size(
+ /** @type {!HTMLElement} */ (element).offsetWidth,
+ /** @type {!HTMLElement} */ (element).offsetHeight);
+};
+
+
+/**
+ * Sets the border box size of an element. This is potentially expensive in IE
+ * if the document is CSS1Compat mode
+ * @param {Element} element The element to set the size on.
+ * @param {goog.math.Size} size The new size.
+ */
+goog.style.setBorderBoxSize = function(element, size) {
+ var doc = goog.dom.getOwnerDocument(element);
+ var isCss1CompatMode = goog.dom.getDomHelper(doc).isCss1CompatMode();
+
+ if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('10') &&
+ (!isCss1CompatMode || !goog.userAgent.isVersionOrHigher('8'))) {
+ var style = element.style;
+ if (isCss1CompatMode) {
+ var paddingBox = goog.style.getPaddingBox(element);
+ var borderBox = goog.style.getBorderBox(element);
+ style.pixelWidth = size.width - borderBox.left - paddingBox.left -
+ paddingBox.right - borderBox.right;
+ style.pixelHeight = size.height - borderBox.top - paddingBox.top -
+ paddingBox.bottom - borderBox.bottom;
+ } else {
+ style.pixelWidth = size.width;
+ style.pixelHeight = size.height;
+ }
+ } else {
+ goog.style.setBoxSizingSize_(element, size, 'border-box');
+ }
+};
+
+
+/**
+ * Gets the content box size for an element. This is potentially expensive in
+ * all browsers.
+ * @param {Element} element The element to get the size for.
+ * @return {!goog.math.Size} The content box size.
+ */
+goog.style.getContentBoxSize = function(element) {
+ var doc = goog.dom.getOwnerDocument(element);
+ var ieCurrentStyle = goog.userAgent.IE && element.currentStyle;
+ if (ieCurrentStyle && goog.dom.getDomHelper(doc).isCss1CompatMode() &&
+ ieCurrentStyle.width != 'auto' && ieCurrentStyle.height != 'auto' &&
+ !ieCurrentStyle.boxSizing) {
+ // If IE in CSS1Compat mode than just use the width and height.
+ // If we have a boxSizing then fall back on measuring the borders etc.
+ var width = goog.style.getIePixelValue_(
+ element, /** @type {string} */ (ieCurrentStyle.width), 'width',
+ 'pixelWidth');
+ var height = goog.style.getIePixelValue_(
+ element, /** @type {string} */ (ieCurrentStyle.height), 'height',
+ 'pixelHeight');
+ return new goog.math.Size(width, height);
+ } else {
+ var borderBoxSize = goog.style.getBorderBoxSize(element);
+ var paddingBox = goog.style.getPaddingBox(element);
+ var borderBox = goog.style.getBorderBox(element);
+ return new goog.math.Size(
+ borderBoxSize.width - borderBox.left - paddingBox.left -
+ paddingBox.right - borderBox.right,
+ borderBoxSize.height - borderBox.top - paddingBox.top -
+ paddingBox.bottom - borderBox.bottom);
+ }
+};
+
+
+/**
+ * Sets the content box size of an element. This is potentially expensive in IE
+ * if the document is BackCompat mode.
+ * @param {Element} element The element to set the size on.
+ * @param {goog.math.Size} size The new size.
+ */
+goog.style.setContentBoxSize = function(element, size) {
+ var doc = goog.dom.getOwnerDocument(element);
+ var isCss1CompatMode = goog.dom.getDomHelper(doc).isCss1CompatMode();
+ if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('10') &&
+ (!isCss1CompatMode || !goog.userAgent.isVersionOrHigher('8'))) {
+ var style = element.style;
+ if (isCss1CompatMode) {
+ style.pixelWidth = size.width;
+ style.pixelHeight = size.height;
+ } else {
+ var paddingBox = goog.style.getPaddingBox(element);
+ var borderBox = goog.style.getBorderBox(element);
+ style.pixelWidth = size.width + borderBox.left + paddingBox.left +
+ paddingBox.right + borderBox.right;
+ style.pixelHeight = size.height + borderBox.top + paddingBox.top +
+ paddingBox.bottom + borderBox.bottom;
+ }
+ } else {
+ goog.style.setBoxSizingSize_(element, size, 'content-box');
+ }
+};
+
+
+/**
+ * Helper function that sets the box sizing as well as the width and height
+ * @param {Element} element The element to set the size on.
+ * @param {goog.math.Size} size The new size to set.
+ * @param {string} boxSizing The box-sizing value.
+ * @private
+ */
+goog.style.setBoxSizingSize_ = function(element, size, boxSizing) {
+ var style = element.style;
+ if (goog.userAgent.GECKO) {
+ style.MozBoxSizing = boxSizing;
+ } else if (goog.userAgent.WEBKIT) {
+ style.WebkitBoxSizing = boxSizing;
+ } else {
+ // Includes IE8 and Opera 9.50+
+ style.boxSizing = boxSizing;
+ }
+
+ // Setting this to a negative value will throw an exception on IE
+ // (and doesn't do anything different than setting it to 0).
+ style.width = Math.max(size.width, 0) + 'px';
+ style.height = Math.max(size.height, 0) + 'px';
+};
+
+
+/**
+ * IE specific function that converts a non pixel unit to pixels.
+ * @param {Element} element The element to convert the value for.
+ * @param {string} value The current value as a string. The value must not be
+ * ''.
+ * @param {string} name The CSS property name to use for the converstion. This
+ * should be 'left', 'top', 'width' or 'height'.
+ * @param {string} pixelName The CSS pixel property name to use to get the
+ * value in pixels.
+ * @return {number} The value in pixels.
+ * @private
+ */
+goog.style.getIePixelValue_ = function(element, value, name, pixelName) {
+ // Try if we already have a pixel value. IE does not do half pixels so we
+ // only check if it matches a number followed by 'px'.
+ if (/^\d+px?$/.test(value)) {
+ return parseInt(value, 10);
+ } else {
+ var oldStyleValue = element.style[name];
+ var oldRuntimeValue = element.runtimeStyle[name];
+ // set runtime style to prevent changes
+ element.runtimeStyle[name] = element.currentStyle[name];
+ element.style[name] = value;
+ var pixelValue = element.style[pixelName];
+ // restore
+ element.style[name] = oldStyleValue;
+ element.runtimeStyle[name] = oldRuntimeValue;
+ return +pixelValue;
+ }
+};
+
+
+/**
+ * Helper function for getting the pixel padding or margin for IE.
+ * @param {Element} element The element to get the padding for.
+ * @param {string} propName The property name.
+ * @return {number} The pixel padding.
+ * @private
+ */
+goog.style.getIePixelDistance_ = function(element, propName) {
+ var value = goog.style.getCascadedStyle(element, propName);
+ return value ?
+ goog.style.getIePixelValue_(element, value, 'left', 'pixelLeft') :
+ 0;
+};
+
+
+/**
+ * Gets the computed paddings or margins (on all sides) in pixels.
+ * @param {Element} element The element to get the padding for.
+ * @param {string} stylePrefix Pass 'padding' to retrieve the padding box,
+ * or 'margin' to retrieve the margin box.
+ * @return {!goog.math.Box} The computed paddings or margins.
+ * @private
+ */
+goog.style.getBox_ = function(element, stylePrefix) {
+ if (goog.userAgent.IE) {
+ var left = goog.style.getIePixelDistance_(element, stylePrefix + 'Left');
+ var right = goog.style.getIePixelDistance_(element, stylePrefix + 'Right');
+ var top = goog.style.getIePixelDistance_(element, stylePrefix + 'Top');
+ var bottom =
+ goog.style.getIePixelDistance_(element, stylePrefix + 'Bottom');
+ return new goog.math.Box(top, right, bottom, left);
+ } else {
+ // On non-IE browsers, getComputedStyle is always non-null.
+ var left = goog.style.getComputedStyle(element, stylePrefix + 'Left');
+ var right = goog.style.getComputedStyle(element, stylePrefix + 'Right');
+ var top = goog.style.getComputedStyle(element, stylePrefix + 'Top');
+ var bottom = goog.style.getComputedStyle(element, stylePrefix + 'Bottom');
+
+ // NOTE(arv): Gecko can return floating point numbers for the computed
+ // style values.
+ return new goog.math.Box(
+ parseFloat(top), parseFloat(right), parseFloat(bottom),
+ parseFloat(left));
+ }
+};
+
+
+/**
+ * Gets the computed paddings (on all sides) in pixels.
+ * @param {Element} element The element to get the padding for.
+ * @return {!goog.math.Box} The computed paddings.
+ */
+goog.style.getPaddingBox = function(element) {
+ return goog.style.getBox_(element, 'padding');
+};
+
+
+/**
+ * Gets the computed margins (on all sides) in pixels.
+ * @param {Element} element The element to get the margins for.
+ * @return {!goog.math.Box} The computed margins.
+ */
+goog.style.getMarginBox = function(element) {
+ return goog.style.getBox_(element, 'margin');
+};
+
+
+/**
+ * A map used to map the border width keywords to a pixel width.
+ * @type {!Object}
+ * @private
+ */
+goog.style.ieBorderWidthKeywords_ = {
+ 'thin': 2,
+ 'medium': 4,
+ 'thick': 6
+};
+
+
+/**
+ * Helper function for IE to get the pixel border.
+ * @param {Element} element The element to get the pixel border for.
+ * @param {string} prop The part of the property name.
+ * @return {number} The value in pixels.
+ * @private
+ */
+goog.style.getIePixelBorder_ = function(element, prop) {
+ if (goog.style.getCascadedStyle(element, prop + 'Style') == 'none') {
+ return 0;
+ }
+ var width = goog.style.getCascadedStyle(element, prop + 'Width');
+ if (width in goog.style.ieBorderWidthKeywords_) {
+ return goog.style.ieBorderWidthKeywords_[width];
+ }
+ return goog.style.getIePixelValue_(element, width, 'left', 'pixelLeft');
+};
+
+
+/**
+ * Gets the computed border widths (on all sides) in pixels
+ * @param {Element} element The element to get the border widths for.
+ * @return {!goog.math.Box} The computed border widths.
+ */
+goog.style.getBorderBox = function(element) {
+ if (goog.userAgent.IE && !goog.userAgent.isDocumentModeOrHigher(9)) {
+ var left = goog.style.getIePixelBorder_(element, 'borderLeft');
+ var right = goog.style.getIePixelBorder_(element, 'borderRight');
+ var top = goog.style.getIePixelBorder_(element, 'borderTop');
+ var bottom = goog.style.getIePixelBorder_(element, 'borderBottom');
+ return new goog.math.Box(top, right, bottom, left);
+ } else {
+ // On non-IE browsers, getComputedStyle is always non-null.
+ var left = goog.style.getComputedStyle(element, 'borderLeftWidth');
+ var right = goog.style.getComputedStyle(element, 'borderRightWidth');
+ var top = goog.style.getComputedStyle(element, 'borderTopWidth');
+ var bottom = goog.style.getComputedStyle(element, 'borderBottomWidth');
+
+ return new goog.math.Box(
+ parseFloat(top), parseFloat(right), parseFloat(bottom),
+ parseFloat(left));
+ }
+};
+
+
+/**
+ * Returns the font face applied to a given node. Opera and IE should return
+ * the font actually displayed. Firefox returns the author's most-preferred
+ * font (whether the browser is capable of displaying it or not.)
+ * @param {Element} el The element whose font family is returned.
+ * @return {string} The font family applied to el.
+ */
+goog.style.getFontFamily = function(el) {
+ var doc = goog.dom.getOwnerDocument(el);
+ var font = '';
+ // The moveToElementText method from the TextRange only works if the element
+ // is attached to the owner document.
+ if (doc.body.createTextRange && goog.dom.contains(doc, el)) {
+ var range = doc.body.createTextRange();
+ range.moveToElementText(el);
+
+ try {
+ font = range.queryCommandValue('FontName');
+ } catch (e) {
+ // This is a workaround for a awkward exception.
+ // On some IE, there is an exception coming from it.
+ // The error description from this exception is:
+ // This window has already been registered as a drop target
+ // This is bogus description, likely due to a bug in ie.
+ font = '';
+ }
+ }
+ if (!font) {
+ // Note if for some reason IE can't derive FontName with a TextRange, we
+ // fallback to using currentStyle
+ font = goog.style.getStyle_(el, 'fontFamily');
+ }
+
+ // Firefox returns the applied font-family string (author's list of
+ // preferred fonts.) We want to return the most-preferred font, in lieu of
+ // the *actually* applied font.
+ var fontsArray = font.split(',');
+ if (fontsArray.length > 1) font = fontsArray[0];
+
+ // Sanitize for x-browser consistency:
+ // Strip quotes because browsers aren't consistent with how they're
+ // applied; Opera always encloses, Firefox sometimes, and IE never.
+ return goog.string.stripQuotes(font, '"\'');
+};
+
+
+/**
+ * Regular expression used for getLengthUnits.
+ * @type {RegExp}
+ * @private
+ */
+goog.style.lengthUnitRegex_ = /[^\d]+$/;
+
+
+/**
+ * Returns the units used for a CSS length measurement.
+ * @param {string} value A CSS length quantity.
+ * @return {?string} The units of measurement.
+ */
+goog.style.getLengthUnits = function(value) {
+ var units = value.match(goog.style.lengthUnitRegex_);
+ return units && units[0] || null;
+};
+
+
+/**
+ * Map of absolute CSS length units
+ * @type {!Object}
+ * @private
+ */
+goog.style.ABSOLUTE_CSS_LENGTH_UNITS_ = {
+ 'cm': 1,
+ 'in': 1,
+ 'mm': 1,
+ 'pc': 1,
+ 'pt': 1
+};
+
+
+/**
+ * Map of relative CSS length units that can be accurately converted to px
+ * font-size values using getIePixelValue_. Only units that are defined in
+ * relation to a font size are convertible (%, small, etc. are not).
+ * @type {!Object}
+ * @private
+ */
+goog.style.CONVERTIBLE_RELATIVE_CSS_UNITS_ = {
+ 'em': 1,
+ 'ex': 1
+};
+
+
+/**
+ * Returns the font size, in pixels, of text in an element.
+ * @param {Element} el The element whose font size is returned.
+ * @return {number} The font size (in pixels).
+ */
+goog.style.getFontSize = function(el) {
+ var fontSize = goog.style.getStyle_(el, 'fontSize');
+ var sizeUnits = goog.style.getLengthUnits(fontSize);
+ if (fontSize && 'px' == sizeUnits) {
+ // NOTE(nathanl): This could be parseFloat instead, but IE doesn't return
+ // decimal fractions in getStyle_ and Firefox reports the fractions, but
+ // ignores them when rendering. Interestingly enough, when we force the
+ // issue and size something to e.g., 50% of 25px, the browsers round in
+ // opposite directions with Firefox reporting 12px and IE 13px. I punt.
+ return parseInt(fontSize, 10);
+ }
+
+ // In IE, we can convert absolute length units to a px value using
+ // goog.style.getIePixelValue_. Units defined in relation to a font size
+ // (em, ex) are applied relative to the element's parentNode and can also
+ // be converted.
+ if (goog.userAgent.IE) {
+ if (String(sizeUnits) in goog.style.ABSOLUTE_CSS_LENGTH_UNITS_) {
+ return goog.style.getIePixelValue_(el, fontSize, 'left', 'pixelLeft');
+ } else if (
+ el.parentNode && el.parentNode.nodeType == goog.dom.NodeType.ELEMENT &&
+ String(sizeUnits) in goog.style.CONVERTIBLE_RELATIVE_CSS_UNITS_) {
+ // Check the parent size - if it is the same it means the relative size
+ // value is inherited and we therefore don't want to count it twice. If
+ // it is different, this element either has explicit style or has a CSS
+ // rule applying to it.
+ var parentElement = /** @type {!Element} */ (el.parentNode);
+ var parentSize = goog.style.getStyle_(parentElement, 'fontSize');
+ return goog.style.getIePixelValue_(
+ parentElement, fontSize == parentSize ? '1em' : fontSize, 'left',
+ 'pixelLeft');
+ }
+ }
+
+ // Sometimes we can't cleanly find the font size (some units relative to a
+ // node's parent's font size are difficult: %, smaller et al), so we create
+ // an invisible, absolutely-positioned span sized to be the height of an 'M'
+ // rendered in its parent's (i.e., our target element's) font size. This is
+ // the definition of CSS's font size attribute.
+ var sizeElement = goog.dom.createDom(goog.dom.TagName.SPAN, {
+ 'style': 'visibility:hidden;position:absolute;' +
+ 'line-height:0;padding:0;margin:0;border:0;height:1em;'
+ });
+ goog.dom.appendChild(el, sizeElement);
+ fontSize = sizeElement.offsetHeight;
+ goog.dom.removeNode(sizeElement);
+
+ return fontSize;
+};
+
+
+/**
+ * Parses a style attribute value. Converts CSS property names to camel case.
+ * @param {string} value The style attribute value.
+ * @return {!Object} Map of CSS properties to string values.
+ */
+goog.style.parseStyleAttribute = function(value) {
+ var result = {};
+ goog.array.forEach(value.split(/\s*;\s*/), function(pair) {
+ var keyValue = pair.match(/\s*([\w-]+)\s*\:(.+)/);
+ if (keyValue) {
+ var styleName = keyValue[1];
+ var styleValue = goog.string.trim(keyValue[2]);
+ result[goog.string.toCamelCase(styleName.toLowerCase())] = styleValue;
+ }
+ });
+ return result;
+};
+
+
+/**
+ * Reverse of parseStyleAttribute; that is, takes a style object and returns the
+ * corresponding attribute value. Converts camel case property names to proper
+ * CSS selector names.
+ * @param {Object} obj Map of CSS properties to values.
+ * @return {string} The style attribute value.
+ */
+goog.style.toStyleAttribute = function(obj) {
+ var buffer = [];
+ goog.object.forEach(obj, function(value, key) {
+ buffer.push(goog.string.toSelectorCase(key), ':', value, ';');
+ });
+ return buffer.join('');
+};
+
+
+/**
+ * Sets CSS float property on an element.
+ * @param {Element} el The element to set float property on.
+ * @param {string} value The value of float CSS property to set on this element.
+ */
+goog.style.setFloat = function(el, value) {
+ el.style[goog.userAgent.IE ? 'styleFloat' : 'cssFloat'] = value;
+};
+
+
+/**
+ * Gets value of explicitly-set float CSS property on an element.
+ * @param {Element} el The element to get float property of.
+ * @return {string} The value of explicitly-set float CSS property on this
+ * element.
+ */
+goog.style.getFloat = function(el) {
+ return el.style[goog.userAgent.IE ? 'styleFloat' : 'cssFloat'] || '';
+};
+
+
+/**
+ * Returns the scroll bar width (represents the width of both horizontal
+ * and vertical scroll).
+ *
+ * @param {string=} opt_className An optional class name (or names) to apply
+ * to the invisible div created to measure the scrollbar. This is necessary
+ * if some scrollbars are styled differently than others.
+ * @return {number} The scroll bar width in px.
+ */
+goog.style.getScrollbarWidth = function(opt_className) {
+ // Add two hidden divs. The child div is larger than the parent and
+ // forces scrollbars to appear on it.
+ // Using overflow:scroll does not work consistently with scrollbars that
+ // are styled with ::-webkit-scrollbar.
+ var outerDiv = goog.dom.createElement(goog.dom.TagName.DIV);
+ if (opt_className) {
+ outerDiv.className = opt_className;
+ }
+ outerDiv.style.cssText = 'overflow:auto;' +
+ 'position:absolute;top:0;width:100px;height:100px';
+ var innerDiv = goog.dom.createElement(goog.dom.TagName.DIV);
+ goog.style.setSize(innerDiv, '200px', '200px');
+ outerDiv.appendChild(innerDiv);
+ goog.dom.appendChild(goog.dom.getDocument().body, outerDiv);
+ var width = outerDiv.offsetWidth - outerDiv.clientWidth;
+ goog.dom.removeNode(outerDiv);
+ return width;
+};
+
+
+/**
+ * Regular expression to extract x and y translation components from a CSS
+ * transform Matrix representation.
+ *
+ * @type {!RegExp}
+ * @const
+ * @private
+ */
+goog.style.MATRIX_TRANSLATION_REGEX_ = new RegExp(
+ 'matrix\\([0-9\\.\\-]+, [0-9\\.\\-]+, ' +
+ '[0-9\\.\\-]+, [0-9\\.\\-]+, ' +
+ '([0-9\\.\\-]+)p?x?, ([0-9\\.\\-]+)p?x?\\)');
+
+
+/**
+ * Returns the x,y translation component of any CSS transforms applied to the
+ * element, in pixels.
+ *
+ * @param {!Element} element The element to get the translation of.
+ * @return {!goog.math.Coordinate} The CSS translation of the element in px.
+ */
+goog.style.getCssTranslation = function(element) {
+ var transform = goog.style.getComputedTransform(element);
+ if (!transform) {
+ return new goog.math.Coordinate(0, 0);
+ }
+ var matches = transform.match(goog.style.MATRIX_TRANSLATION_REGEX_);
+ if (!matches) {
+ return new goog.math.Coordinate(0, 0);
+ }
+ return new goog.math.Coordinate(
+ parseFloat(matches[1]), parseFloat(matches[2]));
+};
diff --git a/chromium/third_party/ink/closure/ui/component.js b/chromium/third_party/ink/closure/ui/component.js
new file mode 100644
index 00000000000..f4f7737a446
--- /dev/null
+++ b/chromium/third_party/ink/closure/ui/component.js
@@ -0,0 +1,1305 @@
+// Copyright 2007 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Abstract class for all UI components. This defines the standard
+ * design pattern that all UI components should follow.
+ *
+ * @author pupius@google.com (Daniel Pupius)
+ * @author ssaviano@google.com (Steven Saviano)
+ * @author baker@google.com (Greg Baker)
+ * @author attila@google.com (Attila Bodis)
+ * @see ../demos/samplecomponent.html
+ * @see http://code.google.com/p/closure-library/wiki/IntroToComponents
+ */
+
+goog.provide('goog.ui.Component');
+goog.provide('goog.ui.Component.Error');
+goog.provide('goog.ui.Component.EventType');
+goog.provide('goog.ui.Component.State');
+
+goog.require('goog.array');
+goog.require('goog.asserts');
+goog.require('goog.dom');
+goog.require('goog.dom.NodeType');
+goog.require('goog.dom.TagName');
+goog.require('goog.events.EventHandler');
+goog.require('goog.events.EventTarget');
+goog.require('goog.object');
+goog.require('goog.style');
+goog.require('goog.ui.IdGenerator');
+
+
+
+/**
+ * Default implementation of UI component.
+ *
+ * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
+ * @constructor
+ * @extends {goog.events.EventTarget}
+ * @suppress {underscore}
+ */
+goog.ui.Component = function(opt_domHelper) {
+ goog.events.EventTarget.call(this);
+ /**
+ * DomHelper used to interact with the document, allowing components to be
+ * created in a different window.
+ * @protected {!goog.dom.DomHelper}
+ * @suppress {underscore|visibility}
+ */
+ this.dom_ = opt_domHelper || goog.dom.getDomHelper();
+
+ /**
+ * Whether the component is rendered right-to-left. Right-to-left is set
+ * lazily when {@link #isRightToLeft} is called the first time, unless it has
+ * been set by calling {@link #setRightToLeft} explicitly.
+ * @private {?boolean}
+ */
+ this.rightToLeft_ = goog.ui.Component.defaultRightToLeft_;
+
+ /**
+ * Unique ID of the component, lazily initialized in {@link
+ * goog.ui.Component#getId} if needed. This property is strictly private and
+ * must not be accessed directly outside of this class!
+ * @private {?string}
+ */
+ this.id_ = null;
+
+ /**
+ * Whether the component is in the document.
+ * @private {boolean}
+ */
+ this.inDocument_ = false;
+
+ // TODO(attila): Stop referring to this private field in subclasses.
+ /**
+ * The DOM element for the component.
+ * @private {Element}
+ */
+ this.element_ = null;
+
+ /**
+ * Event handler.
+ * TODO(pallosp): rename it to handler_ after all component subclasses in
+ * inside Google have been cleaned up.
+ * Code search: http://go/component_code_search
+ * @private {goog.events.EventHandler|undefined}
+ */
+ this.googUiComponentHandler_ = void 0;
+
+ /**
+ * Arbitrary data object associated with the component. Such as meta-data.
+ * @private {*}
+ */
+ this.model_ = null;
+
+ /**
+ * Parent component to which events will be propagated. This property is
+ * strictly private and must not be accessed directly outside of this class!
+ * @private {goog.ui.Component?}
+ */
+ this.parent_ = null;
+
+ /**
+ * Array of child components. Lazily initialized on first use. Must be kept
+ * in sync with {@code childIndex_}. This property is strictly private and
+ * must not be accessed directly outside of this class!
+ * @private {Array<goog.ui.Component>?}
+ */
+ this.children_ = null;
+
+ /**
+ * Map of child component IDs to child components. Used for constant-time
+ * random access to child components by ID. Lazily initialized on first use.
+ * Must be kept in sync with {@code children_}. This property is strictly
+ * private and must not be accessed directly outside of this class!
+ *
+ * We use a plain Object, not a {@link goog.structs.Map}, for simplicity.
+ * This means components can't have children with IDs such as 'constructor' or
+ * 'valueOf', but this shouldn't really be an issue in practice, and if it is,
+ * we can always fix it later without changing the API.
+ *
+ * @private {Object}
+ */
+ this.childIndex_ = null;
+
+ /**
+ * Flag used to keep track of whether a component decorated an already
+ * existing element or whether it created the DOM itself.
+ *
+ * If an element is decorated, dispose will leave the node in the document.
+ * It is up to the app to remove the node.
+ *
+ * If an element was rendered, dispose will remove the node automatically.
+ *
+ * @private {boolean}
+ */
+ this.wasDecorated_ = false;
+};
+goog.inherits(goog.ui.Component, goog.events.EventTarget);
+
+
+/**
+ * @define {boolean} Whether to support calling decorate with an element that is
+ * not yet in the document. If true, we check if the element is in the
+ * document, and avoid calling enterDocument if it isn't. If false, we
+ * maintain legacy behavior (always call enterDocument from decorate).
+ */
+goog.define('goog.ui.Component.ALLOW_DETACHED_DECORATION', false);
+
+
+/**
+ * Generator for unique IDs.
+ * @type {goog.ui.IdGenerator}
+ * @private
+ */
+goog.ui.Component.prototype.idGenerator_ = goog.ui.IdGenerator.getInstance();
+
+
+// TODO(gboyer): See if we can remove this and just check goog.i18n.bidi.IS_RTL.
+/**
+ * @define {number} Defines the default BIDI directionality.
+ * 0: Unknown.
+ * 1: Left-to-right.
+ * -1: Right-to-left.
+ */
+goog.define('goog.ui.Component.DEFAULT_BIDI_DIR', 0);
+
+
+/**
+ * The default right to left value.
+ * @type {?boolean}
+ * @private
+ */
+goog.ui.Component.defaultRightToLeft_ =
+ (goog.ui.Component.DEFAULT_BIDI_DIR == 1) ?
+ false :
+ (goog.ui.Component.DEFAULT_BIDI_DIR == -1) ? true : null;
+
+
+/**
+ * Common events fired by components so that event propagation is useful. Not
+ * all components are expected to dispatch or listen for all event types.
+ * Events dispatched before a state transition should be cancelable to prevent
+ * the corresponding state change.
+ * @enum {string}
+ */
+goog.ui.Component.EventType = {
+ /** Dispatched before the component becomes visible. */
+ BEFORE_SHOW: 'beforeshow',
+
+ /**
+ * Dispatched after the component becomes visible.
+ * NOTE(bloom): For goog.ui.Container, this actually fires before containers
+ * are shown. Use goog.ui.Container.EventType.AFTER_SHOW if you want an event
+ * that fires after a goog.ui.Container is shown.
+ */
+ SHOW: 'show',
+
+ /** Dispatched before the component becomes hidden. */
+ HIDE: 'hide',
+
+ /** Dispatched before the component becomes disabled. */
+ DISABLE: 'disable',
+
+ /** Dispatched before the component becomes enabled. */
+ ENABLE: 'enable',
+
+ /** Dispatched before the component becomes highlighted. */
+ HIGHLIGHT: 'highlight',
+
+ /** Dispatched before the component becomes un-highlighted. */
+ UNHIGHLIGHT: 'unhighlight',
+
+ /** Dispatched before the component becomes activated. */
+ ACTIVATE: 'activate',
+
+ /** Dispatched before the component becomes deactivated. */
+ DEACTIVATE: 'deactivate',
+
+ /** Dispatched before the component becomes selected. */
+ SELECT: 'select',
+
+ /** Dispatched before the component becomes un-selected. */
+ UNSELECT: 'unselect',
+
+ /** Dispatched before a component becomes checked. */
+ CHECK: 'check',
+
+ /** Dispatched before a component becomes un-checked. */
+ UNCHECK: 'uncheck',
+
+ /** Dispatched before a component becomes focused. */
+ FOCUS: 'focus',
+
+ /** Dispatched before a component becomes blurred. */
+ BLUR: 'blur',
+
+ /** Dispatched before a component is opened (expanded). */
+ OPEN: 'open',
+
+ /** Dispatched before a component is closed (collapsed). */
+ CLOSE: 'close',
+
+ /** Dispatched after a component is moused over. */
+ ENTER: 'enter',
+
+ /** Dispatched after a component is moused out of. */
+ LEAVE: 'leave',
+
+ /** Dispatched after the user activates the component. */
+ ACTION: 'action',
+
+ /** Dispatched after the external-facing state of a component is changed. */
+ CHANGE: 'change'
+};
+
+
+/**
+ * Errors thrown by the component.
+ * @enum {string}
+ */
+goog.ui.Component.Error = {
+ /**
+ * Error when a method is not supported.
+ */
+ NOT_SUPPORTED: 'Method not supported',
+
+ /**
+ * Error when the given element can not be decorated.
+ */
+ DECORATE_INVALID: 'Invalid element to decorate',
+
+ /**
+ * Error when the component is already rendered and another render attempt is
+ * made.
+ */
+ ALREADY_RENDERED: 'Component already rendered',
+
+ /**
+ * Error when an attempt is made to set the parent of a component in a way
+ * that would result in an inconsistent object graph.
+ */
+ PARENT_UNABLE_TO_BE_SET: 'Unable to set parent component',
+
+ /**
+ * Error when an attempt is made to add a child component at an out-of-bounds
+ * index. We don't support sparse child arrays.
+ */
+ CHILD_INDEX_OUT_OF_BOUNDS: 'Child component index out of bounds',
+
+ /**
+ * Error when an attempt is made to remove a child component from a component
+ * other than its parent.
+ */
+ NOT_OUR_CHILD: 'Child is not in parent component',
+
+ /**
+ * Error when an operation requiring DOM interaction is made when the
+ * component is not in the document
+ */
+ NOT_IN_DOCUMENT: 'Operation not supported while component is not in document',
+
+ /**
+ * Error when an invalid component state is encountered.
+ */
+ STATE_INVALID: 'Invalid component state'
+};
+
+
+/**
+ * Common component states. Components may have distinct appearance depending
+ * on what state(s) apply to them. Not all components are expected to support
+ * all states.
+ * @enum {number}
+ */
+goog.ui.Component.State = {
+ /**
+ * Union of all supported component states.
+ */
+ ALL: 0xFF,
+
+ /**
+ * Component is disabled.
+ * @see goog.ui.Component.EventType.DISABLE
+ * @see goog.ui.Component.EventType.ENABLE
+ */
+ DISABLED: 0x01,
+
+ /**
+ * Component is highlighted.
+ * @see goog.ui.Component.EventType.HIGHLIGHT
+ * @see goog.ui.Component.EventType.UNHIGHLIGHT
+ */
+ HOVER: 0x02,
+
+ /**
+ * Component is active (or "pressed").
+ * @see goog.ui.Component.EventType.ACTIVATE
+ * @see goog.ui.Component.EventType.DEACTIVATE
+ */
+ ACTIVE: 0x04,
+
+ /**
+ * Component is selected.
+ * @see goog.ui.Component.EventType.SELECT
+ * @see goog.ui.Component.EventType.UNSELECT
+ */
+ SELECTED: 0x08,
+
+ /**
+ * Component is checked.
+ * @see goog.ui.Component.EventType.CHECK
+ * @see goog.ui.Component.EventType.UNCHECK
+ */
+ CHECKED: 0x10,
+
+ /**
+ * Component has focus.
+ * @see goog.ui.Component.EventType.FOCUS
+ * @see goog.ui.Component.EventType.BLUR
+ */
+ FOCUSED: 0x20,
+
+ /**
+ * Component is opened (expanded). Applies to tree nodes, menu buttons,
+ * submenus, zippys (zippies?), etc.
+ * @see goog.ui.Component.EventType.OPEN
+ * @see goog.ui.Component.EventType.CLOSE
+ */
+ OPENED: 0x40
+};
+
+
+/**
+ * Static helper method; returns the type of event components are expected to
+ * dispatch when transitioning to or from the given state.
+ * @param {goog.ui.Component.State} state State to/from which the component
+ * is transitioning.
+ * @param {boolean} isEntering Whether the component is entering or leaving the
+ * state.
+ * @return {goog.ui.Component.EventType} Event type to dispatch.
+ */
+goog.ui.Component.getStateTransitionEvent = function(state, isEntering) {
+ switch (state) {
+ case goog.ui.Component.State.DISABLED:
+ return isEntering ? goog.ui.Component.EventType.DISABLE :
+ goog.ui.Component.EventType.ENABLE;
+ case goog.ui.Component.State.HOVER:
+ return isEntering ? goog.ui.Component.EventType.HIGHLIGHT :
+ goog.ui.Component.EventType.UNHIGHLIGHT;
+ case goog.ui.Component.State.ACTIVE:
+ return isEntering ? goog.ui.Component.EventType.ACTIVATE :
+ goog.ui.Component.EventType.DEACTIVATE;
+ case goog.ui.Component.State.SELECTED:
+ return isEntering ? goog.ui.Component.EventType.SELECT :
+ goog.ui.Component.EventType.UNSELECT;
+ case goog.ui.Component.State.CHECKED:
+ return isEntering ? goog.ui.Component.EventType.CHECK :
+ goog.ui.Component.EventType.UNCHECK;
+ case goog.ui.Component.State.FOCUSED:
+ return isEntering ? goog.ui.Component.EventType.FOCUS :
+ goog.ui.Component.EventType.BLUR;
+ case goog.ui.Component.State.OPENED:
+ return isEntering ? goog.ui.Component.EventType.OPEN :
+ goog.ui.Component.EventType.CLOSE;
+ default:
+ // Fall through.
+ }
+
+ // Invalid state.
+ throw new Error(goog.ui.Component.Error.STATE_INVALID);
+};
+
+
+/**
+ * Set the default right-to-left value. This causes all component's created from
+ * this point forward to have the given value. This is useful for cases where
+ * a given page is always in one directionality, avoiding unnecessary
+ * right to left determinations.
+ * @param {?boolean} rightToLeft Whether the components should be rendered
+ * right-to-left. Null iff components should determine their directionality.
+ */
+goog.ui.Component.setDefaultRightToLeft = function(rightToLeft) {
+ goog.ui.Component.defaultRightToLeft_ = rightToLeft;
+};
+
+
+/**
+ * Gets the unique ID for the instance of this component. If the instance
+ * doesn't already have an ID, generates one on the fly.
+ * @return {string} Unique component ID.
+ */
+goog.ui.Component.prototype.getId = function() {
+ return this.id_ || (this.id_ = this.idGenerator_.getNextUniqueId());
+};
+
+
+/**
+ * Assigns an ID to this component instance. It is the caller's responsibility
+ * to guarantee that the ID is unique. If the component is a child of a parent
+ * component, then the parent component's child index is updated to reflect the
+ * new ID; this may throw an error if the parent already has a child with an ID
+ * that conflicts with the new ID.
+ * @param {string} id Unique component ID.
+ */
+goog.ui.Component.prototype.setId = function(id) {
+ if (this.parent_ && this.parent_.childIndex_) {
+ // Update the parent's child index.
+ goog.object.remove(this.parent_.childIndex_, this.id_);
+ goog.object.add(this.parent_.childIndex_, id, this);
+ }
+
+ // Update the component ID.
+ this.id_ = id;
+};
+
+
+/**
+ * Gets the component's element.
+ * @return {Element} The element for the component.
+ */
+goog.ui.Component.prototype.getElement = function() {
+ return this.element_;
+};
+
+
+/**
+ * Gets the component's element. This differs from getElement in that
+ * it assumes that the element exists (i.e. the component has been
+ * rendered/decorated) and will cause an assertion error otherwise (if
+ * assertion is enabled).
+ * @return {!Element} The element for the component.
+ */
+goog.ui.Component.prototype.getElementStrict = function() {
+ var el = this.element_;
+ goog.asserts.assert(
+ el, 'Can not call getElementStrict before rendering/decorating.');
+ return el;
+};
+
+
+/**
+ * Sets the component's root element to the given element. Considered
+ * protected and final.
+ *
+ * This should generally only be called during createDom. Setting the element
+ * does not actually change which element is rendered, only the element that is
+ * associated with this UI component.
+ *
+ * This should only be used by subclasses and its associated renderers.
+ *
+ * @param {Element} element Root element for the component.
+ */
+goog.ui.Component.prototype.setElementInternal = function(element) {
+ this.element_ = element;
+};
+
+
+/**
+ * Returns an array of all the elements in this component's DOM with the
+ * provided className.
+ * @param {string} className The name of the class to look for.
+ * @return {!IArrayLike<!Element>} The items found with the class name provided.
+ */
+goog.ui.Component.prototype.getElementsByClass = function(className) {
+ return this.element_ ?
+ this.dom_.getElementsByClass(className, this.element_) :
+ [];
+};
+
+
+/**
+ * Returns the first element in this component's DOM with the provided
+ * className.
+ * @param {string} className The name of the class to look for.
+ * @return {Element} The first item with the class name provided.
+ */
+goog.ui.Component.prototype.getElementByClass = function(className) {
+ return this.element_ ? this.dom_.getElementByClass(className, this.element_) :
+ null;
+};
+
+
+/**
+ * Similar to {@code getElementByClass} except that it expects the
+ * element to be present in the dom thus returning a required value. Otherwise,
+ * will assert.
+ * @param {string} className The name of the class to look for.
+ * @return {!Element} The first item with the class name provided.
+ */
+goog.ui.Component.prototype.getRequiredElementByClass = function(className) {
+ var el = this.getElementByClass(className);
+ goog.asserts.assert(
+ el, 'Expected element in component with class: %s', className);
+ return el;
+};
+
+
+/**
+ * Returns the event handler for this component, lazily created the first time
+ * this method is called.
+ * @return {!goog.events.EventHandler<T>} Event handler for this component.
+ * @protected
+ * @this {T}
+ * @template T
+ */
+goog.ui.Component.prototype.getHandler = function() {
+ // TODO(17988911): templated "this" values currently result in "this" being
+ // "unknown" in the body of the function.
+ var self = /** @type {goog.ui.Component} */ (this);
+ if (!self.googUiComponentHandler_) {
+ self.googUiComponentHandler_ = new goog.events.EventHandler(self);
+ }
+ return self.googUiComponentHandler_;
+};
+
+
+/**
+ * Sets the parent of this component to use for event bubbling. Throws an error
+ * if the component already has a parent or if an attempt is made to add a
+ * component to itself as a child. Callers must use {@code removeChild}
+ * or {@code removeChildAt} to remove components from their containers before
+ * calling this method.
+ * @see goog.ui.Component#removeChild
+ * @see goog.ui.Component#removeChildAt
+ * @param {goog.ui.Component} parent The parent component.
+ */
+goog.ui.Component.prototype.setParent = function(parent) {
+ if (this == parent) {
+ // Attempting to add a child to itself is an error.
+ throw new Error(goog.ui.Component.Error.PARENT_UNABLE_TO_BE_SET);
+ }
+
+ if (parent && this.parent_ && this.id_ && this.parent_.getChild(this.id_) &&
+ this.parent_ != parent) {
+ // This component is already the child of some parent, so it should be
+ // removed using removeChild/removeChildAt first.
+ throw new Error(goog.ui.Component.Error.PARENT_UNABLE_TO_BE_SET);
+ }
+
+ this.parent_ = parent;
+ goog.ui.Component.superClass_.setParentEventTarget.call(this, parent);
+};
+
+
+/**
+ * Returns the component's parent, if any.
+ * @return {goog.ui.Component?} The parent component.
+ */
+goog.ui.Component.prototype.getParent = function() {
+ return this.parent_;
+};
+
+
+/**
+ * Overrides {@link goog.events.EventTarget#setParentEventTarget} to throw an
+ * error if the parent component is set, and the argument is not the parent.
+ * @override
+ */
+goog.ui.Component.prototype.setParentEventTarget = function(parent) {
+ if (this.parent_ && this.parent_ != parent) {
+ throw new Error(goog.ui.Component.Error.NOT_SUPPORTED);
+ }
+ goog.ui.Component.superClass_.setParentEventTarget.call(this, parent);
+};
+
+
+/**
+ * Returns the dom helper that is being used on this component.
+ * @return {!goog.dom.DomHelper} The dom helper used on this component.
+ */
+goog.ui.Component.prototype.getDomHelper = function() {
+ return this.dom_;
+};
+
+
+/**
+ * Determines whether the component has been added to the document.
+ * @return {boolean} TRUE if rendered. Otherwise, FALSE.
+ */
+goog.ui.Component.prototype.isInDocument = function() {
+ return this.inDocument_;
+};
+
+
+/**
+ * Creates the initial DOM representation for the component. The default
+ * implementation is to set this.element_ = div.
+ */
+goog.ui.Component.prototype.createDom = function() {
+ this.element_ = this.dom_.createElement(goog.dom.TagName.DIV);
+};
+
+
+/**
+ * Renders the component. If a parent element is supplied, the component's
+ * element will be appended to it. If there is no optional parent element and
+ * the element doesn't have a parentNode then it will be appended to the
+ * document body.
+ *
+ * If this component has a parent component, and the parent component is
+ * not in the document already, then this will not call {@code enterDocument}
+ * on this component.
+ *
+ * Throws an Error if the component is already rendered.
+ *
+ * @param {Element=} opt_parentElement Optional parent element to render the
+ * component into.
+ */
+goog.ui.Component.prototype.render = function(opt_parentElement) {
+ this.render_(opt_parentElement);
+};
+
+
+/**
+ * Renders the component before another element. The other element should be in
+ * the document already.
+ *
+ * Throws an Error if the component is already rendered.
+ *
+ * @param {Node} sibling Node to render the component before.
+ */
+goog.ui.Component.prototype.renderBefore = function(sibling) {
+ this.render_(/** @type {Element} */ (sibling.parentNode), sibling);
+};
+
+
+/**
+ * Renders the component. If a parent element is supplied, the component's
+ * element will be appended to it. If there is no optional parent element and
+ * the element doesn't have a parentNode then it will be appended to the
+ * document body.
+ *
+ * If this component has a parent component, and the parent component is
+ * not in the document already, then this will not call {@code enterDocument}
+ * on this component.
+ *
+ * Throws an Error if the component is already rendered.
+ *
+ * @param {Element=} opt_parentElement Optional parent element to render the
+ * component into.
+ * @param {Node=} opt_beforeNode Node before which the component is to
+ * be rendered. If left out the node is appended to the parent element.
+ * @private
+ */
+goog.ui.Component.prototype.render_ = function(
+ opt_parentElement, opt_beforeNode) {
+ if (this.inDocument_) {
+ throw new Error(goog.ui.Component.Error.ALREADY_RENDERED);
+ }
+
+ if (!this.element_) {
+ this.createDom();
+ }
+
+ if (opt_parentElement) {
+ opt_parentElement.insertBefore(this.element_, opt_beforeNode || null);
+ } else {
+ this.dom_.getDocument().body.appendChild(this.element_);
+ }
+
+ // If this component has a parent component that isn't in the document yet,
+ // we don't call enterDocument() here. Instead, when the parent component
+ // enters the document, the enterDocument() call will propagate to its
+ // children, including this one. If the component doesn't have a parent
+ // or if the parent is already in the document, we call enterDocument().
+ if (!this.parent_ || this.parent_.isInDocument()) {
+ this.enterDocument();
+ }
+};
+
+
+/**
+ * Decorates the element for the UI component. If the element is in the
+ * document, the enterDocument method will be called.
+ *
+ * If goog.ui.Component.ALLOW_DETACHED_DECORATION is false, the caller must
+ * pass an element that is in the document.
+ *
+ * @param {Element} element Element to decorate.
+ */
+goog.ui.Component.prototype.decorate = function(element) {
+ if (this.inDocument_) {
+ throw new Error(goog.ui.Component.Error.ALREADY_RENDERED);
+ } else if (element && this.canDecorate(element)) {
+ this.wasDecorated_ = true;
+
+ // Set the DOM helper of the component to match the decorated element.
+ var doc = goog.dom.getOwnerDocument(element);
+ if (!this.dom_ || this.dom_.getDocument() != doc) {
+ this.dom_ = goog.dom.getDomHelper(element);
+ }
+
+ // Call specific component decorate logic.
+ this.decorateInternal(element);
+
+ // If supporting detached decoration, check that element is in doc.
+ if (!goog.ui.Component.ALLOW_DETACHED_DECORATION ||
+ goog.dom.contains(doc, element)) {
+ this.enterDocument();
+ }
+ } else {
+ throw new Error(goog.ui.Component.Error.DECORATE_INVALID);
+ }
+};
+
+
+/**
+ * Determines if a given element can be decorated by this type of component.
+ * This method should be overridden by inheriting objects.
+ * @param {Element} element Element to decorate.
+ * @return {boolean} True if the element can be decorated, false otherwise.
+ */
+goog.ui.Component.prototype.canDecorate = function(element) {
+ return true;
+};
+
+
+/**
+ * @return {boolean} Whether the component was decorated.
+ */
+goog.ui.Component.prototype.wasDecorated = function() {
+ return this.wasDecorated_;
+};
+
+
+/**
+ * Actually decorates the element. Should be overridden by inheriting objects.
+ * This method can assume there are checks to ensure the component has not
+ * already been rendered have occurred and that enter document will be called
+ * afterwards. This method is considered protected.
+ * @param {Element} element Element to decorate.
+ * @protected
+ */
+goog.ui.Component.prototype.decorateInternal = function(element) {
+ this.element_ = element;
+};
+
+
+/**
+ * Called when the component's element is known to be in the document. Anything
+ * using document.getElementById etc. should be done at this stage.
+ *
+ * If the component contains child components, this call is propagated to its
+ * children.
+ */
+goog.ui.Component.prototype.enterDocument = function() {
+ this.inDocument_ = true;
+
+ // Propagate enterDocument to child components that have a DOM, if any.
+ // If a child was decorated before entering the document (permitted when
+ // goog.ui.Component.ALLOW_DETACHED_DECORATION is true), its enterDocument
+ // will be called here.
+ this.forEachChild(function(child) {
+ if (!child.isInDocument() && child.getElement()) {
+ child.enterDocument();
+ }
+ });
+};
+
+
+/**
+ * Called by dispose to clean up the elements and listeners created by a
+ * component, or by a parent component/application who has removed the
+ * component from the document but wants to reuse it later.
+ *
+ * If the component contains child components, this call is propagated to its
+ * children.
+ *
+ * It should be possible for the component to be rendered again once this method
+ * has been called.
+ */
+goog.ui.Component.prototype.exitDocument = function() {
+ // Propagate exitDocument to child components that have been rendered, if any.
+ this.forEachChild(function(child) {
+ if (child.isInDocument()) {
+ child.exitDocument();
+ }
+ });
+
+ if (this.googUiComponentHandler_) {
+ this.googUiComponentHandler_.removeAll();
+ }
+
+ this.inDocument_ = false;
+};
+
+
+/**
+ * Disposes of the component. Calls {@code exitDocument}, which is expected to
+ * remove event handlers and clean up the component. Propagates the call to
+ * the component's children, if any. Removes the component's DOM from the
+ * document unless it was decorated.
+ * @override
+ * @protected
+ */
+goog.ui.Component.prototype.disposeInternal = function() {
+ if (this.inDocument_) {
+ this.exitDocument();
+ }
+
+ if (this.googUiComponentHandler_) {
+ this.googUiComponentHandler_.dispose();
+ delete this.googUiComponentHandler_;
+ }
+
+ // Disposes of the component's children, if any.
+ this.forEachChild(function(child) { child.dispose(); });
+
+ // Detach the component's element from the DOM, unless it was decorated.
+ if (!this.wasDecorated_ && this.element_) {
+ goog.dom.removeNode(this.element_);
+ }
+
+ this.children_ = null;
+ this.childIndex_ = null;
+ this.element_ = null;
+ this.model_ = null;
+ this.parent_ = null;
+
+ goog.ui.Component.superClass_.disposeInternal.call(this);
+};
+
+
+/**
+ * Helper function for subclasses that gets a unique id for a given fragment,
+ * this can be used by components to generate unique string ids for DOM
+ * elements.
+ * @param {string} idFragment A partial id.
+ * @return {string} Unique element id.
+ */
+goog.ui.Component.prototype.makeId = function(idFragment) {
+ return this.getId() + '.' + idFragment;
+};
+
+
+/**
+ * Makes a collection of ids. This is a convenience method for makeId. The
+ * object's values are the id fragments and the new values are the generated
+ * ids. The key will remain the same.
+ * @param {Object} object The object that will be used to create the ids.
+ * @return {!Object<string, string>} An object of id keys to generated ids.
+ */
+goog.ui.Component.prototype.makeIds = function(object) {
+ var ids = {};
+ for (var key in object) {
+ ids[key] = this.makeId(object[key]);
+ }
+ return ids;
+};
+
+
+/**
+ * Returns the model associated with the UI component.
+ * @return {*} The model.
+ */
+goog.ui.Component.prototype.getModel = function() {
+ return this.model_;
+};
+
+
+/**
+ * Sets the model associated with the UI component.
+ * @param {*} obj The model.
+ */
+goog.ui.Component.prototype.setModel = function(obj) {
+ this.model_ = obj;
+};
+
+
+/**
+ * Helper function for returning the fragment portion of an id generated using
+ * makeId().
+ * @param {string} id Id generated with makeId().
+ * @return {string} Fragment.
+ */
+goog.ui.Component.prototype.getFragmentFromId = function(id) {
+ return id.substring(this.getId().length + 1);
+};
+
+
+/**
+ * Helper function for returning an element in the document with a unique id
+ * generated using makeId().
+ * @param {string} idFragment The partial id.
+ * @return {Element} The element with the unique id, or null if it cannot be
+ * found.
+ */
+goog.ui.Component.prototype.getElementByFragment = function(idFragment) {
+ if (!this.inDocument_) {
+ throw new Error(goog.ui.Component.Error.NOT_IN_DOCUMENT);
+ }
+ return this.dom_.getElement(this.makeId(idFragment));
+};
+
+
+/**
+ * Adds the specified component as the last child of this component. See
+ * {@link goog.ui.Component#addChildAt} for detailed semantics.
+ *
+ * @see goog.ui.Component#addChildAt
+ * @param {goog.ui.Component} child The new child component.
+ * @param {boolean=} opt_render If true, the child component will be rendered
+ * into the parent.
+ */
+goog.ui.Component.prototype.addChild = function(child, opt_render) {
+ // TODO(gboyer): addChildAt(child, this.getChildCount(), false) will
+ // reposition any already-rendered child to the end. Instead, perhaps
+ // addChild(child, false) should never reposition the child; instead, clients
+ // that need the repositioning will use addChildAt explicitly. Right now,
+ // clients can get around this by calling addChild before calling decorate.
+ this.addChildAt(child, this.getChildCount(), opt_render);
+};
+
+
+/**
+ * Adds the specified component as a child of this component at the given
+ * 0-based index.
+ *
+ * Both {@code addChild} and {@code addChildAt} assume the following contract
+ * between parent and child components:
+ * <ul>
+ * <li>the child component's element must be a descendant of the parent
+ * component's element, and
+ * <li>the DOM state of the child component must be consistent with the DOM
+ * state of the parent component (see {@code isInDocument}) in the
+ * steady state -- the exception is to addChildAt(child, i, false) and
+ * then immediately decorate/render the child.
+ * </ul>
+ *
+ * In particular, {@code parent.addChild(child)} will throw an error if the
+ * child component is already in the document, but the parent isn't.
+ *
+ * Clients of this API may call {@code addChild} and {@code addChildAt} with
+ * {@code opt_render} set to true. If {@code opt_render} is true, calling these
+ * methods will automatically render the child component's element into the
+ * parent component's element. If the parent does not yet have an element, then
+ * {@code createDom} will automatically be invoked on the parent before
+ * rendering the child.
+ *
+ * Invoking {@code parent.addChild(child, true)} will throw an error if the
+ * child component is already in the document, regardless of the parent's DOM
+ * state.
+ *
+ * If {@code opt_render} is true and the parent component is not already
+ * in the document, {@code enterDocument} will not be called on this component
+ * at this point.
+ *
+ * Finally, this method also throws an error if the new child already has a
+ * different parent, or the given index is out of bounds.
+ *
+ * @see goog.ui.Component#addChild
+ * @param {goog.ui.Component} child The new child component.
+ * @param {number} index 0-based index at which the new child component is to be
+ * added; must be between 0 and the current child count (inclusive).
+ * @param {boolean=} opt_render If true, the child component will be rendered
+ * into the parent.
+ * @return {void} Nada.
+ */
+goog.ui.Component.prototype.addChildAt = function(child, index, opt_render) {
+ goog.asserts.assert(!!child, 'Provided element must not be null.');
+
+ if (child.inDocument_ && (opt_render || !this.inDocument_)) {
+ // Adding a child that's already in the document is an error, except if the
+ // parent is also in the document and opt_render is false (e.g. decorate()).
+ throw new Error(goog.ui.Component.Error.ALREADY_RENDERED);
+ }
+
+ if (index < 0 || index > this.getChildCount()) {
+ // Allowing sparse child arrays would lead to strange behavior, so we don't.
+ throw new Error(goog.ui.Component.Error.CHILD_INDEX_OUT_OF_BOUNDS);
+ }
+
+ // Create the index and the child array on first use.
+ if (!this.childIndex_ || !this.children_) {
+ this.childIndex_ = {};
+ this.children_ = [];
+ }
+
+ // Moving child within component, remove old reference.
+ if (child.getParent() == this) {
+ goog.object.set(this.childIndex_, child.getId(), child);
+ goog.array.remove(this.children_, child);
+
+ // Add the child to this component. goog.object.add() throws an error if
+ // a child with the same ID already exists.
+ } else {
+ goog.object.add(this.childIndex_, child.getId(), child);
+ }
+
+ // Set the parent of the child to this component. This throws an error if
+ // the child is already contained by another component.
+ child.setParent(this);
+ goog.array.insertAt(this.children_, child, index);
+
+ if (child.inDocument_ && this.inDocument_ && child.getParent() == this) {
+ // Changing the position of an existing child, move the DOM node (if
+ // necessary).
+ var contentElement = this.getContentElement();
+ var insertBeforeElement = contentElement.childNodes[index] || null;
+ if (insertBeforeElement != child.getElement()) {
+ contentElement.insertBefore(child.getElement(), insertBeforeElement);
+ }
+ } else if (opt_render) {
+ // If this (parent) component doesn't have a DOM yet, call createDom now
+ // to make sure we render the child component's element into the correct
+ // parent element (otherwise render_ with a null first argument would
+ // render the child into the document body, which is almost certainly not
+ // what we want).
+ if (!this.element_) {
+ this.createDom();
+ }
+ // Render the child into the parent at the appropriate location. Note that
+ // getChildAt(index + 1) returns undefined if inserting at the end.
+ // TODO(attila): We should have a renderer with a renderChildAt API.
+ var sibling = this.getChildAt(index + 1);
+ // render_() calls enterDocument() if the parent is already in the document.
+ child.render_(this.getContentElement(), sibling ? sibling.element_ : null);
+ } else if (
+ this.inDocument_ && !child.inDocument_ && child.element_ &&
+ child.element_.parentNode &&
+ // Under some circumstances, IE8 implicitly creates a Document Fragment
+ // for detached nodes, so ensure the parent is an Element as it should be.
+ child.element_.parentNode.nodeType == goog.dom.NodeType.ELEMENT) {
+ // We don't touch the DOM, but if the parent is in the document, and the
+ // child element is in the document but not marked as such, then we call
+ // enterDocument on the child.
+ // TODO(gboyer): It would be nice to move this condition entirely, but
+ // there's a large risk of breaking existing applications that manually
+ // append the child to the DOM and then call addChild.
+ child.enterDocument();
+ }
+};
+
+
+/**
+ * Returns the DOM element into which child components are to be rendered,
+ * or null if the component itself hasn't been rendered yet. This default
+ * implementation returns the component's root element. Subclasses with
+ * complex DOM structures must override this method.
+ * @return {Element} Element to contain child elements (null if none).
+ */
+goog.ui.Component.prototype.getContentElement = function() {
+ return this.element_;
+};
+
+
+/**
+ * Returns true if the component is rendered right-to-left, false otherwise.
+ * The first time this function is invoked, the right-to-left rendering property
+ * is set if it has not been already.
+ * @return {boolean} Whether the control is rendered right-to-left.
+ */
+goog.ui.Component.prototype.isRightToLeft = function() {
+ if (this.rightToLeft_ == null) {
+ this.rightToLeft_ = goog.style.isRightToLeft(
+ this.inDocument_ ? this.element_ : this.dom_.getDocument().body);
+ }
+ return this.rightToLeft_;
+};
+
+
+/**
+ * Set is right-to-left. This function should be used if the component needs
+ * to know the rendering direction during dom creation (i.e. before
+ * {@link #enterDocument} is called and is right-to-left is set).
+ * @param {boolean} rightToLeft Whether the component is rendered
+ * right-to-left.
+ */
+goog.ui.Component.prototype.setRightToLeft = function(rightToLeft) {
+ if (this.inDocument_) {
+ throw new Error(goog.ui.Component.Error.ALREADY_RENDERED);
+ }
+ this.rightToLeft_ = rightToLeft;
+};
+
+
+/**
+ * Returns true if the component has children.
+ * @return {boolean} True if the component has children.
+ */
+goog.ui.Component.prototype.hasChildren = function() {
+ return !!this.children_ && this.children_.length != 0;
+};
+
+
+/**
+ * Returns the number of children of this component.
+ * @return {number} The number of children.
+ */
+goog.ui.Component.prototype.getChildCount = function() {
+ return this.children_ ? this.children_.length : 0;
+};
+
+
+/**
+ * Returns an array containing the IDs of the children of this component, or an
+ * empty array if the component has no children.
+ * @return {!Array<string>} Child component IDs.
+ */
+goog.ui.Component.prototype.getChildIds = function() {
+ var ids = [];
+
+ // We don't use goog.object.getKeys(this.childIndex_) because we want to
+ // return the IDs in the correct order as determined by this.children_.
+ this.forEachChild(function(child) {
+ // addChild()/addChildAt() guarantee that the child array isn't sparse.
+ ids.push(child.getId());
+ });
+
+ return ids;
+};
+
+
+/**
+ * Returns the child with the given ID, or null if no such child exists.
+ * @param {string} id Child component ID.
+ * @return {goog.ui.Component?} The child with the given ID; null if none.
+ */
+goog.ui.Component.prototype.getChild = function(id) {
+ // Use childIndex_ for O(1) access by ID.
+ return (this.childIndex_ && id) ?
+ /** @type {goog.ui.Component} */ (
+ goog.object.get(this.childIndex_, id)) ||
+ null :
+ null;
+};
+
+
+/**
+ * Returns the child at the given index, or null if the index is out of bounds.
+ * @param {number} index 0-based index.
+ * @return {goog.ui.Component?} The child at the given index; null if none.
+ */
+goog.ui.Component.prototype.getChildAt = function(index) {
+ // Use children_ for access by index.
+ return this.children_ ? this.children_[index] || null : null;
+};
+
+
+/**
+ * Calls the given function on each of this component's children in order. If
+ * {@code opt_obj} is provided, it will be used as the 'this' object in the
+ * function when called. The function should take two arguments: the child
+ * component and its 0-based index. The return value is ignored.
+ * @param {function(this:T,?,number):?} f The function to call for every
+ * child component; should take 2 arguments (the child and its index).
+ * @param {T=} opt_obj Used as the 'this' object in f when called.
+ * @template T
+ */
+goog.ui.Component.prototype.forEachChild = function(f, opt_obj) {
+ if (this.children_) {
+ goog.array.forEach(this.children_, f, opt_obj);
+ }
+};
+
+
+/**
+ * Returns the 0-based index of the given child component, or -1 if no such
+ * child is found.
+ * @param {goog.ui.Component?} child The child component.
+ * @return {number} 0-based index of the child component; -1 if not found.
+ */
+goog.ui.Component.prototype.indexOfChild = function(child) {
+ return (this.children_ && child) ? goog.array.indexOf(this.children_, child) :
+ -1;
+};
+
+
+/**
+ * Removes the given child from this component, and returns it. Throws an error
+ * if the argument is invalid or if the specified child isn't found in the
+ * parent component. The argument can either be a string (interpreted as the
+ * ID of the child component to remove) or the child component itself.
+ *
+ * If {@code opt_unrender} is true, calls {@link goog.ui.component#exitDocument}
+ * on the removed child, and subsequently detaches the child's DOM from the
+ * document. Otherwise it is the caller's responsibility to clean up the child
+ * component's DOM.
+ *
+ * @see goog.ui.Component#removeChildAt
+ * @param {string|goog.ui.Component|null} child The ID of the child to remove,
+ * or the child component itself.
+ * @param {boolean=} opt_unrender If true, calls {@code exitDocument} on the
+ * removed child component, and detaches its DOM from the document.
+ * @return {goog.ui.Component} The removed component, if any.
+ */
+goog.ui.Component.prototype.removeChild = function(child, opt_unrender) {
+ if (child) {
+ // Normalize child to be the object and id to be the ID string. This also
+ // ensures that the child is really ours.
+ var id = goog.isString(child) ? child : child.getId();
+ child = this.getChild(id);
+
+ if (id && child) {
+ goog.object.remove(this.childIndex_, id);
+ goog.array.remove(this.children_, child);
+
+ if (opt_unrender) {
+ // Remove the child component's DOM from the document. We have to call
+ // exitDocument first (see documentation).
+ child.exitDocument();
+ if (child.element_) {
+ goog.dom.removeNode(child.element_);
+ }
+ }
+
+ // Child's parent must be set to null after exitDocument is called
+ // so that the child can unlisten to its parent if required.
+ child.setParent(null);
+ }
+ }
+
+ if (!child) {
+ throw new Error(goog.ui.Component.Error.NOT_OUR_CHILD);
+ }
+
+ return /** @type {!goog.ui.Component} */ (child);
+};
+
+
+/**
+ * Removes the child at the given index from this component, and returns it.
+ * Throws an error if the argument is out of bounds, or if the specified child
+ * isn't found in the parent. See {@link goog.ui.Component#removeChild} for
+ * detailed semantics.
+ *
+ * @see goog.ui.Component#removeChild
+ * @param {number} index 0-based index of the child to remove.
+ * @param {boolean=} opt_unrender If true, calls {@code exitDocument} on the
+ * removed child component, and detaches its DOM from the document.
+ * @return {goog.ui.Component} The removed component, if any.
+ */
+goog.ui.Component.prototype.removeChildAt = function(index, opt_unrender) {
+ // removeChild(null) will throw error.
+ return this.removeChild(this.getChildAt(index), opt_unrender);
+};
+
+
+/**
+ * Removes every child component attached to this one and returns them.
+ *
+ * @see goog.ui.Component#removeChild
+ * @param {boolean=} opt_unrender If true, calls {@link #exitDocument} on the
+ * removed child components, and detaches their DOM from the document.
+ * @return {!Array<goog.ui.Component>} The removed components if any.
+ */
+goog.ui.Component.prototype.removeChildren = function(opt_unrender) {
+ var removedChildren = [];
+ while (this.hasChildren()) {
+ removedChildren.push(this.removeChildAt(0, opt_unrender));
+ }
+ return removedChildren;
+};
diff --git a/chromium/third_party/ink/closure/ui/idgenerator.js b/chromium/third_party/ink/closure/ui/idgenerator.js
new file mode 100644
index 00000000000..799dc2bc58c
--- /dev/null
+++ b/chromium/third_party/ink/closure/ui/idgenerator.js
@@ -0,0 +1,48 @@
+// Copyright 2008 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Generator for unique element IDs.
+ *
+ * @author jonp@google.com (Jon Perlow)
+ */
+
+goog.provide('goog.ui.IdGenerator');
+
+
+
+/**
+ * Creates a new id generator.
+ * @constructor
+ * @final
+ */
+goog.ui.IdGenerator = function() {};
+goog.addSingletonGetter(goog.ui.IdGenerator);
+
+
+/**
+ * Next unique ID to use
+ * @type {number}
+ * @private
+ */
+goog.ui.IdGenerator.prototype.nextId_ = 0;
+
+
+/**
+ * Gets the next unique ID.
+ * @return {string} The next unique identifier.
+ */
+goog.ui.IdGenerator.prototype.getNextUniqueId = function() {
+ return ':' + (this.nextId_++).toString(36);
+};
diff --git a/chromium/third_party/ink/closure/uri/uri.js b/chromium/third_party/ink/closure/uri/uri.js
new file mode 100644
index 00000000000..33bb5ac195a
--- /dev/null
+++ b/chromium/third_party/ink/closure/uri/uri.js
@@ -0,0 +1,1550 @@
+// Copyright 2006 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Class for parsing and formatting URIs.
+ *
+ * Use goog.Uri(string) to parse a URI string. Use goog.Uri.create(...) to
+ * create a new instance of the goog.Uri object from Uri parts.
+ *
+ * e.g: <code>var myUri = new goog.Uri(window.location);</code>
+ *
+ * Implements RFC 3986 for parsing/formatting URIs.
+ * http://www.ietf.org/rfc/rfc3986.txt
+ *
+ * Some changes have been made to the interface (more like .NETs), though the
+ * internal representation is now of un-encoded parts, this will change the
+ * behavior slightly.
+ *
+ * @author msamuel@google.com (Mike Samuel)
+ * @author pupius@google.com (Dan Pupius) - Ported to Closure
+ * @author jonp@google.com (Jon Perlow) - Optimized for IE6
+ * @author micapolos@google.com (Michal Pociecha-Los) - Dot segments removal
+ */
+
+goog.provide('goog.Uri');
+goog.provide('goog.Uri.QueryData');
+
+goog.require('goog.array');
+goog.require('goog.asserts');
+goog.require('goog.string');
+goog.require('goog.structs');
+goog.require('goog.structs.Map');
+goog.require('goog.uri.utils');
+goog.require('goog.uri.utils.ComponentIndex');
+goog.require('goog.uri.utils.StandardQueryParam');
+
+
+
+/**
+ * This class contains setters and getters for the parts of the URI.
+ * The <code>getXyz</code>/<code>setXyz</code> methods return the decoded part
+ * -- so<code>goog.Uri.parse('/foo%20bar').getPath()</code> will return the
+ * decoded path, <code>/foo bar</code>.
+ *
+ * Reserved characters (see RFC 3986 section 2.2) can be present in
+ * their percent-encoded form in scheme, domain, and path URI components and
+ * will not be auto-decoded. For example:
+ * <code>goog.Uri.parse('rel%61tive/path%2fto/resource').getPath()</code> will
+ * return <code>relative/path%2fto/resource</code>.
+ *
+ * The constructor accepts an optional unparsed, raw URI string. The parser
+ * is relaxed, so special characters that aren't escaped but don't cause
+ * ambiguities will not cause parse failures.
+ *
+ * All setters return <code>this</code> and so may be chained, a la
+ * <code>goog.Uri.parse('/foo').setFragment('part').toString()</code>.
+ *
+ * @param {*=} opt_uri Optional string URI to parse
+ * (use goog.Uri.create() to create a URI from parts), or if
+ * a goog.Uri is passed, a clone is created.
+ * @param {boolean=} opt_ignoreCase If true, #getParameterValue will ignore
+ * the case of the parameter name.
+ *
+ * @throws URIError If opt_uri is provided and URI is malformed (that is,
+ * if decodeURIComponent fails on any of the URI components).
+ * @constructor
+ * @struct
+ */
+goog.Uri = function(opt_uri, opt_ignoreCase) {
+ /**
+ * Scheme such as "http".
+ * @private {string}
+ */
+ this.scheme_ = '';
+
+ /**
+ * User credentials in the form "username:password".
+ * @private {string}
+ */
+ this.userInfo_ = '';
+
+ /**
+ * Domain part, e.g. "www.google.com".
+ * @private {string}
+ */
+ this.domain_ = '';
+
+ /**
+ * Port, e.g. 8080.
+ * @private {?number}
+ */
+ this.port_ = null;
+
+ /**
+ * Path, e.g. "/tests/img.png".
+ * @private {string}
+ */
+ this.path_ = '';
+
+ /**
+ * The fragment without the #.
+ * @private {string}
+ */
+ this.fragment_ = '';
+
+ /**
+ * Whether or not this Uri should be treated as Read Only.
+ * @private {boolean}
+ */
+ this.isReadOnly_ = false;
+
+ /**
+ * Whether or not to ignore case when comparing query params.
+ * @private {boolean}
+ */
+ this.ignoreCase_ = false;
+
+ /**
+ * Object representing query data.
+ * @private {!goog.Uri.QueryData}
+ */
+ this.queryData_;
+
+ // Parse in the uri string
+ var m;
+ if (opt_uri instanceof goog.Uri) {
+ this.ignoreCase_ =
+ goog.isDef(opt_ignoreCase) ? opt_ignoreCase : opt_uri.getIgnoreCase();
+ this.setScheme(opt_uri.getScheme());
+ this.setUserInfo(opt_uri.getUserInfo());
+ this.setDomain(opt_uri.getDomain());
+ this.setPort(opt_uri.getPort());
+ this.setPath(opt_uri.getPath());
+ this.setQueryData(opt_uri.getQueryData().clone());
+ this.setFragment(opt_uri.getFragment());
+ } else if (opt_uri && (m = goog.uri.utils.split(String(opt_uri)))) {
+ this.ignoreCase_ = !!opt_ignoreCase;
+
+ // Set the parts -- decoding as we do so.
+ // COMPATIBILITY NOTE - In IE, unmatched fields may be empty strings,
+ // whereas in other browsers they will be undefined.
+ this.setScheme(m[goog.uri.utils.ComponentIndex.SCHEME] || '', true);
+ this.setUserInfo(m[goog.uri.utils.ComponentIndex.USER_INFO] || '', true);
+ this.setDomain(m[goog.uri.utils.ComponentIndex.DOMAIN] || '', true);
+ this.setPort(m[goog.uri.utils.ComponentIndex.PORT]);
+ this.setPath(m[goog.uri.utils.ComponentIndex.PATH] || '', true);
+ this.setQueryData(m[goog.uri.utils.ComponentIndex.QUERY_DATA] || '', true);
+ this.setFragment(m[goog.uri.utils.ComponentIndex.FRAGMENT] || '', true);
+
+ } else {
+ this.ignoreCase_ = !!opt_ignoreCase;
+ this.queryData_ = new goog.Uri.QueryData(null, null, this.ignoreCase_);
+ }
+};
+
+
+/**
+ * If true, we preserve the type of query parameters set programmatically.
+ *
+ * This means that if you set a parameter to a boolean, and then call
+ * getParameterValue, you will get a boolean back.
+ *
+ * If false, we will coerce parameters to strings, just as they would
+ * appear in real URIs.
+ *
+ * TODO(nicksantos): Remove this once people have time to fix all tests.
+ *
+ * @type {boolean}
+ */
+goog.Uri.preserveParameterTypesCompatibilityFlag = false;
+
+
+/**
+ * Parameter name added to stop caching.
+ * @type {string}
+ */
+goog.Uri.RANDOM_PARAM = goog.uri.utils.StandardQueryParam.RANDOM;
+
+
+/**
+ * @return {string} The string form of the url.
+ * @override
+ */
+goog.Uri.prototype.toString = function() {
+ var out = [];
+
+ var scheme = this.getScheme();
+ if (scheme) {
+ out.push(
+ goog.Uri.encodeSpecialChars_(
+ scheme, goog.Uri.reDisallowedInSchemeOrUserInfo_, true),
+ ':');
+ }
+
+ var domain = this.getDomain();
+ if (domain || scheme == 'file') {
+ out.push('//');
+
+ var userInfo = this.getUserInfo();
+ if (userInfo) {
+ out.push(
+ goog.Uri.encodeSpecialChars_(
+ userInfo, goog.Uri.reDisallowedInSchemeOrUserInfo_, true),
+ '@');
+ }
+
+ out.push(goog.Uri.removeDoubleEncoding_(goog.string.urlEncode(domain)));
+
+ var port = this.getPort();
+ if (port != null) {
+ out.push(':', String(port));
+ }
+ }
+
+ var path = this.getPath();
+ if (path) {
+ if (this.hasDomain() && path.charAt(0) != '/') {
+ out.push('/');
+ }
+ out.push(
+ goog.Uri.encodeSpecialChars_(
+ path, path.charAt(0) == '/' ? goog.Uri.reDisallowedInAbsolutePath_ :
+ goog.Uri.reDisallowedInRelativePath_,
+ true));
+ }
+
+ var query = this.getEncodedQuery();
+ if (query) {
+ out.push('?', query);
+ }
+
+ var fragment = this.getFragment();
+ if (fragment) {
+ out.push(
+ '#', goog.Uri.encodeSpecialChars_(
+ fragment, goog.Uri.reDisallowedInFragment_));
+ }
+ return out.join('');
+};
+
+
+/**
+ * Resolves the given relative URI (a goog.Uri object), using the URI
+ * represented by this instance as the base URI.
+ *
+ * There are several kinds of relative URIs:<br>
+ * 1. foo - replaces the last part of the path, the whole query and fragment<br>
+ * 2. /foo - replaces the the path, the query and fragment<br>
+ * 3. //foo - replaces everything from the domain on. foo is a domain name<br>
+ * 4. ?foo - replace the query and fragment<br>
+ * 5. #foo - replace the fragment only
+ *
+ * Additionally, if relative URI has a non-empty path, all ".." and "."
+ * segments will be resolved, as described in RFC 3986.
+ *
+ * @param {!goog.Uri} relativeUri The relative URI to resolve.
+ * @return {!goog.Uri} The resolved URI.
+ */
+goog.Uri.prototype.resolve = function(relativeUri) {
+
+ var absoluteUri = this.clone();
+
+ // we satisfy these conditions by looking for the first part of relativeUri
+ // that is not blank and applying defaults to the rest
+
+ var overridden = relativeUri.hasScheme();
+
+ if (overridden) {
+ absoluteUri.setScheme(relativeUri.getScheme());
+ } else {
+ overridden = relativeUri.hasUserInfo();
+ }
+
+ if (overridden) {
+ absoluteUri.setUserInfo(relativeUri.getUserInfo());
+ } else {
+ overridden = relativeUri.hasDomain();
+ }
+
+ if (overridden) {
+ absoluteUri.setDomain(relativeUri.getDomain());
+ } else {
+ overridden = relativeUri.hasPort();
+ }
+
+ var path = relativeUri.getPath();
+ if (overridden) {
+ absoluteUri.setPort(relativeUri.getPort());
+ } else {
+ overridden = relativeUri.hasPath();
+ if (overridden) {
+ // resolve path properly
+ if (path.charAt(0) != '/') {
+ // path is relative
+ if (this.hasDomain() && !this.hasPath()) {
+ // RFC 3986, section 5.2.3, case 1
+ path = '/' + path;
+ } else {
+ // RFC 3986, section 5.2.3, case 2
+ var lastSlashIndex = absoluteUri.getPath().lastIndexOf('/');
+ if (lastSlashIndex != -1) {
+ path = absoluteUri.getPath().substr(0, lastSlashIndex + 1) + path;
+ }
+ }
+ }
+ path = goog.Uri.removeDotSegments(path);
+ }
+ }
+
+ if (overridden) {
+ absoluteUri.setPath(path);
+ } else {
+ overridden = relativeUri.hasQuery();
+ }
+
+ if (overridden) {
+ absoluteUri.setQueryData(relativeUri.getQueryData().clone());
+ } else {
+ overridden = relativeUri.hasFragment();
+ }
+
+ if (overridden) {
+ absoluteUri.setFragment(relativeUri.getFragment());
+ }
+
+ return absoluteUri;
+};
+
+
+/**
+ * Clones the URI instance.
+ * @return {!goog.Uri} New instance of the URI object.
+ */
+goog.Uri.prototype.clone = function() {
+ return new goog.Uri(this);
+};
+
+
+/**
+ * @return {string} The encoded scheme/protocol for the URI.
+ */
+goog.Uri.prototype.getScheme = function() {
+ return this.scheme_;
+};
+
+
+/**
+ * Sets the scheme/protocol.
+ * @throws URIError If opt_decode is true and newScheme is malformed (that is,
+ * if decodeURIComponent fails).
+ * @param {string} newScheme New scheme value.
+ * @param {boolean=} opt_decode Optional param for whether to decode new value.
+ * @return {!goog.Uri} Reference to this URI object.
+ */
+goog.Uri.prototype.setScheme = function(newScheme, opt_decode) {
+ this.enforceReadOnly();
+ this.scheme_ =
+ opt_decode ? goog.Uri.decodeOrEmpty_(newScheme, true) : newScheme;
+
+ // remove an : at the end of the scheme so somebody can pass in
+ // window.location.protocol
+ if (this.scheme_) {
+ this.scheme_ = this.scheme_.replace(/:$/, '');
+ }
+ return this;
+};
+
+
+/**
+ * @return {boolean} Whether the scheme has been set.
+ */
+goog.Uri.prototype.hasScheme = function() {
+ return !!this.scheme_;
+};
+
+
+/**
+ * @return {string} The decoded user info.
+ */
+goog.Uri.prototype.getUserInfo = function() {
+ return this.userInfo_;
+};
+
+
+/**
+ * Sets the userInfo.
+ * @throws URIError If opt_decode is true and newUserInfo is malformed (that is,
+ * if decodeURIComponent fails).
+ * @param {string} newUserInfo New userInfo value.
+ * @param {boolean=} opt_decode Optional param for whether to decode new value.
+ * @return {!goog.Uri} Reference to this URI object.
+ */
+goog.Uri.prototype.setUserInfo = function(newUserInfo, opt_decode) {
+ this.enforceReadOnly();
+ this.userInfo_ =
+ opt_decode ? goog.Uri.decodeOrEmpty_(newUserInfo) : newUserInfo;
+ return this;
+};
+
+
+/**
+ * @return {boolean} Whether the user info has been set.
+ */
+goog.Uri.prototype.hasUserInfo = function() {
+ return !!this.userInfo_;
+};
+
+
+/**
+ * @return {string} The decoded domain.
+ */
+goog.Uri.prototype.getDomain = function() {
+ return this.domain_;
+};
+
+
+/**
+ * Sets the domain.
+ * @throws URIError If opt_decode is true and newDomain is malformed (that is,
+ * if decodeURIComponent fails).
+ * @param {string} newDomain New domain value.
+ * @param {boolean=} opt_decode Optional param for whether to decode new value.
+ * @return {!goog.Uri} Reference to this URI object.
+ */
+goog.Uri.prototype.setDomain = function(newDomain, opt_decode) {
+ this.enforceReadOnly();
+ this.domain_ =
+ opt_decode ? goog.Uri.decodeOrEmpty_(newDomain, true) : newDomain;
+ return this;
+};
+
+
+/**
+ * @return {boolean} Whether the domain has been set.
+ */
+goog.Uri.prototype.hasDomain = function() {
+ return !!this.domain_;
+};
+
+
+/**
+ * @return {?number} The port number.
+ */
+goog.Uri.prototype.getPort = function() {
+ return this.port_;
+};
+
+
+/**
+ * Sets the port number.
+ * @param {*} newPort Port number. Will be explicitly casted to a number.
+ * @return {!goog.Uri} Reference to this URI object.
+ */
+goog.Uri.prototype.setPort = function(newPort) {
+ this.enforceReadOnly();
+
+ if (newPort) {
+ newPort = Number(newPort);
+ if (isNaN(newPort) || newPort < 0) {
+ throw new Error('Bad port number ' + newPort);
+ }
+ this.port_ = newPort;
+ } else {
+ this.port_ = null;
+ }
+
+ return this;
+};
+
+
+/**
+ * @return {boolean} Whether the port has been set.
+ */
+goog.Uri.prototype.hasPort = function() {
+ return this.port_ != null;
+};
+
+
+/**
+ * @return {string} The decoded path.
+ */
+goog.Uri.prototype.getPath = function() {
+ return this.path_;
+};
+
+
+/**
+ * Sets the path.
+ * @throws URIError If opt_decode is true and newPath is malformed (that is,
+ * if decodeURIComponent fails).
+ * @param {string} newPath New path value.
+ * @param {boolean=} opt_decode Optional param for whether to decode new value.
+ * @return {!goog.Uri} Reference to this URI object.
+ */
+goog.Uri.prototype.setPath = function(newPath, opt_decode) {
+ this.enforceReadOnly();
+ this.path_ = opt_decode ? goog.Uri.decodeOrEmpty_(newPath, true) : newPath;
+ return this;
+};
+
+
+/**
+ * @return {boolean} Whether the path has been set.
+ */
+goog.Uri.prototype.hasPath = function() {
+ return !!this.path_;
+};
+
+
+/**
+ * @return {boolean} Whether the query string has been set.
+ */
+goog.Uri.prototype.hasQuery = function() {
+ return this.queryData_.toString() !== '';
+};
+
+
+/**
+ * Sets the query data.
+ * @param {goog.Uri.QueryData|string|undefined} queryData QueryData object.
+ * @param {boolean=} opt_decode Optional param for whether to decode new value.
+ * Applies only if queryData is a string.
+ * @return {!goog.Uri} Reference to this URI object.
+ */
+goog.Uri.prototype.setQueryData = function(queryData, opt_decode) {
+ this.enforceReadOnly();
+
+ if (queryData instanceof goog.Uri.QueryData) {
+ this.queryData_ = queryData;
+ this.queryData_.setIgnoreCase(this.ignoreCase_);
+ } else {
+ if (!opt_decode) {
+ // QueryData accepts encoded query string, so encode it if
+ // opt_decode flag is not true.
+ queryData = goog.Uri.encodeSpecialChars_(
+ queryData, goog.Uri.reDisallowedInQuery_);
+ }
+ this.queryData_ = new goog.Uri.QueryData(queryData, null, this.ignoreCase_);
+ }
+
+ return this;
+};
+
+
+/**
+ * Sets the URI query.
+ * @param {string} newQuery New query value.
+ * @param {boolean=} opt_decode Optional param for whether to decode new value.
+ * @return {!goog.Uri} Reference to this URI object.
+ */
+goog.Uri.prototype.setQuery = function(newQuery, opt_decode) {
+ return this.setQueryData(newQuery, opt_decode);
+};
+
+
+/**
+ * @return {string} The encoded URI query, not including the ?.
+ */
+goog.Uri.prototype.getEncodedQuery = function() {
+ return this.queryData_.toString();
+};
+
+
+/**
+ * @return {string} The decoded URI query, not including the ?.
+ */
+goog.Uri.prototype.getDecodedQuery = function() {
+ return this.queryData_.toDecodedString();
+};
+
+
+/**
+ * Returns the query data.
+ * @return {!goog.Uri.QueryData} QueryData object.
+ */
+goog.Uri.prototype.getQueryData = function() {
+ return this.queryData_;
+};
+
+
+/**
+ * @return {string} The encoded URI query, not including the ?.
+ *
+ * Warning: This method, unlike other getter methods, returns encoded
+ * value, instead of decoded one.
+ */
+goog.Uri.prototype.getQuery = function() {
+ return this.getEncodedQuery();
+};
+
+
+/**
+ * Sets the value of the named query parameters, clearing previous values for
+ * that key.
+ *
+ * @param {string} key The parameter to set.
+ * @param {*} value The new value.
+ * @return {!goog.Uri} Reference to this URI object.
+ */
+goog.Uri.prototype.setParameterValue = function(key, value) {
+ this.enforceReadOnly();
+ this.queryData_.set(key, value);
+ return this;
+};
+
+
+/**
+ * Sets the values of the named query parameters, clearing previous values for
+ * that key. Not new values will currently be moved to the end of the query
+ * string.
+ *
+ * So, <code>goog.Uri.parse('foo?a=b&c=d&e=f').setParameterValues('c', ['new'])
+ * </code> yields <tt>foo?a=b&e=f&c=new</tt>.</p>
+ *
+ * @param {string} key The parameter to set.
+ * @param {*} values The new values. If values is a single
+ * string then it will be treated as the sole value.
+ * @return {!goog.Uri} Reference to this URI object.
+ */
+goog.Uri.prototype.setParameterValues = function(key, values) {
+ this.enforceReadOnly();
+
+ if (!goog.isArray(values)) {
+ values = [String(values)];
+ }
+
+ this.queryData_.setValues(key, values);
+
+ return this;
+};
+
+
+/**
+ * Returns the value<b>s</b> for a given cgi parameter as a list of decoded
+ * query parameter values.
+ * @param {string} name The parameter to get values for.
+ * @return {!Array<?>} The values for a given cgi parameter as a list of
+ * decoded query parameter values.
+ */
+goog.Uri.prototype.getParameterValues = function(name) {
+ return this.queryData_.getValues(name);
+};
+
+
+/**
+ * Returns the first value for a given cgi parameter or undefined if the given
+ * parameter name does not appear in the query string.
+ * @param {string} paramName Unescaped parameter name.
+ * @return {string|undefined} The first value for a given cgi parameter or
+ * undefined if the given parameter name does not appear in the query
+ * string.
+ */
+goog.Uri.prototype.getParameterValue = function(paramName) {
+ // NOTE(nicksantos): This type-cast is a lie when
+ // preserveParameterTypesCompatibilityFlag is set to true.
+ // But this should only be set to true in tests.
+ return /** @type {string|undefined} */ (this.queryData_.get(paramName));
+};
+
+
+/**
+ * @return {string} The URI fragment, not including the #.
+ */
+goog.Uri.prototype.getFragment = function() {
+ return this.fragment_;
+};
+
+
+/**
+ * Sets the URI fragment.
+ * @throws URIError If opt_decode is true and newFragment is malformed (that is,
+ * if decodeURIComponent fails).
+ * @param {string} newFragment New fragment value.
+ * @param {boolean=} opt_decode Optional param for whether to decode new value.
+ * @return {!goog.Uri} Reference to this URI object.
+ */
+goog.Uri.prototype.setFragment = function(newFragment, opt_decode) {
+ this.enforceReadOnly();
+ this.fragment_ =
+ opt_decode ? goog.Uri.decodeOrEmpty_(newFragment) : newFragment;
+ return this;
+};
+
+
+/**
+ * @return {boolean} Whether the URI has a fragment set.
+ */
+goog.Uri.prototype.hasFragment = function() {
+ return !!this.fragment_;
+};
+
+
+/**
+ * Returns true if this has the same domain as that of uri2.
+ * @param {!goog.Uri} uri2 The URI object to compare to.
+ * @return {boolean} true if same domain; false otherwise.
+ */
+goog.Uri.prototype.hasSameDomainAs = function(uri2) {
+ return ((!this.hasDomain() && !uri2.hasDomain()) ||
+ this.getDomain() == uri2.getDomain()) &&
+ ((!this.hasPort() && !uri2.hasPort()) ||
+ this.getPort() == uri2.getPort());
+};
+
+
+/**
+ * Adds a random parameter to the Uri.
+ * @return {!goog.Uri} Reference to this Uri object.
+ */
+goog.Uri.prototype.makeUnique = function() {
+ this.enforceReadOnly();
+ this.setParameterValue(goog.Uri.RANDOM_PARAM, goog.string.getRandomString());
+
+ return this;
+};
+
+
+/**
+ * Removes the named query parameter.
+ *
+ * @param {string} key The parameter to remove.
+ * @return {!goog.Uri} Reference to this URI object.
+ */
+goog.Uri.prototype.removeParameter = function(key) {
+ this.enforceReadOnly();
+ this.queryData_.remove(key);
+ return this;
+};
+
+
+/**
+ * Sets whether Uri is read only. If this goog.Uri is read-only,
+ * enforceReadOnly_ will be called at the start of any function that may modify
+ * this Uri.
+ * @param {boolean} isReadOnly whether this goog.Uri should be read only.
+ * @return {!goog.Uri} Reference to this Uri object.
+ */
+goog.Uri.prototype.setReadOnly = function(isReadOnly) {
+ this.isReadOnly_ = isReadOnly;
+ return this;
+};
+
+
+/**
+ * @return {boolean} Whether the URI is read only.
+ */
+goog.Uri.prototype.isReadOnly = function() {
+ return this.isReadOnly_;
+};
+
+
+/**
+ * Checks if this Uri has been marked as read only, and if so, throws an error.
+ * This should be called whenever any modifying function is called.
+ */
+goog.Uri.prototype.enforceReadOnly = function() {
+ if (this.isReadOnly_) {
+ throw new Error('Tried to modify a read-only Uri');
+ }
+};
+
+
+/**
+ * Sets whether to ignore case.
+ * NOTE: If there are already key/value pairs in the QueryData, and
+ * ignoreCase_ is set to false, the keys will all be lower-cased.
+ * @param {boolean} ignoreCase whether this goog.Uri should ignore case.
+ * @return {!goog.Uri} Reference to this Uri object.
+ */
+goog.Uri.prototype.setIgnoreCase = function(ignoreCase) {
+ this.ignoreCase_ = ignoreCase;
+ if (this.queryData_) {
+ this.queryData_.setIgnoreCase(ignoreCase);
+ }
+ return this;
+};
+
+
+/**
+ * @return {boolean} Whether to ignore case.
+ */
+goog.Uri.prototype.getIgnoreCase = function() {
+ return this.ignoreCase_;
+};
+
+
+//==============================================================================
+// Static members
+//==============================================================================
+
+
+/**
+ * Creates a uri from the string form. Basically an alias of new goog.Uri().
+ * If a Uri object is passed to parse then it will return a clone of the object.
+ *
+ * @throws URIError If parsing the URI is malformed. The passed URI components
+ * should all be parseable by decodeURIComponent.
+ * @param {*} uri Raw URI string or instance of Uri
+ * object.
+ * @param {boolean=} opt_ignoreCase Whether to ignore the case of parameter
+ * names in #getParameterValue.
+ * @return {!goog.Uri} The new URI object.
+ */
+goog.Uri.parse = function(uri, opt_ignoreCase) {
+ return uri instanceof goog.Uri ? uri.clone() :
+ new goog.Uri(uri, opt_ignoreCase);
+};
+
+
+/**
+ * Creates a new goog.Uri object from unencoded parts.
+ *
+ * @param {?string=} opt_scheme Scheme/protocol or full URI to parse.
+ * @param {?string=} opt_userInfo username:password.
+ * @param {?string=} opt_domain www.google.com.
+ * @param {?number=} opt_port 9830.
+ * @param {?string=} opt_path /some/path/to/a/file.html.
+ * @param {string|goog.Uri.QueryData=} opt_query a=1&b=2.
+ * @param {?string=} opt_fragment The fragment without the #.
+ * @param {boolean=} opt_ignoreCase Whether to ignore parameter name case in
+ * #getParameterValue.
+ *
+ * @return {!goog.Uri} The new URI object.
+ */
+goog.Uri.create = function(
+ opt_scheme, opt_userInfo, opt_domain, opt_port, opt_path, opt_query,
+ opt_fragment, opt_ignoreCase) {
+
+ var uri = new goog.Uri(null, opt_ignoreCase);
+
+ // Only set the parts if they are defined and not empty strings.
+ opt_scheme && uri.setScheme(opt_scheme);
+ opt_userInfo && uri.setUserInfo(opt_userInfo);
+ opt_domain && uri.setDomain(opt_domain);
+ opt_port && uri.setPort(opt_port);
+ opt_path && uri.setPath(opt_path);
+ opt_query && uri.setQueryData(opt_query);
+ opt_fragment && uri.setFragment(opt_fragment);
+
+ return uri;
+};
+
+
+/**
+ * Resolves a relative Uri against a base Uri, accepting both strings and
+ * Uri objects.
+ *
+ * @param {*} base Base Uri.
+ * @param {*} rel Relative Uri.
+ * @return {!goog.Uri} Resolved uri.
+ */
+goog.Uri.resolve = function(base, rel) {
+ if (!(base instanceof goog.Uri)) {
+ base = goog.Uri.parse(base);
+ }
+
+ if (!(rel instanceof goog.Uri)) {
+ rel = goog.Uri.parse(rel);
+ }
+
+ return base.resolve(rel);
+};
+
+
+/**
+ * Removes dot segments in given path component, as described in
+ * RFC 3986, section 5.2.4.
+ *
+ * @param {string} path A non-empty path component.
+ * @return {string} Path component with removed dot segments.
+ */
+goog.Uri.removeDotSegments = function(path) {
+ if (path == '..' || path == '.') {
+ return '';
+
+ } else if (
+ !goog.string.contains(path, './') && !goog.string.contains(path, '/.')) {
+ // This optimization detects uris which do not contain dot-segments,
+ // and as a consequence do not require any processing.
+ return path;
+
+ } else {
+ var leadingSlash = goog.string.startsWith(path, '/');
+ var segments = path.split('/');
+ var out = [];
+
+ for (var pos = 0; pos < segments.length;) {
+ var segment = segments[pos++];
+
+ if (segment == '.') {
+ if (leadingSlash && pos == segments.length) {
+ out.push('');
+ }
+ } else if (segment == '..') {
+ if (out.length > 1 || out.length == 1 && out[0] != '') {
+ out.pop();
+ }
+ if (leadingSlash && pos == segments.length) {
+ out.push('');
+ }
+ } else {
+ out.push(segment);
+ leadingSlash = true;
+ }
+ }
+
+ return out.join('/');
+ }
+};
+
+
+/**
+ * Decodes a value or returns the empty string if it isn't defined or empty.
+ * @throws URIError If decodeURIComponent fails to decode val.
+ * @param {string|undefined} val Value to decode.
+ * @param {boolean=} opt_preserveReserved If true, restricted characters will
+ * not be decoded.
+ * @return {string} Decoded value.
+ * @private
+ */
+goog.Uri.decodeOrEmpty_ = function(val, opt_preserveReserved) {
+ // Don't use UrlDecode() here because val is not a query parameter.
+ if (!val) {
+ return '';
+ }
+
+ // decodeURI has the same output for '%2f' and '%252f'. We double encode %25
+ // so that we can distinguish between the 2 inputs. This is later undone by
+ // removeDoubleEncoding_.
+ return opt_preserveReserved ? decodeURI(val.replace(/%25/g, '%2525')) :
+ decodeURIComponent(val);
+};
+
+
+/**
+ * If unescapedPart is non null, then escapes any characters in it that aren't
+ * valid characters in a url and also escapes any special characters that
+ * appear in extra.
+ *
+ * @param {*} unescapedPart The string to encode.
+ * @param {RegExp} extra A character set of characters in [\01-\177].
+ * @param {boolean=} opt_removeDoubleEncoding If true, remove double percent
+ * encoding.
+ * @return {?string} null iff unescapedPart == null.
+ * @private
+ */
+goog.Uri.encodeSpecialChars_ = function(
+ unescapedPart, extra, opt_removeDoubleEncoding) {
+ if (goog.isString(unescapedPart)) {
+ var encoded = encodeURI(unescapedPart).replace(extra, goog.Uri.encodeChar_);
+ if (opt_removeDoubleEncoding) {
+ // encodeURI double-escapes %XX sequences used to represent restricted
+ // characters in some URI components, remove the double escaping here.
+ encoded = goog.Uri.removeDoubleEncoding_(encoded);
+ }
+ return encoded;
+ }
+ return null;
+};
+
+
+/**
+ * Converts a character in [\01-\177] to its unicode character equivalent.
+ * @param {string} ch One character string.
+ * @return {string} Encoded string.
+ * @private
+ */
+goog.Uri.encodeChar_ = function(ch) {
+ var n = ch.charCodeAt(0);
+ return '%' + ((n >> 4) & 0xf).toString(16) + (n & 0xf).toString(16);
+};
+
+
+/**
+ * Removes double percent-encoding from a string.
+ * @param {string} doubleEncodedString String
+ * @return {string} String with double encoding removed.
+ * @private
+ */
+goog.Uri.removeDoubleEncoding_ = function(doubleEncodedString) {
+ return doubleEncodedString.replace(/%25([0-9a-fA-F]{2})/g, '%$1');
+};
+
+
+/**
+ * Regular expression for characters that are disallowed in the scheme or
+ * userInfo part of the URI.
+ * @type {RegExp}
+ * @private
+ */
+goog.Uri.reDisallowedInSchemeOrUserInfo_ = /[#\/\?@]/g;
+
+
+/**
+ * Regular expression for characters that are disallowed in a relative path.
+ * Colon is included due to RFC 3986 3.3.
+ * @type {RegExp}
+ * @private
+ */
+goog.Uri.reDisallowedInRelativePath_ = /[\#\?:]/g;
+
+
+/**
+ * Regular expression for characters that are disallowed in an absolute path.
+ * @type {RegExp}
+ * @private
+ */
+goog.Uri.reDisallowedInAbsolutePath_ = /[\#\?]/g;
+
+
+/**
+ * Regular expression for characters that are disallowed in the query.
+ * @type {RegExp}
+ * @private
+ */
+goog.Uri.reDisallowedInQuery_ = /[\#\?@]/g;
+
+
+/**
+ * Regular expression for characters that are disallowed in the fragment.
+ * @type {RegExp}
+ * @private
+ */
+goog.Uri.reDisallowedInFragment_ = /#/g;
+
+
+/**
+ * Checks whether two URIs have the same domain.
+ * @param {string} uri1String First URI string.
+ * @param {string} uri2String Second URI string.
+ * @return {boolean} true if the two URIs have the same domain; false otherwise.
+ */
+goog.Uri.haveSameDomain = function(uri1String, uri2String) {
+ // Differs from goog.uri.utils.haveSameDomain, since this ignores scheme.
+ // TODO(gboyer): Have this just call goog.uri.util.haveSameDomain.
+ var pieces1 = goog.uri.utils.split(uri1String);
+ var pieces2 = goog.uri.utils.split(uri2String);
+ return pieces1[goog.uri.utils.ComponentIndex.DOMAIN] ==
+ pieces2[goog.uri.utils.ComponentIndex.DOMAIN] &&
+ pieces1[goog.uri.utils.ComponentIndex.PORT] ==
+ pieces2[goog.uri.utils.ComponentIndex.PORT];
+};
+
+
+
+/**
+ * Class used to represent URI query parameters. It is essentially a hash of
+ * name-value pairs, though a name can be present more than once.
+ *
+ * Has the same interface as the collections in goog.structs.
+ *
+ * @param {?string=} opt_query Optional encoded query string to parse into
+ * the object.
+ * @param {goog.Uri=} opt_uri Optional uri object that should have its
+ * cache invalidated when this object updates. Deprecated -- this
+ * is no longer required.
+ * @param {boolean=} opt_ignoreCase If true, ignore the case of the parameter
+ * name in #get.
+ * @constructor
+ * @struct
+ * @final
+ */
+goog.Uri.QueryData = function(opt_query, opt_uri, opt_ignoreCase) {
+ /**
+ * The map containing name/value or name/array-of-values pairs.
+ * May be null if it requires parsing from the query string.
+ *
+ * We need to use a Map because we cannot guarantee that the key names will
+ * not be problematic for IE.
+ *
+ * @private {goog.structs.Map<string, !Array<*>>}
+ */
+ this.keyMap_ = null;
+
+ /**
+ * The number of params, or null if it requires computing.
+ * @private {?number}
+ */
+ this.count_ = null;
+
+ /**
+ * Encoded query string, or null if it requires computing from the key map.
+ * @private {?string}
+ */
+ this.encodedQuery_ = opt_query || null;
+
+ /**
+ * If true, ignore the case of the parameter name in #get.
+ * @private {boolean}
+ */
+ this.ignoreCase_ = !!opt_ignoreCase;
+};
+
+
+/**
+ * If the underlying key map is not yet initialized, it parses the
+ * query string and fills the map with parsed data.
+ * @private
+ */
+goog.Uri.QueryData.prototype.ensureKeyMapInitialized_ = function() {
+ if (!this.keyMap_) {
+ this.keyMap_ = new goog.structs.Map();
+ this.count_ = 0;
+ if (this.encodedQuery_) {
+ var self = this;
+ goog.uri.utils.parseQueryData(this.encodedQuery_, function(name, value) {
+ self.add(goog.string.urlDecode(name), value);
+ });
+ }
+ }
+};
+
+
+/**
+ * Creates a new query data instance from a map of names and values.
+ *
+ * @param {!goog.structs.Map<string, ?>|!Object} map Map of string parameter
+ * names to parameter value. If parameter value is an array, it is
+ * treated as if the key maps to each individual value in the
+ * array.
+ * @param {goog.Uri=} opt_uri URI object that should have its cache
+ * invalidated when this object updates.
+ * @param {boolean=} opt_ignoreCase If true, ignore the case of the parameter
+ * name in #get.
+ * @return {!goog.Uri.QueryData} The populated query data instance.
+ */
+goog.Uri.QueryData.createFromMap = function(map, opt_uri, opt_ignoreCase) {
+ var keys = goog.structs.getKeys(map);
+ if (typeof keys == 'undefined') {
+ throw new Error('Keys are undefined');
+ }
+
+ var queryData = new goog.Uri.QueryData(null, null, opt_ignoreCase);
+ var values = goog.structs.getValues(map);
+ for (var i = 0; i < keys.length; i++) {
+ var key = keys[i];
+ var value = values[i];
+ if (!goog.isArray(value)) {
+ queryData.add(key, value);
+ } else {
+ queryData.setValues(key, value);
+ }
+ }
+ return queryData;
+};
+
+
+/**
+ * Creates a new query data instance from parallel arrays of parameter names
+ * and values. Allows for duplicate parameter names. Throws an error if the
+ * lengths of the arrays differ.
+ *
+ * @param {!Array<string>} keys Parameter names.
+ * @param {!Array<?>} values Parameter values.
+ * @param {goog.Uri=} opt_uri URI object that should have its cache
+ * invalidated when this object updates.
+ * @param {boolean=} opt_ignoreCase If true, ignore the case of the parameter
+ * name in #get.
+ * @return {!goog.Uri.QueryData} The populated query data instance.
+ */
+goog.Uri.QueryData.createFromKeysValues = function(
+ keys, values, opt_uri, opt_ignoreCase) {
+ if (keys.length != values.length) {
+ throw new Error('Mismatched lengths for keys/values');
+ }
+ var queryData = new goog.Uri.QueryData(null, null, opt_ignoreCase);
+ for (var i = 0; i < keys.length; i++) {
+ queryData.add(keys[i], values[i]);
+ }
+ return queryData;
+};
+
+
+/**
+ * @return {?number} The number of parameters.
+ */
+goog.Uri.QueryData.prototype.getCount = function() {
+ this.ensureKeyMapInitialized_();
+ return this.count_;
+};
+
+
+/**
+ * Adds a key value pair.
+ * @param {string} key Name.
+ * @param {*} value Value.
+ * @return {!goog.Uri.QueryData} Instance of this object.
+ */
+goog.Uri.QueryData.prototype.add = function(key, value) {
+ this.ensureKeyMapInitialized_();
+ this.invalidateCache_();
+
+ key = this.getKeyName_(key);
+ var values = this.keyMap_.get(key);
+ if (!values) {
+ this.keyMap_.set(key, (values = []));
+ }
+ values.push(value);
+ this.count_ = goog.asserts.assertNumber(this.count_) + 1;
+ return this;
+};
+
+
+/**
+ * Removes all the params with the given key.
+ * @param {string} key Name.
+ * @return {boolean} Whether any parameter was removed.
+ */
+goog.Uri.QueryData.prototype.remove = function(key) {
+ this.ensureKeyMapInitialized_();
+
+ key = this.getKeyName_(key);
+ if (this.keyMap_.containsKey(key)) {
+ this.invalidateCache_();
+
+ // Decrement parameter count.
+ this.count_ =
+ goog.asserts.assertNumber(this.count_) - this.keyMap_.get(key).length;
+ return this.keyMap_.remove(key);
+ }
+ return false;
+};
+
+
+/**
+ * Clears the parameters.
+ */
+goog.Uri.QueryData.prototype.clear = function() {
+ this.invalidateCache_();
+ this.keyMap_ = null;
+ this.count_ = 0;
+};
+
+
+/**
+ * @return {boolean} Whether we have any parameters.
+ */
+goog.Uri.QueryData.prototype.isEmpty = function() {
+ this.ensureKeyMapInitialized_();
+ return this.count_ == 0;
+};
+
+
+/**
+ * Whether there is a parameter with the given name
+ * @param {string} key The parameter name to check for.
+ * @return {boolean} Whether there is a parameter with the given name.
+ */
+goog.Uri.QueryData.prototype.containsKey = function(key) {
+ this.ensureKeyMapInitialized_();
+ key = this.getKeyName_(key);
+ return this.keyMap_.containsKey(key);
+};
+
+
+/**
+ * Whether there is a parameter with the given value.
+ * @param {*} value The value to check for.
+ * @return {boolean} Whether there is a parameter with the given value.
+ */
+goog.Uri.QueryData.prototype.containsValue = function(value) {
+ // NOTE(arv): This solution goes through all the params even if it was the
+ // first param. We can get around this by not reusing code or by switching to
+ // iterators.
+ var vals = this.getValues();
+ return goog.array.contains(vals, value);
+};
+
+
+/**
+ * Runs a callback on every key-value pair in the map, including duplicate keys.
+ * This won't maintain original order when duplicate keys are interspersed (like
+ * getKeys() / getValues()).
+ * @param {function(this:SCOPE, ?, string, !goog.Uri.QueryData)} f
+ * @param {SCOPE=} opt_scope The value of "this" inside f.
+ * @template SCOPE
+ */
+goog.Uri.QueryData.prototype.forEach = function(f, opt_scope) {
+ this.ensureKeyMapInitialized_();
+ this.keyMap_.forEach(function(values, key) {
+ goog.array.forEach(values, function(value) {
+ f.call(opt_scope, value, key, this);
+ }, this);
+ }, this);
+};
+
+
+/**
+ * Returns all the keys of the parameters. If a key is used multiple times
+ * it will be included multiple times in the returned array
+ * @return {!Array<string>} All the keys of the parameters.
+ */
+goog.Uri.QueryData.prototype.getKeys = function() {
+ this.ensureKeyMapInitialized_();
+ // We need to get the values to know how many keys to add.
+ var vals = this.keyMap_.getValues();
+ var keys = this.keyMap_.getKeys();
+ var rv = [];
+ for (var i = 0; i < keys.length; i++) {
+ var val = vals[i];
+ for (var j = 0; j < val.length; j++) {
+ rv.push(keys[i]);
+ }
+ }
+ return rv;
+};
+
+
+/**
+ * Returns all the values of the parameters with the given name. If the query
+ * data has no such key this will return an empty array. If no key is given
+ * all values wil be returned.
+ * @param {string=} opt_key The name of the parameter to get the values for.
+ * @return {!Array<?>} All the values of the parameters with the given name.
+ */
+goog.Uri.QueryData.prototype.getValues = function(opt_key) {
+ this.ensureKeyMapInitialized_();
+ var rv = [];
+ if (goog.isString(opt_key)) {
+ if (this.containsKey(opt_key)) {
+ rv = goog.array.concat(rv, this.keyMap_.get(this.getKeyName_(opt_key)));
+ }
+ } else {
+ // Return all values.
+ var values = this.keyMap_.getValues();
+ for (var i = 0; i < values.length; i++) {
+ rv = goog.array.concat(rv, values[i]);
+ }
+ }
+ return rv;
+};
+
+
+/**
+ * Sets a key value pair and removes all other keys with the same value.
+ *
+ * @param {string} key Name.
+ * @param {*} value Value.
+ * @return {!goog.Uri.QueryData} Instance of this object.
+ */
+goog.Uri.QueryData.prototype.set = function(key, value) {
+ this.ensureKeyMapInitialized_();
+ this.invalidateCache_();
+
+ // TODO(chrishenry): This could be better written as
+ // this.remove(key), this.add(key, value), but that would reorder
+ // the key (since the key is first removed and then added at the
+ // end) and we would have to fix unit tests that depend on key
+ // ordering.
+ key = this.getKeyName_(key);
+ if (this.containsKey(key)) {
+ this.count_ =
+ goog.asserts.assertNumber(this.count_) - this.keyMap_.get(key).length;
+ }
+ this.keyMap_.set(key, [value]);
+ this.count_ = goog.asserts.assertNumber(this.count_) + 1;
+ return this;
+};
+
+
+/**
+ * Returns the first value associated with the key. If the query data has no
+ * such key this will return undefined or the optional default.
+ * @param {string} key The name of the parameter to get the value for.
+ * @param {*=} opt_default The default value to return if the query data
+ * has no such key.
+ * @return {*} The first string value associated with the key, or opt_default
+ * if there's no value.
+ */
+goog.Uri.QueryData.prototype.get = function(key, opt_default) {
+ var values = key ? this.getValues(key) : [];
+ if (goog.Uri.preserveParameterTypesCompatibilityFlag) {
+ return values.length > 0 ? values[0] : opt_default;
+ } else {
+ return values.length > 0 ? String(values[0]) : opt_default;
+ }
+};
+
+
+/**
+ * Sets the values for a key. If the key already exists, this will
+ * override all of the existing values that correspond to the key.
+ * @param {string} key The key to set values for.
+ * @param {!Array<?>} values The values to set.
+ */
+goog.Uri.QueryData.prototype.setValues = function(key, values) {
+ this.remove(key);
+
+ if (values.length > 0) {
+ this.invalidateCache_();
+ this.keyMap_.set(this.getKeyName_(key), goog.array.clone(values));
+ this.count_ = goog.asserts.assertNumber(this.count_) + values.length;
+ }
+};
+
+
+/**
+ * @return {string} Encoded query string.
+ * @override
+ */
+goog.Uri.QueryData.prototype.toString = function() {
+ if (this.encodedQuery_) {
+ return this.encodedQuery_;
+ }
+
+ if (!this.keyMap_) {
+ return '';
+ }
+
+ var sb = [];
+
+ // In the past, we use this.getKeys() and this.getVals(), but that
+ // generates a lot of allocations as compared to simply iterating
+ // over the keys.
+ var keys = this.keyMap_.getKeys();
+ for (var i = 0; i < keys.length; i++) {
+ var key = keys[i];
+ var encodedKey = goog.string.urlEncode(key);
+ var val = this.getValues(key);
+ for (var j = 0; j < val.length; j++) {
+ var param = encodedKey;
+ // Ensure that null and undefined are encoded into the url as
+ // literal strings.
+ if (val[j] !== '') {
+ param += '=' + goog.string.urlEncode(val[j]);
+ }
+ sb.push(param);
+ }
+ }
+
+ return this.encodedQuery_ = sb.join('&');
+};
+
+
+/**
+ * @throws URIError If URI is malformed (that is, if decodeURIComponent fails on
+ * any of the URI components).
+ * @return {string} Decoded query string.
+ */
+goog.Uri.QueryData.prototype.toDecodedString = function() {
+ return goog.Uri.decodeOrEmpty_(this.toString());
+};
+
+
+/**
+ * Invalidate the cache.
+ * @private
+ */
+goog.Uri.QueryData.prototype.invalidateCache_ = function() {
+ this.encodedQuery_ = null;
+};
+
+
+/**
+ * Removes all keys that are not in the provided list. (Modifies this object.)
+ * @param {Array<string>} keys The desired keys.
+ * @return {!goog.Uri.QueryData} a reference to this object.
+ */
+goog.Uri.QueryData.prototype.filterKeys = function(keys) {
+ this.ensureKeyMapInitialized_();
+ this.keyMap_.forEach(function(value, key) {
+ if (!goog.array.contains(keys, key)) {
+ this.remove(key);
+ }
+ }, this);
+ return this;
+};
+
+
+/**
+ * Clone the query data instance.
+ * @return {!goog.Uri.QueryData} New instance of the QueryData object.
+ */
+goog.Uri.QueryData.prototype.clone = function() {
+ var rv = new goog.Uri.QueryData();
+ rv.encodedQuery_ = this.encodedQuery_;
+ if (this.keyMap_) {
+ rv.keyMap_ = this.keyMap_.clone();
+ rv.count_ = this.count_;
+ }
+ return rv;
+};
+
+
+/**
+ * Helper function to get the key name from a JavaScript object. Converts
+ * the object to a string, and to lower case if necessary.
+ * @private
+ * @param {*} arg The object to get a key name from.
+ * @return {string} valid key name which can be looked up in #keyMap_.
+ */
+goog.Uri.QueryData.prototype.getKeyName_ = function(arg) {
+ var keyName = String(arg);
+ if (this.ignoreCase_) {
+ keyName = keyName.toLowerCase();
+ }
+ return keyName;
+};
+
+
+/**
+ * Ignore case in parameter names.
+ * NOTE: If there are already key/value pairs in the QueryData, and
+ * ignoreCase_ is set to false, the keys will all be lower-cased.
+ * @param {boolean} ignoreCase whether this goog.Uri should ignore case.
+ */
+goog.Uri.QueryData.prototype.setIgnoreCase = function(ignoreCase) {
+ var resetKeys = ignoreCase && !this.ignoreCase_;
+ if (resetKeys) {
+ this.ensureKeyMapInitialized_();
+ this.invalidateCache_();
+ this.keyMap_.forEach(function(value, key) {
+ var lowerCase = key.toLowerCase();
+ if (key != lowerCase) {
+ this.remove(key);
+ this.setValues(lowerCase, value);
+ }
+ }, this);
+ }
+ this.ignoreCase_ = ignoreCase;
+};
+
+
+/**
+ * Extends a query data object with another query data or map like object. This
+ * operates 'in-place', it does not create a new QueryData object.
+ *
+ * @param {...(?goog.Uri.QueryData|?goog.structs.Map<?, ?>|?Object)} var_args
+ * The object from which key value pairs will be copied.
+ * @suppress {deprecated} Use deprecated goog.structs.forEach to allow different
+ * types of parameters.
+ */
+goog.Uri.QueryData.prototype.extend = function(var_args) {
+ for (var i = 0; i < arguments.length; i++) {
+ var data = arguments[i];
+ goog.structs.forEach(
+ data, function(value, key) { this.add(key, value); }, this);
+ }
+};
diff --git a/chromium/third_party/ink/closure/uri/utils.js b/chromium/third_party/ink/closure/uri/utils.js
new file mode 100644
index 00000000000..3b8917ae9b2
--- /dev/null
+++ b/chromium/third_party/ink/closure/uri/utils.js
@@ -0,0 +1,1103 @@
+// Copyright 2008 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Simple utilities for dealing with URI strings.
+ *
+ * This is intended to be a lightweight alternative to constructing goog.Uri
+ * objects. Whereas goog.Uri adds several kilobytes to the binary regardless
+ * of how much of its functionality you use, this is designed to be a set of
+ * mostly-independent utilities so that the compiler includes only what is
+ * necessary for the task. Estimated savings of porting is 5k pre-gzip and
+ * 1.5k post-gzip. To ensure the savings remain, future developers should
+ * avoid adding new functionality to existing functions, but instead create
+ * new ones and factor out shared code.
+ *
+ * Many of these utilities have limited functionality, tailored to common
+ * cases. The query parameter utilities assume that the parameter keys are
+ * already encoded, since most keys are compile-time alphanumeric strings. The
+ * query parameter mutation utilities also do not tolerate fragment identifiers.
+ *
+ * By design, these functions can be slower than goog.Uri equivalents.
+ * Repeated calls to some of functions may be quadratic in behavior for IE,
+ * although the effect is somewhat limited given the 2kb limit.
+ *
+ * One advantage of the limited functionality here is that this approach is
+ * less sensitive to differences in URI encodings than goog.Uri, since these
+ * functions operate on strings directly, rather than decoding them and
+ * then re-encoding.
+ *
+ * Uses features of RFC 3986 for parsing/formatting URIs:
+ * http://www.ietf.org/rfc/rfc3986.txt
+ *
+ * @author gboyer@google.com (Garrett Boyer) - The "lightened" design.
+ * @author msamuel@google.com (Mike Samuel) - Domain knowledge and regexes.
+ */
+
+goog.provide('goog.uri.utils');
+goog.provide('goog.uri.utils.ComponentIndex');
+goog.provide('goog.uri.utils.QueryArray');
+goog.provide('goog.uri.utils.QueryValue');
+goog.provide('goog.uri.utils.StandardQueryParam');
+
+goog.require('goog.array');
+goog.require('goog.asserts');
+goog.require('goog.string');
+
+
+/**
+ * Character codes inlined to avoid object allocations due to charCode.
+ * @enum {number}
+ * @private
+ */
+goog.uri.utils.CharCode_ = {
+ AMPERSAND: 38,
+ EQUAL: 61,
+ HASH: 35,
+ QUESTION: 63
+};
+
+
+/**
+ * Builds a URI string from already-encoded parts.
+ *
+ * No encoding is performed. Any component may be omitted as either null or
+ * undefined.
+ *
+ * @param {?string=} opt_scheme The scheme such as 'http'.
+ * @param {?string=} opt_userInfo The user name before the '@'.
+ * @param {?string=} opt_domain The domain such as 'www.google.com', already
+ * URI-encoded.
+ * @param {(string|number|null)=} opt_port The port number.
+ * @param {?string=} opt_path The path, already URI-encoded. If it is not
+ * empty, it must begin with a slash.
+ * @param {?string=} opt_queryData The URI-encoded query data.
+ * @param {?string=} opt_fragment The URI-encoded fragment identifier.
+ * @return {string} The fully combined URI.
+ */
+goog.uri.utils.buildFromEncodedParts = function(
+ opt_scheme, opt_userInfo, opt_domain, opt_port, opt_path, opt_queryData,
+ opt_fragment) {
+ var out = '';
+
+ if (opt_scheme) {
+ out += opt_scheme + ':';
+ }
+
+ if (opt_domain) {
+ out += '//';
+
+ if (opt_userInfo) {
+ out += opt_userInfo + '@';
+ }
+
+ out += opt_domain;
+
+ if (opt_port) {
+ out += ':' + opt_port;
+ }
+ }
+
+ if (opt_path) {
+ out += opt_path;
+ }
+
+ if (opt_queryData) {
+ out += '?' + opt_queryData;
+ }
+
+ if (opt_fragment) {
+ out += '#' + opt_fragment;
+ }
+
+ return out;
+};
+
+
+/**
+ * A regular expression for breaking a URI into its component parts.
+ *
+ * {@link http://www.ietf.org/rfc/rfc3986.txt} says in Appendix B
+ * As the "first-match-wins" algorithm is identical to the "greedy"
+ * disambiguation method used by POSIX regular expressions, it is natural and
+ * commonplace to use a regular expression for parsing the potential five
+ * components of a URI reference.
+ *
+ * The following line is the regular expression for breaking-down a
+ * well-formed URI reference into its components.
+ *
+ * <pre>
+ * ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?
+ * 12 3 4 5 6 7 8 9
+ * </pre>
+ *
+ * The numbers in the second line above are only to assist readability; they
+ * indicate the reference points for each subexpression (i.e., each paired
+ * parenthesis). We refer to the value matched for subexpression <n> as $<n>.
+ * For example, matching the above expression to
+ * <pre>
+ * http://www.ics.uci.edu/pub/ietf/uri/#Related
+ * </pre>
+ * results in the following subexpression matches:
+ * <pre>
+ * $1 = http:
+ * $2 = http
+ * $3 = //www.ics.uci.edu
+ * $4 = www.ics.uci.edu
+ * $5 = /pub/ietf/uri/
+ * $6 = <undefined>
+ * $7 = <undefined>
+ * $8 = #Related
+ * $9 = Related
+ * </pre>
+ * where <undefined> indicates that the component is not present, as is the
+ * case for the query component in the above example. Therefore, we can
+ * determine the value of the five components as
+ * <pre>
+ * scheme = $2
+ * authority = $4
+ * path = $5
+ * query = $7
+ * fragment = $9
+ * </pre>
+ *
+ * The regular expression has been modified slightly to expose the
+ * userInfo, domain, and port separately from the authority.
+ * The modified version yields
+ * <pre>
+ * $1 = http scheme
+ * $2 = <undefined> userInfo -\
+ * $3 = www.ics.uci.edu domain | authority
+ * $4 = <undefined> port -/
+ * $5 = /pub/ietf/uri/ path
+ * $6 = <undefined> query without ?
+ * $7 = Related fragment without #
+ * </pre>
+ * @type {!RegExp}
+ * @private
+ */
+goog.uri.utils.splitRe_ = new RegExp(
+ '^' +
+ '(?:' +
+ '([^:/?#.]+)' + // scheme - ignore special characters
+ // used by other URL parts such as :,
+ // ?, /, #, and .
+ ':)?' +
+ '(?://' +
+ '(?:([^/?#]*)@)?' + // userInfo
+ '([^/#?]*?)' + // domain
+ '(?::([0-9]+))?' + // port
+ '(?=[/#?]|$)' + // authority-terminating character
+ ')?' +
+ '([^?#]+)?' + // path
+ '(?:\\?([^#]*))?' + // query
+ '(?:#([\\s\\S]*))?' + // fragment
+ '$');
+
+
+/**
+ * The index of each URI component in the return value of goog.uri.utils.split.
+ * @enum {number}
+ */
+goog.uri.utils.ComponentIndex = {
+ SCHEME: 1,
+ USER_INFO: 2,
+ DOMAIN: 3,
+ PORT: 4,
+ PATH: 5,
+ QUERY_DATA: 6,
+ FRAGMENT: 7
+};
+
+
+/**
+ * Splits a URI into its component parts.
+ *
+ * Each component can be accessed via the component indices; for example:
+ * <pre>
+ * goog.uri.utils.split(someStr)[goog.uri.utils.ComponentIndex.QUERY_DATA];
+ * </pre>
+ *
+ * @param {string} uri The URI string to examine.
+ * @return {!Array<string|undefined>} Each component still URI-encoded.
+ * Each component that is present will contain the encoded value, whereas
+ * components that are not present will be undefined or empty, depending
+ * on the browser's regular expression implementation. Never null, since
+ * arbitrary strings may still look like path names.
+ */
+goog.uri.utils.split = function(uri) {
+ // See @return comment -- never null.
+ return /** @type {!Array<string|undefined>} */ (
+ uri.match(goog.uri.utils.splitRe_));
+};
+
+
+/**
+ * @param {?string} uri A possibly null string.
+ * @param {boolean=} opt_preserveReserved If true, percent-encoding of RFC-3986
+ * reserved characters will not be removed.
+ * @return {?string} The string URI-decoded, or null if uri is null.
+ * @private
+ */
+goog.uri.utils.decodeIfPossible_ = function(uri, opt_preserveReserved) {
+ if (!uri) {
+ return uri;
+ }
+
+ return opt_preserveReserved ? decodeURI(uri) : decodeURIComponent(uri);
+};
+
+
+/**
+ * Gets a URI component by index.
+ *
+ * It is preferred to use the getPathEncoded() variety of functions ahead,
+ * since they are more readable.
+ *
+ * @param {goog.uri.utils.ComponentIndex} componentIndex The component index.
+ * @param {string} uri The URI to examine.
+ * @return {?string} The still-encoded component, or null if the component
+ * is not present.
+ * @private
+ */
+goog.uri.utils.getComponentByIndex_ = function(componentIndex, uri) {
+ // Convert undefined, null, and empty string into null.
+ return goog.uri.utils.split(uri)[componentIndex] || null;
+};
+
+
+/**
+ * @param {string} uri The URI to examine.
+ * @return {?string} The protocol or scheme, or null if none. Does not
+ * include trailing colons or slashes.
+ */
+goog.uri.utils.getScheme = function(uri) {
+ return goog.uri.utils.getComponentByIndex_(
+ goog.uri.utils.ComponentIndex.SCHEME, uri);
+};
+
+
+/**
+ * Gets the effective scheme for the URL. If the URL is relative then the
+ * scheme is derived from the page's location.
+ * @param {string} uri The URI to examine.
+ * @return {string} The protocol or scheme, always lower case.
+ */
+goog.uri.utils.getEffectiveScheme = function(uri) {
+ var scheme = goog.uri.utils.getScheme(uri);
+ if (!scheme && goog.global.self && goog.global.self.location) {
+ var protocol = goog.global.self.location.protocol;
+ scheme = protocol.substr(0, protocol.length - 1);
+ }
+ // NOTE: When called from a web worker in Firefox 3.5, location maybe null.
+ // All other browsers with web workers support self.location from the worker.
+ return scheme ? scheme.toLowerCase() : '';
+};
+
+
+/**
+ * @param {string} uri The URI to examine.
+ * @return {?string} The user name still encoded, or null if none.
+ */
+goog.uri.utils.getUserInfoEncoded = function(uri) {
+ return goog.uri.utils.getComponentByIndex_(
+ goog.uri.utils.ComponentIndex.USER_INFO, uri);
+};
+
+
+/**
+ * @param {string} uri The URI to examine.
+ * @return {?string} The decoded user info, or null if none.
+ */
+goog.uri.utils.getUserInfo = function(uri) {
+ return goog.uri.utils.decodeIfPossible_(
+ goog.uri.utils.getUserInfoEncoded(uri));
+};
+
+
+/**
+ * @param {string} uri The URI to examine.
+ * @return {?string} The domain name still encoded, or null if none.
+ */
+goog.uri.utils.getDomainEncoded = function(uri) {
+ return goog.uri.utils.getComponentByIndex_(
+ goog.uri.utils.ComponentIndex.DOMAIN, uri);
+};
+
+
+/**
+ * @param {string} uri The URI to examine.
+ * @return {?string} The decoded domain, or null if none.
+ */
+goog.uri.utils.getDomain = function(uri) {
+ return goog.uri.utils.decodeIfPossible_(
+ goog.uri.utils.getDomainEncoded(uri), true /* opt_preserveReserved */);
+};
+
+
+/**
+ * @param {string} uri The URI to examine.
+ * @return {?number} The port number, or null if none.
+ */
+goog.uri.utils.getPort = function(uri) {
+ // Coerce to a number. If the result of getComponentByIndex_ is null or
+ // non-numeric, the number coersion yields NaN. This will then return
+ // null for all non-numeric cases (though also zero, which isn't a relevant
+ // port number).
+ return Number(
+ goog.uri.utils.getComponentByIndex_(
+ goog.uri.utils.ComponentIndex.PORT, uri)) ||
+ null;
+};
+
+
+/**
+ * @param {string} uri The URI to examine.
+ * @return {?string} The path still encoded, or null if none. Includes the
+ * leading slash, if any.
+ */
+goog.uri.utils.getPathEncoded = function(uri) {
+ return goog.uri.utils.getComponentByIndex_(
+ goog.uri.utils.ComponentIndex.PATH, uri);
+};
+
+
+/**
+ * @param {string} uri The URI to examine.
+ * @return {?string} The decoded path, or null if none. Includes the leading
+ * slash, if any.
+ */
+goog.uri.utils.getPath = function(uri) {
+ return goog.uri.utils.decodeIfPossible_(
+ goog.uri.utils.getPathEncoded(uri), true /* opt_preserveReserved */);
+};
+
+
+/**
+ * @param {string} uri The URI to examine.
+ * @return {?string} The query data still encoded, or null if none. Does not
+ * include the question mark itself.
+ */
+goog.uri.utils.getQueryData = function(uri) {
+ return goog.uri.utils.getComponentByIndex_(
+ goog.uri.utils.ComponentIndex.QUERY_DATA, uri);
+};
+
+
+/**
+ * @param {string} uri The URI to examine.
+ * @return {?string} The fragment identifier, or null if none. Does not
+ * include the hash mark itself.
+ */
+goog.uri.utils.getFragmentEncoded = function(uri) {
+ // The hash mark may not appear in any other part of the URL.
+ var hashIndex = uri.indexOf('#');
+ return hashIndex < 0 ? null : uri.substr(hashIndex + 1);
+};
+
+
+/**
+ * @param {string} uri The URI to examine.
+ * @param {?string} fragment The encoded fragment identifier, or null if none.
+ * Does not include the hash mark itself.
+ * @return {string} The URI with the fragment set.
+ */
+goog.uri.utils.setFragmentEncoded = function(uri, fragment) {
+ return goog.uri.utils.removeFragment(uri) + (fragment ? '#' + fragment : '');
+};
+
+
+/**
+ * @param {string} uri The URI to examine.
+ * @return {?string} The decoded fragment identifier, or null if none. Does
+ * not include the hash mark.
+ */
+goog.uri.utils.getFragment = function(uri) {
+ return goog.uri.utils.decodeIfPossible_(
+ goog.uri.utils.getFragmentEncoded(uri));
+};
+
+
+/**
+ * Extracts everything up to the port of the URI.
+ * @param {string} uri The URI string.
+ * @return {string} Everything up to and including the port.
+ */
+goog.uri.utils.getHost = function(uri) {
+ var pieces = goog.uri.utils.split(uri);
+ return goog.uri.utils.buildFromEncodedParts(
+ pieces[goog.uri.utils.ComponentIndex.SCHEME],
+ pieces[goog.uri.utils.ComponentIndex.USER_INFO],
+ pieces[goog.uri.utils.ComponentIndex.DOMAIN],
+ pieces[goog.uri.utils.ComponentIndex.PORT]);
+};
+
+
+/**
+ * Returns the origin for a given URL.
+ * @param {string} uri The URI string.
+ * @return {string} Everything up to and including the port.
+ */
+goog.uri.utils.getOrigin = function(uri) {
+ var pieces = goog.uri.utils.split(uri);
+ return goog.uri.utils.buildFromEncodedParts(
+ pieces[goog.uri.utils.ComponentIndex.SCHEME], null /* opt_userInfo */,
+ pieces[goog.uri.utils.ComponentIndex.DOMAIN],
+ pieces[goog.uri.utils.ComponentIndex.PORT]);
+};
+
+
+/**
+ * Extracts the path of the URL and everything after.
+ * @param {string} uri The URI string.
+ * @return {string} The URI, starting at the path and including the query
+ * parameters and fragment identifier.
+ */
+goog.uri.utils.getPathAndAfter = function(uri) {
+ var pieces = goog.uri.utils.split(uri);
+ return goog.uri.utils.buildFromEncodedParts(
+ null, null, null, null, pieces[goog.uri.utils.ComponentIndex.PATH],
+ pieces[goog.uri.utils.ComponentIndex.QUERY_DATA],
+ pieces[goog.uri.utils.ComponentIndex.FRAGMENT]);
+};
+
+
+/**
+ * Gets the URI with the fragment identifier removed.
+ * @param {string} uri The URI to examine.
+ * @return {string} Everything preceding the hash mark.
+ */
+goog.uri.utils.removeFragment = function(uri) {
+ // The hash mark may not appear in any other part of the URL.
+ var hashIndex = uri.indexOf('#');
+ return hashIndex < 0 ? uri : uri.substr(0, hashIndex);
+};
+
+
+/**
+ * Ensures that two URI's have the exact same domain, scheme, and port.
+ *
+ * Unlike the version in goog.Uri, this checks protocol, and therefore is
+ * suitable for checking against the browser's same-origin policy.
+ *
+ * @param {string} uri1 The first URI.
+ * @param {string} uri2 The second URI.
+ * @return {boolean} Whether they have the same scheme, domain and port.
+ */
+goog.uri.utils.haveSameDomain = function(uri1, uri2) {
+ var pieces1 = goog.uri.utils.split(uri1);
+ var pieces2 = goog.uri.utils.split(uri2);
+ return pieces1[goog.uri.utils.ComponentIndex.DOMAIN] ==
+ pieces2[goog.uri.utils.ComponentIndex.DOMAIN] &&
+ pieces1[goog.uri.utils.ComponentIndex.SCHEME] ==
+ pieces2[goog.uri.utils.ComponentIndex.SCHEME] &&
+ pieces1[goog.uri.utils.ComponentIndex.PORT] ==
+ pieces2[goog.uri.utils.ComponentIndex.PORT];
+};
+
+
+/**
+ * Asserts that there are no fragment or query identifiers, only in uncompiled
+ * mode.
+ * @param {string} uri The URI to examine.
+ * @private
+ */
+goog.uri.utils.assertNoFragmentsOrQueries_ = function(uri) {
+ goog.asserts.assert(
+ uri.indexOf('#') < 0 && uri.indexOf('?') < 0,
+ 'goog.uri.utils: Fragment or query identifiers are not supported: [%s]',
+ uri);
+};
+
+
+/**
+ * Supported query parameter values by the parameter serializing utilities.
+ *
+ * If a value is null or undefined, the key-value pair is skipped, as an easy
+ * way to omit parameters conditionally. Non-array parameters are converted
+ * to a string and URI encoded. Array values are expanded into multiple
+ * &key=value pairs, with each element stringized and URI-encoded.
+ *
+ * @typedef {*}
+ */
+goog.uri.utils.QueryValue;
+
+
+/**
+ * An array representing a set of query parameters with alternating keys
+ * and values.
+ *
+ * Keys are assumed to be URI encoded already and live at even indices. See
+ * goog.uri.utils.QueryValue for details on how parameter values are encoded.
+ *
+ * Example:
+ * <pre>
+ * var data = [
+ * // Simple param: ?name=BobBarker
+ * 'name', 'BobBarker',
+ * // Conditional param -- may be omitted entirely.
+ * 'specialDietaryNeeds', hasDietaryNeeds() ? getDietaryNeeds() : null,
+ * // Multi-valued param: &house=LosAngeles&house=NewYork&house=null
+ * 'house', ['LosAngeles', 'NewYork', null]
+ * ];
+ * </pre>
+ *
+ * @typedef {!Array<string|goog.uri.utils.QueryValue>}
+ */
+goog.uri.utils.QueryArray;
+
+
+/**
+ * Parses encoded query parameters and calls callback function for every
+ * parameter found in the string.
+ *
+ * Missing value of parameter (e.g. “…&key&…”) is treated as if the value was an
+ * empty string. Keys may be empty strings (e.g. “…&=value&…”) which also means
+ * that “…&=&…” and “…&&…” will result in an empty key and value.
+ *
+ * @param {string} encodedQuery Encoded query string excluding question mark at
+ * the beginning.
+ * @param {function(string, string)} callback Function called for every
+ * parameter found in query string. The first argument (name) will not be
+ * urldecoded (so the function is consistent with buildQueryData), but the
+ * second will. If the parameter has no value (i.e. “=” was not present)
+ * the second argument (value) will be an empty string.
+ */
+goog.uri.utils.parseQueryData = function(encodedQuery, callback) {
+ if (!encodedQuery) {
+ return;
+ }
+ var pairs = encodedQuery.split('&');
+ for (var i = 0; i < pairs.length; i++) {
+ var indexOfEquals = pairs[i].indexOf('=');
+ var name = null;
+ var value = null;
+ if (indexOfEquals >= 0) {
+ name = pairs[i].substring(0, indexOfEquals);
+ value = pairs[i].substring(indexOfEquals + 1);
+ } else {
+ name = pairs[i];
+ }
+ callback(name, value ? goog.string.urlDecode(value) : '');
+ }
+};
+
+
+/**
+ * Split the URI into 3 parts where the [1] is the queryData without a leading
+ * '?'. For example, the URI http://foo.com/bar?a=b#abc returns
+ * ['http://foo.com/bar','a=b','#abc'].
+ * @param {string} uri The URI to parse.
+ * @return {!Array<string>} An array representation of uri of length 3 where the
+ * middle value is the queryData without a leading '?'.
+ * @private
+ */
+goog.uri.utils.splitQueryData_ = function(uri) {
+ // Find the query data and and hash.
+ var hashIndex = uri.indexOf('#');
+ if (hashIndex < 0) {
+ hashIndex = uri.length;
+ }
+ var questionIndex = uri.indexOf('?');
+ var queryData;
+ if (questionIndex < 0 || questionIndex > hashIndex) {
+ questionIndex = hashIndex;
+ queryData = '';
+ } else {
+ queryData = uri.substring(questionIndex + 1, hashIndex);
+ }
+ return [uri.substr(0, questionIndex), queryData, uri.substr(hashIndex)];
+};
+
+
+/**
+ * Join an array created by splitQueryData_ back into a URI.
+ * @param {!Array<string>} parts A URI in the form generated by splitQueryData_.
+ * @return {string} The joined URI.
+ * @private
+ */
+goog.uri.utils.joinQueryData_ = function(parts) {
+ return parts[0] + (parts[1] ? '?' + parts[1] : '') + parts[2];
+};
+
+
+/**
+ * @param {string} queryData
+ * @param {string} newData
+ * @return {string}
+ * @private
+ */
+goog.uri.utils.appendQueryData_ = function(queryData, newData) {
+ if (!newData) {
+ return queryData;
+ }
+ return queryData ? queryData + '&' + newData : newData;
+};
+
+
+/**
+ * @param {string} uri
+ * @param {string} queryData
+ * @return {string}
+ * @private
+ */
+goog.uri.utils.appendQueryDataToUri_ = function(uri, queryData) {
+ if (!queryData) {
+ return uri;
+ }
+ var parts = goog.uri.utils.splitQueryData_(uri);
+ parts[1] = goog.uri.utils.appendQueryData_(parts[1], queryData);
+ return goog.uri.utils.joinQueryData_(parts);
+};
+
+
+/**
+ * Appends key=value pairs to an array, supporting multi-valued objects.
+ * @param {*} key The key prefix.
+ * @param {goog.uri.utils.QueryValue} value The value to serialize.
+ * @param {!Array<string>} pairs The array to which the 'key=value' strings
+ * should be appended.
+ * @private
+ */
+goog.uri.utils.appendKeyValuePairs_ = function(key, value, pairs) {
+ goog.asserts.assertString(key);
+ if (goog.isArray(value)) {
+ // Convince the compiler it's an array.
+ goog.asserts.assertArray(value);
+ for (var j = 0; j < value.length; j++) {
+ // Convert to string explicitly, to short circuit the null and array
+ // logic in this function -- this ensures that null and undefined get
+ // written as literal 'null' and 'undefined', and arrays don't get
+ // expanded out but instead encoded in the default way.
+ goog.uri.utils.appendKeyValuePairs_(key, String(value[j]), pairs);
+ }
+ } else if (value != null) {
+ // Skip a top-level null or undefined entirely.
+ pairs.push(
+ key +
+ // Check for empty string. Zero gets encoded into the url as literal
+ // strings. For empty string, skip the equal sign, to be consistent
+ // with UriBuilder.java.
+ (value === '' ? '' : '=' + goog.string.urlEncode(value)));
+ }
+};
+
+
+/**
+ * Builds a query data string from a sequence of alternating keys and values.
+ * Currently generates "&key&" for empty args.
+ *
+ * @param {!IArrayLike<string|goog.uri.utils.QueryValue>} keysAndValues
+ * Alternating keys and values. See the QueryArray typedef.
+ * @param {number=} opt_startIndex A start offset into the arary, defaults to 0.
+ * @return {string} The encoded query string, in the form 'a=1&b=2'.
+ */
+goog.uri.utils.buildQueryData = function(keysAndValues, opt_startIndex) {
+ goog.asserts.assert(
+ Math.max(keysAndValues.length - (opt_startIndex || 0), 0) % 2 == 0,
+ 'goog.uri.utils: Key/value lists must be even in length.');
+
+ var params = [];
+ for (var i = opt_startIndex || 0; i < keysAndValues.length; i += 2) {
+ var key = /** @type {string} */ (keysAndValues[i]);
+ goog.uri.utils.appendKeyValuePairs_(key, keysAndValues[i + 1], params);
+ }
+ return params.join('&');
+};
+
+
+/**
+ * Builds a query data string from a map.
+ * Currently generates "&key&" for empty args.
+ *
+ * @param {!Object<string, goog.uri.utils.QueryValue>} map An object where keys
+ * are URI-encoded parameter keys, and the values are arbitrary types
+ * or arrays. Keys with a null value are dropped.
+ * @return {string} The encoded query string, in the form 'a=1&b=2'.
+ */
+goog.uri.utils.buildQueryDataFromMap = function(map) {
+ var params = [];
+ for (var key in map) {
+ goog.uri.utils.appendKeyValuePairs_(key, map[key], params);
+ }
+ return params.join('&');
+};
+
+
+/**
+ * Appends URI parameters to an existing URI.
+ *
+ * The variable arguments may contain alternating keys and values. Keys are
+ * assumed to be already URI encoded. The values should not be URI-encoded,
+ * and will instead be encoded by this function.
+ * <pre>
+ * appendParams('http://www.foo.com?existing=true',
+ * 'key1', 'value1',
+ * 'key2', 'value?willBeEncoded',
+ * 'key3', ['valueA', 'valueB', 'valueC'],
+ * 'key4', null);
+ * result: 'http://www.foo.com?existing=true&' +
+ * 'key1=value1&' +
+ * 'key2=value%3FwillBeEncoded&' +
+ * 'key3=valueA&key3=valueB&key3=valueC'
+ * </pre>
+ *
+ * A single call to this function will not exhibit quadratic behavior in IE,
+ * whereas multiple repeated calls may, although the effect is limited by
+ * fact that URL's generally can't exceed 2kb.
+ *
+ * @param {string} uri The original URI, which may already have query data.
+ * @param {...(goog.uri.utils.QueryArray|goog.uri.utils.QueryValue)}
+ * var_args
+ * An array or argument list conforming to goog.uri.utils.QueryArray.
+ * @return {string} The URI with all query parameters added.
+ */
+goog.uri.utils.appendParams = function(uri, var_args) {
+ var queryData = arguments.length == 2 ?
+ goog.uri.utils.buildQueryData(arguments[1], 0) :
+ goog.uri.utils.buildQueryData(arguments, 1);
+ return goog.uri.utils.appendQueryDataToUri_(uri, queryData);
+};
+
+
+/**
+ * Appends query parameters from a map.
+ *
+ * @param {string} uri The original URI, which may already have query data.
+ * @param {!Object<goog.uri.utils.QueryValue>} map An object where keys are
+ * URI-encoded parameter keys, and the values are arbitrary types or arrays.
+ * Keys with a null value are dropped.
+ * @return {string} The new parameters.
+ */
+goog.uri.utils.appendParamsFromMap = function(uri, map) {
+ var queryData = goog.uri.utils.buildQueryDataFromMap(map);
+ return goog.uri.utils.appendQueryDataToUri_(uri, queryData);
+};
+
+
+/**
+ * Appends a single URI parameter.
+ *
+ * Repeated calls to this can exhibit quadratic behavior in IE6 due to the
+ * way string append works, though it should be limited given the 2kb limit.
+ *
+ * @param {string} uri The original URI, which may already have query data.
+ * @param {string} key The key, which must already be URI encoded.
+ * @param {*=} opt_value The value, which will be stringized and encoded
+ * (assumed not already to be encoded). If omitted, undefined, or null, the
+ * key will be added as a valueless parameter.
+ * @return {string} The URI with the query parameter added.
+ */
+goog.uri.utils.appendParam = function(uri, key, opt_value) {
+ var value = goog.isDefAndNotNull(opt_value) ?
+ '=' + goog.string.urlEncode(opt_value) :
+ '';
+ return goog.uri.utils.appendQueryDataToUri_(uri, key + value);
+};
+
+
+/**
+ * Finds the next instance of a query parameter with the specified name.
+ *
+ * Does not instantiate any objects.
+ *
+ * @param {string} uri The URI to search. May contain a fragment identifier
+ * if opt_hashIndex is specified.
+ * @param {number} startIndex The index to begin searching for the key at. A
+ * match may be found even if this is one character after the ampersand.
+ * @param {string} keyEncoded The URI-encoded key.
+ * @param {number} hashOrEndIndex Index to stop looking at. If a hash
+ * mark is present, it should be its index, otherwise it should be the
+ * length of the string.
+ * @return {number} The position of the first character in the key's name,
+ * immediately after either a question mark or a dot.
+ * @private
+ */
+goog.uri.utils.findParam_ = function(
+ uri, startIndex, keyEncoded, hashOrEndIndex) {
+ var index = startIndex;
+ var keyLength = keyEncoded.length;
+
+ // Search for the key itself and post-filter for surronuding punctuation,
+ // rather than expensively building a regexp.
+ while ((index = uri.indexOf(keyEncoded, index)) >= 0 &&
+ index < hashOrEndIndex) {
+ var precedingChar = uri.charCodeAt(index - 1);
+ // Ensure that the preceding character is '&' or '?'.
+ if (precedingChar == goog.uri.utils.CharCode_.AMPERSAND ||
+ precedingChar == goog.uri.utils.CharCode_.QUESTION) {
+ // Ensure the following character is '&', '=', '#', or NaN
+ // (end of string).
+ var followingChar = uri.charCodeAt(index + keyLength);
+ if (!followingChar || followingChar == goog.uri.utils.CharCode_.EQUAL ||
+ followingChar == goog.uri.utils.CharCode_.AMPERSAND ||
+ followingChar == goog.uri.utils.CharCode_.HASH) {
+ return index;
+ }
+ }
+ index += keyLength + 1;
+ }
+
+ return -1;
+};
+
+
+/**
+ * Regular expression for finding a hash mark or end of string.
+ * @type {RegExp}
+ * @private
+ */
+goog.uri.utils.hashOrEndRe_ = /#|$/;
+
+
+/**
+ * Determines if the URI contains a specific key.
+ *
+ * Performs no object instantiations.
+ *
+ * @param {string} uri The URI to process. May contain a fragment
+ * identifier.
+ * @param {string} keyEncoded The URI-encoded key. Case-sensitive.
+ * @return {boolean} Whether the key is present.
+ */
+goog.uri.utils.hasParam = function(uri, keyEncoded) {
+ return goog.uri.utils.findParam_(
+ uri, 0, keyEncoded, uri.search(goog.uri.utils.hashOrEndRe_)) >= 0;
+};
+
+
+/**
+ * Gets the first value of a query parameter.
+ * @param {string} uri The URI to process. May contain a fragment.
+ * @param {string} keyEncoded The URI-encoded key. Case-sensitive.
+ * @return {?string} The first value of the parameter (URI-decoded), or null
+ * if the parameter is not found.
+ */
+goog.uri.utils.getParamValue = function(uri, keyEncoded) {
+ var hashOrEndIndex = uri.search(goog.uri.utils.hashOrEndRe_);
+ var foundIndex =
+ goog.uri.utils.findParam_(uri, 0, keyEncoded, hashOrEndIndex);
+
+ if (foundIndex < 0) {
+ return null;
+ } else {
+ var endPosition = uri.indexOf('&', foundIndex);
+ if (endPosition < 0 || endPosition > hashOrEndIndex) {
+ endPosition = hashOrEndIndex;
+ }
+ // Progress forth to the end of the "key=" or "key&" substring.
+ foundIndex += keyEncoded.length + 1;
+ // Use substr, because it (unlike substring) will return empty string
+ // if foundIndex > endPosition.
+ return goog.string.urlDecode(
+ uri.substr(foundIndex, endPosition - foundIndex));
+ }
+};
+
+
+/**
+ * Gets all values of a query parameter.
+ * @param {string} uri The URI to process. May contain a fragment.
+ * @param {string} keyEncoded The URI-encoded key. Case-sensitive.
+ * @return {!Array<string>} All URI-decoded values with the given key.
+ * If the key is not found, this will have length 0, but never be null.
+ */
+goog.uri.utils.getParamValues = function(uri, keyEncoded) {
+ var hashOrEndIndex = uri.search(goog.uri.utils.hashOrEndRe_);
+ var position = 0;
+ var foundIndex;
+ var result = [];
+
+ while ((foundIndex = goog.uri.utils.findParam_(
+ uri, position, keyEncoded, hashOrEndIndex)) >= 0) {
+ // Find where this parameter ends, either the '&' or the end of the
+ // query parameters.
+ position = uri.indexOf('&', foundIndex);
+ if (position < 0 || position > hashOrEndIndex) {
+ position = hashOrEndIndex;
+ }
+
+ // Progress forth to the end of the "key=" or "key&" substring.
+ foundIndex += keyEncoded.length + 1;
+ // Use substr, because it (unlike substring) will return empty string
+ // if foundIndex > position.
+ result.push(
+ goog.string.urlDecode(uri.substr(foundIndex, position - foundIndex)));
+ }
+
+ return result;
+};
+
+
+/**
+ * Regexp to find trailing question marks and ampersands.
+ * @type {RegExp}
+ * @private
+ */
+goog.uri.utils.trailingQueryPunctuationRe_ = /[?&]($|#)/;
+
+
+/**
+ * Removes all instances of a query parameter.
+ * @param {string} uri The URI to process. Must not contain a fragment.
+ * @param {string} keyEncoded The URI-encoded key.
+ * @return {string} The URI with all instances of the parameter removed.
+ */
+goog.uri.utils.removeParam = function(uri, keyEncoded) {
+ var hashOrEndIndex = uri.search(goog.uri.utils.hashOrEndRe_);
+ var position = 0;
+ var foundIndex;
+ var buffer = [];
+
+ // Look for a query parameter.
+ while ((foundIndex = goog.uri.utils.findParam_(
+ uri, position, keyEncoded, hashOrEndIndex)) >= 0) {
+ // Get the portion of the query string up to, but not including, the ?
+ // or & starting the parameter.
+ buffer.push(uri.substring(position, foundIndex));
+ // Progress to immediately after the '&'. If not found, go to the end.
+ // Avoid including the hash mark.
+ position = Math.min(
+ (uri.indexOf('&', foundIndex) + 1) || hashOrEndIndex, hashOrEndIndex);
+ }
+
+ // Append everything that is remaining.
+ buffer.push(uri.substr(position));
+
+ // Join the buffer, and remove trailing punctuation that remains.
+ return buffer.join('').replace(
+ goog.uri.utils.trailingQueryPunctuationRe_, '$1');
+};
+
+
+/**
+ * Replaces all existing definitions of a parameter with a single definition.
+ *
+ * Repeated calls to this can exhibit quadratic behavior due to the need to
+ * find existing instances and reconstruct the string, though it should be
+ * limited given the 2kb limit. Consider using appendParams or setParamsFromMap
+ * to update multiple parameters in bulk.
+ *
+ * @param {string} uri The original URI, which may already have query data.
+ * @param {string} keyEncoded The key, which must already be URI encoded.
+ * @param {*} value The value, which will be stringized and encoded (assumed
+ * not already to be encoded).
+ * @return {string} The URI with the query parameter added.
+ */
+goog.uri.utils.setParam = function(uri, keyEncoded, value) {
+ return goog.uri.utils.appendParam(
+ goog.uri.utils.removeParam(uri, keyEncoded), keyEncoded, value);
+};
+
+
+/**
+ * Effeciently set or remove multiple query parameters in a URI. Order of
+ * unchanged parameters will not be modified, all updated parameters will be
+ * appended to the end of the query. Params with values of null or undefined are
+ * removed.
+ *
+ * @param {string} uri The URI to process.
+ * @param {!Object<string, goog.uri.utils.QueryValue>} params A list of
+ * parameters to update. If null or undefined, the param will be removed.
+ * @return {string} An updated URI where the query data has been updated with
+ * the params.
+ */
+goog.uri.utils.setParamsFromMap = function(uri, params) {
+ var parts = goog.uri.utils.splitQueryData_(uri);
+ var queryData = parts[1];
+ var buffer = [];
+ if (queryData) {
+ goog.array.forEach(queryData.split('&'), function(pair) {
+ var indexOfEquals = pair.indexOf('=');
+ var name = indexOfEquals >= 0 ? pair.substr(0, indexOfEquals) : pair;
+ if (!params.hasOwnProperty(name)) {
+ buffer.push(pair);
+ }
+ });
+ }
+ parts[1] = goog.uri.utils.appendQueryData_(
+ buffer.join('&'), goog.uri.utils.buildQueryDataFromMap(params));
+ return goog.uri.utils.joinQueryData_(parts);
+};
+
+
+/**
+ * Generates a URI path using a given URI and a path with checks to
+ * prevent consecutive "//". The baseUri passed in must not contain
+ * query or fragment identifiers. The path to append may not contain query or
+ * fragment identifiers.
+ *
+ * @param {string} baseUri URI to use as the base.
+ * @param {string} path Path to append.
+ * @return {string} Updated URI.
+ */
+goog.uri.utils.appendPath = function(baseUri, path) {
+ goog.uri.utils.assertNoFragmentsOrQueries_(baseUri);
+
+ // Remove any trailing '/'
+ if (goog.string.endsWith(baseUri, '/')) {
+ baseUri = baseUri.substr(0, baseUri.length - 1);
+ }
+ // Remove any leading '/'
+ if (goog.string.startsWith(path, '/')) {
+ path = path.substr(1);
+ }
+ return goog.string.buildString(baseUri, '/', path);
+};
+
+
+/**
+ * Replaces the path.
+ * @param {string} uri URI to use as the base.
+ * @param {string} path New path.
+ * @return {string} Updated URI.
+ */
+goog.uri.utils.setPath = function(uri, path) {
+ // Add any missing '/'.
+ if (!goog.string.startsWith(path, '/')) {
+ path = '/' + path;
+ }
+ var parts = goog.uri.utils.split(uri);
+ return goog.uri.utils.buildFromEncodedParts(
+ parts[goog.uri.utils.ComponentIndex.SCHEME],
+ parts[goog.uri.utils.ComponentIndex.USER_INFO],
+ parts[goog.uri.utils.ComponentIndex.DOMAIN],
+ parts[goog.uri.utils.ComponentIndex.PORT], path,
+ parts[goog.uri.utils.ComponentIndex.QUERY_DATA],
+ parts[goog.uri.utils.ComponentIndex.FRAGMENT]);
+};
+
+
+/**
+ * Standard supported query parameters.
+ * @enum {string}
+ */
+goog.uri.utils.StandardQueryParam = {
+
+ /** Unused parameter for unique-ifying. */
+ RANDOM: 'zx'
+};
+
+
+/**
+ * Sets the zx parameter of a URI to a random value.
+ * @param {string} uri Any URI.
+ * @return {string} That URI with the "zx" parameter added or replaced to
+ * contain a random string.
+ */
+goog.uri.utils.makeUnique = function(uri) {
+ return goog.uri.utils.setParam(
+ uri, goog.uri.utils.StandardQueryParam.RANDOM,
+ goog.string.getRandomString());
+};
diff --git a/chromium/third_party/ink/closure/useragent/product.js b/chromium/third_party/ink/closure/useragent/product.js
new file mode 100644
index 00000000000..cc85e630de1
--- /dev/null
+++ b/chromium/third_party/ink/closure/useragent/product.js
@@ -0,0 +1,182 @@
+// Copyright 2008 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Detects the specific browser and not just the rendering engine.
+ *
+ * @author andybons@google.com (Andrew Bonventre)
+ */
+
+goog.provide('goog.userAgent.product');
+
+goog.require('goog.labs.userAgent.browser');
+goog.require('goog.labs.userAgent.platform');
+goog.require('goog.userAgent');
+
+
+/**
+ * @define {boolean} Whether the code is running on the Firefox web browser.
+ */
+goog.define('goog.userAgent.product.ASSUME_FIREFOX', false);
+
+
+/**
+ * @define {boolean} Whether we know at compile-time that the product is an
+ * iPhone.
+ */
+goog.define('goog.userAgent.product.ASSUME_IPHONE', false);
+
+
+/**
+ * @define {boolean} Whether we know at compile-time that the product is an
+ * iPad.
+ */
+goog.define('goog.userAgent.product.ASSUME_IPAD', false);
+
+
+/**
+ * @define {boolean} Whether we know at compile-time that the product is an
+ * AOSP browser or WebView inside a pre KitKat Android phone or tablet.
+ */
+goog.define('goog.userAgent.product.ASSUME_ANDROID', false);
+
+
+/**
+ * @define {boolean} Whether the code is running on the Chrome web browser on
+ * any platform or AOSP browser or WebView in a KitKat+ Android phone or tablet.
+ */
+goog.define('goog.userAgent.product.ASSUME_CHROME', false);
+
+
+/**
+ * @define {boolean} Whether the code is running on the Safari web browser.
+ */
+goog.define('goog.userAgent.product.ASSUME_SAFARI', false);
+
+
+/**
+ * Whether we know the product type at compile-time.
+ * @type {boolean}
+ * @private
+ */
+goog.userAgent.product.PRODUCT_KNOWN_ = goog.userAgent.ASSUME_IE ||
+ goog.userAgent.ASSUME_EDGE || goog.userAgent.ASSUME_OPERA ||
+ goog.userAgent.product.ASSUME_FIREFOX ||
+ goog.userAgent.product.ASSUME_IPHONE ||
+ goog.userAgent.product.ASSUME_IPAD ||
+ goog.userAgent.product.ASSUME_ANDROID ||
+ goog.userAgent.product.ASSUME_CHROME ||
+ goog.userAgent.product.ASSUME_SAFARI;
+
+
+/**
+ * Whether the code is running on the Opera web browser.
+ * @type {boolean}
+ */
+goog.userAgent.product.OPERA = goog.userAgent.OPERA;
+
+
+/**
+ * Whether the code is running on an IE web browser.
+ * @type {boolean}
+ */
+goog.userAgent.product.IE = goog.userAgent.IE;
+
+
+/**
+ * Whether the code is running on an Edge web browser.
+ * @type {boolean}
+ */
+goog.userAgent.product.EDGE = goog.userAgent.EDGE;
+
+
+/**
+ * Whether the code is running on the Firefox web browser.
+ * @type {boolean}
+ */
+goog.userAgent.product.FIREFOX = goog.userAgent.product.PRODUCT_KNOWN_ ?
+ goog.userAgent.product.ASSUME_FIREFOX :
+ goog.labs.userAgent.browser.isFirefox();
+
+
+/**
+ * Whether the user agent is an iPhone or iPod (as in iPod touch).
+ * @return {boolean}
+ * @private
+ */
+goog.userAgent.product.isIphoneOrIpod_ = function() {
+ return goog.labs.userAgent.platform.isIphone() ||
+ goog.labs.userAgent.platform.isIpod();
+};
+
+
+/**
+ * Whether the code is running on an iPhone or iPod touch.
+ *
+ * iPod touch is considered an iPhone for legacy reasons.
+ * @type {boolean}
+ */
+goog.userAgent.product.IPHONE = goog.userAgent.product.PRODUCT_KNOWN_ ?
+ goog.userAgent.product.ASSUME_IPHONE :
+ goog.userAgent.product.isIphoneOrIpod_();
+
+
+/**
+ * Whether the code is running on an iPad.
+ * @type {boolean}
+ */
+goog.userAgent.product.IPAD = goog.userAgent.product.PRODUCT_KNOWN_ ?
+ goog.userAgent.product.ASSUME_IPAD :
+ goog.labs.userAgent.platform.isIpad();
+
+
+/**
+ * Whether the code is running on AOSP browser or WebView inside
+ * a pre KitKat Android phone or tablet.
+ * @type {boolean}
+ */
+goog.userAgent.product.ANDROID = goog.userAgent.product.PRODUCT_KNOWN_ ?
+ goog.userAgent.product.ASSUME_ANDROID :
+ goog.labs.userAgent.browser.isAndroidBrowser();
+
+
+/**
+ * Whether the code is running on the Chrome web browser on any platform
+ * or AOSP browser or WebView in a KitKat+ Android phone or tablet.
+ * @type {boolean}
+ */
+goog.userAgent.product.CHROME = goog.userAgent.product.PRODUCT_KNOWN_ ?
+ goog.userAgent.product.ASSUME_CHROME :
+ goog.labs.userAgent.browser.isChrome();
+
+
+/**
+ * @return {boolean} Whether the browser is Safari on desktop.
+ * @private
+ */
+goog.userAgent.product.isSafariDesktop_ = function() {
+ return goog.labs.userAgent.browser.isSafari() &&
+ !goog.labs.userAgent.platform.isIos();
+};
+
+
+/**
+ * Whether the code is running on the desktop Safari web browser.
+ * Note: the legacy behavior here is only true for Safari not running
+ * on iOS.
+ * @type {boolean}
+ */
+goog.userAgent.product.SAFARI = goog.userAgent.product.PRODUCT_KNOWN_ ?
+ goog.userAgent.product.ASSUME_SAFARI :
+ goog.userAgent.product.isSafariDesktop_();
diff --git a/chromium/third_party/ink/closure/useragent/useragent.js b/chromium/third_party/ink/closure/useragent/useragent.js
new file mode 100644
index 00000000000..007d5712e9e
--- /dev/null
+++ b/chromium/third_party/ink/closure/useragent/useragent.js
@@ -0,0 +1,581 @@
+// Copyright 2006 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Rendering engine detection.
+ * @see <a href="http://www.useragentstring.com/">User agent strings</a>
+ * For information on the browser brand (such as Safari versus Chrome), see
+ * goog.userAgent.product.
+ * @author pupius@google.com (Daniel Pupius)
+ * @author arv@google.com (Erik Arvidsson)
+ * @see ../demos/useragent.html
+ */
+
+goog.provide('goog.userAgent');
+
+goog.require('goog.labs.userAgent.browser');
+goog.require('goog.labs.userAgent.engine');
+goog.require('goog.labs.userAgent.platform');
+goog.require('goog.labs.userAgent.util');
+goog.require('goog.reflect');
+goog.require('goog.string');
+
+
+/**
+ * @define {boolean} Whether we know at compile-time that the browser is IE.
+ */
+goog.define('goog.userAgent.ASSUME_IE', false);
+
+
+/**
+ * @define {boolean} Whether we know at compile-time that the browser is EDGE.
+ */
+goog.define('goog.userAgent.ASSUME_EDGE', false);
+
+
+/**
+ * @define {boolean} Whether we know at compile-time that the browser is GECKO.
+ */
+goog.define('goog.userAgent.ASSUME_GECKO', false);
+
+
+/**
+ * @define {boolean} Whether we know at compile-time that the browser is WEBKIT.
+ */
+goog.define('goog.userAgent.ASSUME_WEBKIT', false);
+
+
+/**
+ * @define {boolean} Whether we know at compile-time that the browser is a
+ * mobile device running WebKit e.g. iPhone or Android.
+ */
+goog.define('goog.userAgent.ASSUME_MOBILE_WEBKIT', false);
+
+
+/**
+ * @define {boolean} Whether we know at compile-time that the browser is OPERA.
+ */
+goog.define('goog.userAgent.ASSUME_OPERA', false);
+
+
+/**
+ * @define {boolean} Whether the
+ * {@code goog.userAgent.isVersionOrHigher}
+ * function will return true for any version.
+ */
+goog.define('goog.userAgent.ASSUME_ANY_VERSION', false);
+
+
+/**
+ * Whether we know the browser engine at compile-time.
+ * @type {boolean}
+ * @private
+ */
+goog.userAgent.BROWSER_KNOWN_ = goog.userAgent.ASSUME_IE ||
+ goog.userAgent.ASSUME_EDGE || goog.userAgent.ASSUME_GECKO ||
+ goog.userAgent.ASSUME_MOBILE_WEBKIT || goog.userAgent.ASSUME_WEBKIT ||
+ goog.userAgent.ASSUME_OPERA;
+
+
+/**
+ * Returns the userAgent string for the current browser.
+ *
+ * @return {string} The userAgent string.
+ */
+goog.userAgent.getUserAgentString = function() {
+ return goog.labs.userAgent.util.getUserAgent();
+};
+
+
+/**
+ * TODO(nnaze): Change type to "Navigator" and update compilation targets.
+ * @return {?Object} The native navigator object.
+ */
+goog.userAgent.getNavigator = function() {
+ // Need a local navigator reference instead of using the global one,
+ // to avoid the rare case where they reference different objects.
+ // (in a WorkerPool, for example).
+ return goog.global['navigator'] || null;
+};
+
+
+/**
+ * Whether the user agent is Opera.
+ * @type {boolean}
+ */
+goog.userAgent.OPERA = goog.userAgent.BROWSER_KNOWN_ ?
+ goog.userAgent.ASSUME_OPERA :
+ goog.labs.userAgent.browser.isOpera();
+
+
+/**
+ * Whether the user agent is Internet Explorer.
+ * @type {boolean}
+ */
+goog.userAgent.IE = goog.userAgent.BROWSER_KNOWN_ ?
+ goog.userAgent.ASSUME_IE :
+ goog.labs.userAgent.browser.isIE();
+
+
+/**
+ * Whether the user agent is Microsoft Edge.
+ * @type {boolean}
+ */
+goog.userAgent.EDGE = goog.userAgent.BROWSER_KNOWN_ ?
+ goog.userAgent.ASSUME_EDGE :
+ goog.labs.userAgent.engine.isEdge();
+
+
+/**
+ * Whether the user agent is MS Internet Explorer or MS Edge.
+ * @type {boolean}
+ */
+goog.userAgent.EDGE_OR_IE = goog.userAgent.EDGE || goog.userAgent.IE;
+
+
+/**
+ * Whether the user agent is Gecko. Gecko is the rendering engine used by
+ * Mozilla, Firefox, and others.
+ * @type {boolean}
+ */
+goog.userAgent.GECKO = goog.userAgent.BROWSER_KNOWN_ ?
+ goog.userAgent.ASSUME_GECKO :
+ goog.labs.userAgent.engine.isGecko();
+
+
+/**
+ * Whether the user agent is WebKit. WebKit is the rendering engine that
+ * Safari, Android and others use.
+ * @type {boolean}
+ */
+goog.userAgent.WEBKIT = goog.userAgent.BROWSER_KNOWN_ ?
+ goog.userAgent.ASSUME_WEBKIT || goog.userAgent.ASSUME_MOBILE_WEBKIT :
+ goog.labs.userAgent.engine.isWebKit();
+
+
+/**
+ * Whether the user agent is running on a mobile device.
+ *
+ * This is a separate function so that the logic can be tested.
+ *
+ * TODO(nnaze): Investigate swapping in goog.labs.userAgent.device.isMobile().
+ *
+ * @return {boolean} Whether the user agent is running on a mobile device.
+ * @private
+ */
+goog.userAgent.isMobile_ = function() {
+ return goog.userAgent.WEBKIT &&
+ goog.labs.userAgent.util.matchUserAgent('Mobile');
+};
+
+
+/**
+ * Whether the user agent is running on a mobile device.
+ *
+ * TODO(nnaze): Consider deprecating MOBILE when labs.userAgent
+ * is promoted as the gecko/webkit logic is likely inaccurate.
+ *
+ * @type {boolean}
+ */
+goog.userAgent.MOBILE =
+ goog.userAgent.ASSUME_MOBILE_WEBKIT || goog.userAgent.isMobile_();
+
+
+/**
+ * Used while transitioning code to use WEBKIT instead.
+ * @type {boolean}
+ * @deprecated Use {@link goog.userAgent.product.SAFARI} instead.
+ * TODO(nicksantos): Delete this from goog.userAgent.
+ */
+goog.userAgent.SAFARI = goog.userAgent.WEBKIT;
+
+
+/**
+ * @return {string} the platform (operating system) the user agent is running
+ * on. Default to empty string because navigator.platform may not be defined
+ * (on Rhino, for example).
+ * @private
+ */
+goog.userAgent.determinePlatform_ = function() {
+ var navigator = goog.userAgent.getNavigator();
+ return navigator && navigator.platform || '';
+};
+
+
+/**
+ * The platform (operating system) the user agent is running on. Default to
+ * empty string because navigator.platform may not be defined (on Rhino, for
+ * example).
+ * @type {string}
+ */
+goog.userAgent.PLATFORM = goog.userAgent.determinePlatform_();
+
+
+/**
+ * @define {boolean} Whether the user agent is running on a Macintosh operating
+ * system.
+ */
+goog.define('goog.userAgent.ASSUME_MAC', false);
+
+
+/**
+ * @define {boolean} Whether the user agent is running on a Windows operating
+ * system.
+ */
+goog.define('goog.userAgent.ASSUME_WINDOWS', false);
+
+
+/**
+ * @define {boolean} Whether the user agent is running on a Linux operating
+ * system.
+ */
+goog.define('goog.userAgent.ASSUME_LINUX', false);
+
+
+/**
+ * @define {boolean} Whether the user agent is running on a X11 windowing
+ * system.
+ */
+goog.define('goog.userAgent.ASSUME_X11', false);
+
+
+/**
+ * @define {boolean} Whether the user agent is running on Android.
+ */
+goog.define('goog.userAgent.ASSUME_ANDROID', false);
+
+
+/**
+ * @define {boolean} Whether the user agent is running on an iPhone.
+ */
+goog.define('goog.userAgent.ASSUME_IPHONE', false);
+
+
+/**
+ * @define {boolean} Whether the user agent is running on an iPad.
+ */
+goog.define('goog.userAgent.ASSUME_IPAD', false);
+
+
+/**
+ * @define {boolean} Whether the user agent is running on an iPod.
+ */
+goog.define('goog.userAgent.ASSUME_IPOD', false);
+
+
+/**
+ * @type {boolean}
+ * @private
+ */
+goog.userAgent.PLATFORM_KNOWN_ = goog.userAgent.ASSUME_MAC ||
+ goog.userAgent.ASSUME_WINDOWS || goog.userAgent.ASSUME_LINUX ||
+ goog.userAgent.ASSUME_X11 || goog.userAgent.ASSUME_ANDROID ||
+ goog.userAgent.ASSUME_IPHONE || goog.userAgent.ASSUME_IPAD ||
+ goog.userAgent.ASSUME_IPOD;
+
+
+/**
+ * Whether the user agent is running on a Macintosh operating system.
+ * @type {boolean}
+ */
+goog.userAgent.MAC = goog.userAgent.PLATFORM_KNOWN_ ?
+ goog.userAgent.ASSUME_MAC :
+ goog.labs.userAgent.platform.isMacintosh();
+
+
+/**
+ * Whether the user agent is running on a Windows operating system.
+ * @type {boolean}
+ */
+goog.userAgent.WINDOWS = goog.userAgent.PLATFORM_KNOWN_ ?
+ goog.userAgent.ASSUME_WINDOWS :
+ goog.labs.userAgent.platform.isWindows();
+
+
+/**
+ * Whether the user agent is Linux per the legacy behavior of
+ * goog.userAgent.LINUX, which considered ChromeOS to also be
+ * Linux.
+ * @return {boolean}
+ * @private
+ */
+goog.userAgent.isLegacyLinux_ = function() {
+ return goog.labs.userAgent.platform.isLinux() ||
+ goog.labs.userAgent.platform.isChromeOS();
+};
+
+
+/**
+ * Whether the user agent is running on a Linux operating system.
+ *
+ * Note that goog.userAgent.LINUX considers ChromeOS to be Linux,
+ * while goog.labs.userAgent.platform considers ChromeOS and
+ * Linux to be different OSes.
+ *
+ * @type {boolean}
+ */
+goog.userAgent.LINUX = goog.userAgent.PLATFORM_KNOWN_ ?
+ goog.userAgent.ASSUME_LINUX :
+ goog.userAgent.isLegacyLinux_();
+
+
+/**
+ * @return {boolean} Whether the user agent is an X11 windowing system.
+ * @private
+ */
+goog.userAgent.isX11_ = function() {
+ var navigator = goog.userAgent.getNavigator();
+ return !!navigator &&
+ goog.string.contains(navigator['appVersion'] || '', 'X11');
+};
+
+
+/**
+ * Whether the user agent is running on a X11 windowing system.
+ * @type {boolean}
+ */
+goog.userAgent.X11 = goog.userAgent.PLATFORM_KNOWN_ ?
+ goog.userAgent.ASSUME_X11 :
+ goog.userAgent.isX11_();
+
+
+/**
+ * Whether the user agent is running on Android.
+ * @type {boolean}
+ */
+goog.userAgent.ANDROID = goog.userAgent.PLATFORM_KNOWN_ ?
+ goog.userAgent.ASSUME_ANDROID :
+ goog.labs.userAgent.platform.isAndroid();
+
+
+/**
+ * Whether the user agent is running on an iPhone.
+ * @type {boolean}
+ */
+goog.userAgent.IPHONE = goog.userAgent.PLATFORM_KNOWN_ ?
+ goog.userAgent.ASSUME_IPHONE :
+ goog.labs.userAgent.platform.isIphone();
+
+
+/**
+ * Whether the user agent is running on an iPad.
+ * @type {boolean}
+ */
+goog.userAgent.IPAD = goog.userAgent.PLATFORM_KNOWN_ ?
+ goog.userAgent.ASSUME_IPAD :
+ goog.labs.userAgent.platform.isIpad();
+
+
+/**
+ * Whether the user agent is running on an iPod.
+ * @type {boolean}
+ */
+goog.userAgent.IPOD = goog.userAgent.PLATFORM_KNOWN_ ?
+ goog.userAgent.ASSUME_IPOD :
+ goog.labs.userAgent.platform.isIpod();
+
+
+/**
+ * Whether the user agent is running on iOS.
+ * @type {boolean}
+ */
+goog.userAgent.IOS = goog.userAgent.PLATFORM_KNOWN_ ?
+ (goog.userAgent.ASSUME_IPHONE || goog.userAgent.ASSUME_IPAD ||
+ goog.userAgent.ASSUME_IPOD) :
+ goog.labs.userAgent.platform.isIos();
+
+/**
+ * @return {string} The string that describes the version number of the user
+ * agent.
+ * @private
+ */
+goog.userAgent.determineVersion_ = function() {
+ // All browsers have different ways to detect the version and they all have
+ // different naming schemes.
+ // version is a string rather than a number because it may contain 'b', 'a',
+ // and so on.
+ var version = '';
+ var arr = goog.userAgent.getVersionRegexResult_();
+ if (arr) {
+ version = arr ? arr[1] : '';
+ }
+
+ if (goog.userAgent.IE) {
+ // IE9 can be in document mode 9 but be reporting an inconsistent user agent
+ // version. If it is identifying as a version lower than 9 we take the
+ // documentMode as the version instead. IE8 has similar behavior.
+ // It is recommended to set the X-UA-Compatible header to ensure that IE9
+ // uses documentMode 9.
+ var docMode = goog.userAgent.getDocumentMode_();
+ if (docMode != null && docMode > parseFloat(version)) {
+ return String(docMode);
+ }
+ }
+
+ return version;
+};
+
+
+/**
+ * @return {?Array|undefined} The version regex matches from parsing the user
+ * agent string. These regex statements must be executed inline so they can
+ * be compiled out by the closure compiler with the rest of the useragent
+ * detection logic when ASSUME_* is specified.
+ * @private
+ */
+goog.userAgent.getVersionRegexResult_ = function() {
+ var userAgent = goog.userAgent.getUserAgentString();
+ if (goog.userAgent.GECKO) {
+ return /rv\:([^\);]+)(\)|;)/.exec(userAgent);
+ }
+ if (goog.userAgent.EDGE) {
+ return /Edge\/([\d\.]+)/.exec(userAgent);
+ }
+ if (goog.userAgent.IE) {
+ return /\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/.exec(userAgent);
+ }
+ if (goog.userAgent.WEBKIT) {
+ // WebKit/125.4
+ return /WebKit\/(\S+)/.exec(userAgent);
+ }
+ if (goog.userAgent.OPERA) {
+ // If none of the above browsers were detected but the browser is Opera, the
+ // only string that is of interest is 'Version/<number>'.
+ return /(?:Version)[ \/]?(\S+)/.exec(userAgent);
+ }
+ return undefined;
+};
+
+
+/**
+ * @return {number|undefined} Returns the document mode (for testing).
+ * @private
+ */
+goog.userAgent.getDocumentMode_ = function() {
+ // NOTE(pupius): goog.userAgent may be used in context where there is no DOM.
+ var doc = goog.global['document'];
+ return doc ? doc['documentMode'] : undefined;
+};
+
+
+/**
+ * The version of the user agent. This is a string because it might contain
+ * 'b' (as in beta) as well as multiple dots.
+ * @type {string}
+ */
+goog.userAgent.VERSION = goog.userAgent.determineVersion_();
+
+
+/**
+ * Compares two version numbers.
+ *
+ * @param {string} v1 Version of first item.
+ * @param {string} v2 Version of second item.
+ *
+ * @return {number} 1 if first argument is higher
+ * 0 if arguments are equal
+ * -1 if second argument is higher.
+ * @deprecated Use goog.string.compareVersions.
+ */
+goog.userAgent.compare = function(v1, v2) {
+ return goog.string.compareVersions(v1, v2);
+};
+
+
+/**
+ * Cache for {@link goog.userAgent.isVersionOrHigher}.
+ * Calls to compareVersions are surprisingly expensive and, as a browser's
+ * version number is unlikely to change during a session, we cache the results.
+ * @const
+ * @private
+ */
+goog.userAgent.isVersionOrHigherCache_ = {};
+
+
+/**
+ * Whether the user agent version is higher or the same as the given version.
+ * NOTE: When checking the version numbers for Firefox and Safari, be sure to
+ * use the engine's version, not the browser's version number. For example,
+ * Firefox 3.0 corresponds to Gecko 1.9 and Safari 3.0 to Webkit 522.11.
+ * Opera and Internet Explorer versions match the product release number.<br>
+ * @see <a href="http://en.wikipedia.org/wiki/Safari_version_history">
+ * Webkit</a>
+ * @see <a href="http://en.wikipedia.org/wiki/Gecko_engine">Gecko</a>
+ *
+ * @param {string|number} version The version to check.
+ * @return {boolean} Whether the user agent version is higher or the same as
+ * the given version.
+ */
+goog.userAgent.isVersionOrHigher = function(version) {
+ return goog.userAgent.ASSUME_ANY_VERSION ||
+ goog.reflect.cache(
+ goog.userAgent.isVersionOrHigherCache_, version, function() {
+ return goog.string.compareVersions(
+ goog.userAgent.VERSION, version) >= 0;
+ });
+};
+
+
+/**
+ * Deprecated alias to {@code goog.userAgent.isVersionOrHigher}.
+ * @param {string|number} version The version to check.
+ * @return {boolean} Whether the user agent version is higher or the same as
+ * the given version.
+ * @deprecated Use goog.userAgent.isVersionOrHigher().
+ */
+goog.userAgent.isVersion = goog.userAgent.isVersionOrHigher;
+
+
+/**
+ * Whether the IE effective document mode is higher or the same as the given
+ * document mode version.
+ * NOTE: Only for IE, return false for another browser.
+ *
+ * @param {number} documentMode The document mode version to check.
+ * @return {boolean} Whether the IE effective document mode is higher or the
+ * same as the given version.
+ */
+goog.userAgent.isDocumentModeOrHigher = function(documentMode) {
+ return Number(goog.userAgent.DOCUMENT_MODE) >= documentMode;
+};
+
+
+/**
+ * Deprecated alias to {@code goog.userAgent.isDocumentModeOrHigher}.
+ * @param {number} version The version to check.
+ * @return {boolean} Whether the IE effective document mode is higher or the
+ * same as the given version.
+ * @deprecated Use goog.userAgent.isDocumentModeOrHigher().
+ */
+goog.userAgent.isDocumentMode = goog.userAgent.isDocumentModeOrHigher;
+
+
+/**
+ * For IE version < 7, documentMode is undefined, so attempt to use the
+ * CSS1Compat property to see if we are in standards mode. If we are in
+ * standards mode, treat the browser version as the document mode. Otherwise,
+ * IE is emulating version 5.
+ * @type {number|undefined}
+ * @const
+ */
+goog.userAgent.DOCUMENT_MODE = (function() {
+ var doc = goog.global['document'];
+ var mode = goog.userAgent.getDocumentMode_();
+ if (!doc || !goog.userAgent.IE) {
+ return undefined;
+ }
+ return mode || (doc['compatMode'] == 'CSS1Compat' ?
+ parseInt(goog.userAgent.VERSION, 10) :
+ 5);
+})();
diff --git a/chromium/third_party/ink/ink/web/js/canvas_manager/canvas_manager.js b/chromium/third_party/ink/ink/web/js/canvas_manager/canvas_manager.js
new file mode 100644
index 00000000000..c4db2c8a3ac
--- /dev/null
+++ b/chromium/third_party/ink/ink/web/js/canvas_manager/canvas_manager.js
@@ -0,0 +1,598 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+goog.provide('ink.CanvasManager');
+
+goog.require('goog.array');
+goog.require('goog.asserts');
+goog.require('goog.html.SafeUrl');
+goog.require('goog.structs.Set');
+goog.require('goog.ui.Component');
+goog.require('ink.BrushModel');
+goog.require('ink.Color');
+goog.require('ink.ElementListener');
+goog.require('ink.SketchologyEngineWrapper');
+goog.require('ink.embed.events');
+goog.require('ink.util');
+goog.require('sketchology.proto.BackgroundImageInfo');
+goog.require('sketchology.proto.Border');
+goog.require('sketchology.proto.ImageExport');
+goog.require('sketchology.proto.Rect');
+goog.require('sketchology.proto.SetCallbackFlags');
+
+
+
+/**
+ * The controller of the canvas used for drawing.
+ *
+ * @param {?string} engineUrl
+ * @param {ink.util.SEngineType} sengineType
+ * @struct
+ * @constructor
+ * @extends {goog.ui.Component}
+ * @implements {ink.ElementListener}
+ */
+ink.CanvasManager = function(engineUrl, sengineType) {
+ ink.CanvasManager.base(this, 'constructor');
+
+ /** @private {ink.BrushModel} */
+ this.brushModel_ = null;
+
+ /** @private {!ink.SketchologyEngineWrapper} */
+ this.engine_ = new ink.SketchologyEngineWrapper(
+ engineUrl, this, goog.bind(this.onPngExportComplete_, this), sengineType);
+ this.addChild(this.engine_);
+ this.getHandler().listenOnce(
+ this.engine_, ink.SketchologyEngineWrapper.EventType.CANVAS_INITIALIZED,
+ goog.bind(function() {
+ this.brushUpdate_();
+ this.setBorderImage_();
+ this.dispatchEvent(ink.embed.events.EventType.CANVAS_INITIALIZED);
+ }, this));
+
+ // Redispatch CANVAS_FATAL_ERROR events as the top level FATAL_ERROR.
+ this.getHandler().listen(
+ this.engine_, ink.SketchologyEngineWrapper.EventType.CANVAS_FATAL_ERROR,
+ this.dispatchFatalError_);
+
+ this.getHandler().listen(
+ this.engine_, ink.SketchologyEngineWrapper.EventType.PEN_MODE_ENABLED,
+ (ev) => {
+ this.dispatchEvent(new ink.embed.events.PenModeEnabled(ev.enabled));
+ });
+
+ /**
+ * Known element UUIDs from bottom to top
+ * @private {Array.<string>}
+ */
+ this.UUIDs_ = [];
+
+ /**
+ * Set of UUIDs created by the engine but not yet acknowledged by Brix.
+ * @private {goog.structs.Set}}
+ */
+ this.pendingUUIDs_ = new goog.structs.Set();
+
+ /**
+ * Next local ID to use for Brix element bundles missing IDs.
+ * @private {number}
+ */
+ this.nextLocalId_ = 0;
+
+ /**
+ * @const
+ * @type {string}
+ */
+ this.FAKE_UUID = 'fake';
+
+ /**
+ * Background counter
+ * @type {number}
+ * @private
+ */
+ this.bgCount_ = 0;
+
+ /** @private {?function(!goog.html.SafeUrl)} */
+ this.onPngExportCompleteCallback_ = null;
+
+ /** @private {boolean} */
+ this.exportAsBlob_ = false;
+};
+goog.inherits(ink.CanvasManager, goog.ui.Component);
+
+
+/** @const */
+ink.CanvasManager.BORDER_IMAGE = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEU' +
+ 'gAAAFgAAABYCAYAAABxlTA0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAm' +
+ 'pwYAAAAB3RJTUUH4AgPEBYrHoEFUgAAAw1JREFUeNrt3b9uE0EQBvBvZvd8LURUkVwEkEDAm' +
+ '9AgQYOQKHgZWloqUMoUaVJSpAivQBEhJGhSWiAlKXz7JwXey9nE9vrOgHT3fZKVFM5F/mUyN' +
+ '5tIY8F2EtG/yNYucnZ21keg/57d3V1RMvzdEJjABGYITGACM+1iNz5RxGGPzCIbnT+i3RA1A' +
+ 'jgFcADgR4zRDwTVABjHGF8AeJwOaDnYNgd29jGGED6JyMvxeDwZYvVOJpN3FxcXH0TkWfMov' +
+ 'Qpac6p39jgdMi4A7Ozs/HLOvQkhfEkurW9yDVh47+GcOxgybsre3t5P59y+937OqHUFhxAQQ' +
+ 'kBVVd85E/yOc+5bcuk0pqWLeO/hvQ+krV289x45yLqqPSRk5xym0yllZ5lOp6iqCiGEtW3C5' +
+ 'lZwzq/DUOK9h4jUwK3GtGYFz1oEZa97cA2crJaNapozoiVk5rqCm+2h898ihn487uKiWPMPy' +
+ '2arYG7G7TQHM91CYAITmCEwgQnMEJjABGYITGCGwAQmMENgAhOYITCBCUwCAhOYITCBCcwQm' +
+ 'MAEZghMYIbABCYwQ2ACE5ghMIEJzBCYwAyBCUxghsAEJjBDYAIzBP63wFkLcUX4lhAp3nu79' +
+ 'Qr23t8lbZ37WwNuLFx7fnJycnvossfHx7dijK+WGLWrYBFBjPHh5eXl/tHR0Z0h41ZV9T7G+' +
+ 'EREum/AThcREaiqqOpTAJ8PDw8/VlX11TnnhgArIrYsy3vn5+evy7J8NLNADrJd1xpUFcaY9' +
+ 'BBVfSAib2foAPq7GTAZGGNSkcFaC2stjDH161+BLGsrOAEXRYGyLOsNgKqK5hbovgIng/T6R' +
+ '6MRiqKogVtVcPqi5k+tKIo5XOfcYICNMbDWYjQaoSzLP4BXtYqVFayqiDHCWlsjJnDnXPYG0' +
+ 'j5UcCqy9LDWtq/gZcipHxVFMbfitec3uMX70FwP7nyTW2zyaSt236v3pipO7UJVs9pDVgU3p' +
+ '4n0jZqwQwBeHFlzYLPn4MXPb4Lt+5i2CJ1zgsuu4GUX2vBNk3qJnvX8jOdwv3h7O1wBYIqaD' +
+ '5lCtYoAAAAASUVORK5CYII=';
+
+
+/**
+ * This color needs to match BORDER_IMAGE.
+ * @const
+ */
+ink.CanvasManager.OUT_OF_BOUNDS_COLOR = 0xe6e6e6ff;
+
+
+/** @override */
+ink.CanvasManager.prototype.enterDocument = function() {
+ ink.CanvasManager.base(this, 'enterDocument');
+
+ this.engine_.render(this.getElement());
+
+ var handler = this.getHandler();
+ goog.asserts.assert(handler);
+
+ this.brushModel_ = ink.BrushModel.getInstance(this);
+
+ handler.listen(
+ this.brushModel_, ink.BrushModel.EventType.CHANGE, this.brushUpdate_);
+};
+
+/**
+ * Sets or unsets readOnly on the engine.
+ * @param {boolean} readOnly
+ */
+ink.CanvasManager.prototype.setReadOnly = function(readOnly) {
+ this.engine_.setReadOnly(readOnly);
+};
+
+
+/**
+ * Export the scene as a PNG from the engine.
+ * @param {number} maxWidth
+ * @param {boolean} drawBackground
+ * @param {function(!goog.html.SafeUrl)} callback
+ * @param {boolean=} opt_asBlob
+ */
+ink.CanvasManager.prototype.exportPng = function(
+ maxWidth, drawBackground, callback, opt_asBlob) {
+ this.onPngExportCompleteCallback_ = callback;
+ this.exportAsBlob_ = !!opt_asBlob;
+ var exportProto = new sketchology.proto.ImageExport();
+ exportProto.setMaxDimensionPx(maxWidth);
+ exportProto.setShouldDrawBackground(drawBackground);
+ this.engine_.exportPng(exportProto);
+};
+
+
+/**
+ * @private
+ * @param {number} width
+ * @param {number} height
+ * @param {Uint8ClampedArray} bytesArr
+ */
+ink.CanvasManager.prototype.onPngExportComplete_ = function(
+ width, height, bytesArr) {
+ if (this.onPngExportCompleteCallback_) {
+ try {
+ var imageData = new ImageData(bytesArr, width, height);
+ var scratchCanvas =
+ /** @type {!HTMLCanvasElement} */ (document.createElement('canvas'));
+ var scratchContext =
+ /** @type {!CanvasRenderingContext2D} */ (
+ scratchCanvas.getContext('2d'));
+ scratchCanvas.width = width;
+ scratchCanvas.height = height;
+ scratchContext.putImageData(imageData, 0, 0);
+ } catch (ex) {
+ this.dispatchFatalError_(ex);
+ return;
+ }
+ if (this.exportAsBlob_) {
+ var cb = (blob) => {
+ this.onPngExportCompleteCallback_(goog.html.SafeUrl.fromBlob(blob));
+ };
+
+ if (scratchCanvas['msToBlob']) {
+ scratchCanvas['msToBlob'](cb, 'image/png');
+ } else {
+ scratchCanvas.toBlob(cb, 'image/png');
+ }
+ } else {
+ this.onPngExportCompleteCallback_(
+ goog.html.SafeUrl.fromDataUrl(scratchCanvas.toDataURL()));
+ }
+ }
+};
+
+
+/**
+ * Sets a background image, and sets the page bounds to match the image size.
+ * @param {Uint8ClampedArray} data The image data in RGBA 8888.
+ * @param {goog.math.Size} size The image dimensions.
+ */
+ink.CanvasManager.prototype.setBackgroundImage = function(data, size) {
+ this.setBackgroundImage_(data, size);
+};
+
+
+/**
+ * Sets a background image and scales the image to match the existing page
+ * bounds. Will not display the background if no page bounds are set.
+ * @param {Uint8ClampedArray} data The image data in RGBA 8888.
+ * @param {goog.math.Size} size The image dimensions.
+ */
+ink.CanvasManager.prototype.setImageToUseForPageBackground = function(
+ data, size) {
+ this.setBackgroundImage_(data, size, {'bounds': 'none'});
+};
+
+
+/**
+ * @private
+ * @param {Uint8ClampedArray} data The image data in RGBA 8888.
+ * @param {goog.math.Size} size The image dimensions.
+ * @param {Object<string, *>=} opt_options
+ */
+ink.CanvasManager.prototype.setBackgroundImage_ = function(
+ data, size, opt_options) {
+ opt_options = opt_options || {};
+
+ var nextUri = 'sketchology://background_' + this.bgCount_;
+ this.bgCount_++;
+
+ var bgImageProto = new sketchology.proto.BackgroundImageInfo();
+ bgImageProto.setUri(nextUri);
+ if (opt_options['bounds'] != 'none') {
+ var optBounds = opt_options['bounds'] ||
+ {'xlow': 0, 'ylow': 0, 'xhigh': size.width, 'yhigh': size.height};
+ var bounds = new sketchology.proto.Rect();
+ bounds.setXlow(optBounds['xlow']);
+ bounds.setYlow(optBounds['ylow']);
+ bounds.setXhigh(optBounds['xhigh']);
+ bounds.setYhigh(optBounds['yhigh']);
+ bgImageProto.setBounds(bounds);
+ }
+ this.engine_.setBackgroundImage(data, size, nextUri, bgImageProto);
+};
+
+
+/**
+ * Set background color.
+ * @param {ink.Color} color
+ */
+ink.CanvasManager.prototype.setBackgroundColor = function(color) {
+ this.engine_.setBackgroundColor(color);
+};
+
+
+/** @private */
+ink.CanvasManager.prototype.brushUpdate_ = function() {
+ goog.asserts.assert(this.brushModel_);
+
+ var tool_type = this.brushModel_.getToolType();
+ var brush_type = this.brushModel_.getBrushType();
+ var strokeWidth = this.brushModel_.getStrokeWidth();
+
+ var colorString = this.brushModel_.getColor().substring(1);
+ var color = new ink.Color(parseInt(colorString, 16));
+ // Using this alpha channel would make calligraphy or marker brushes
+ // translucent; highlighter, watercolor, and airbrush have hard-coded alpha
+ // values in the engine.
+ color.a = 0xFF; // Set alpha to opaque
+
+ this.engine_.brushUpdate(
+ color.getRgbaUint32(), strokeWidth, tool_type, brush_type);
+};
+
+
+/**
+ * Handles a new element created in the engine.
+ * @param {string} uuid
+ * @param {string} encodedElement
+ * @param {string} encodedTransform
+ * @override
+ */
+ink.CanvasManager.prototype.onElementCreated = function(
+ uuid, encodedElement, encodedTransform) {
+ this.pendingUUIDs_.add(uuid);
+ this.dispatchEvent(new ink.embed.events.ElementCreatedEvent(
+ uuid, encodedElement, encodedTransform));
+};
+
+
+/**
+ * Handles an element being transformed.
+ * @param {Array.<string>} uuids
+ * @param {Array.<string>} encodedTransforms
+ * @override
+ */
+ink.CanvasManager.prototype.onElementsMutated = function(
+ uuids, encodedTransforms) {
+ this.dispatchEvent(
+ new ink.embed.events.ElementsMutatedEvent(uuids, encodedTransforms));
+};
+
+
+/**
+ * Handles elements being removed.
+ * @param {Array.<string>} uuids
+ * @override
+ */
+ink.CanvasManager.prototype.onElementsRemoved = function(uuids) {
+ this.dispatchEvent(new ink.embed.events.ElementsRemovedEvent(uuids));
+};
+
+
+/**
+ * Handle an addElement request from Brix. If this is a remote add or an
+ * add-by-undo or redo, adds the element to the engine and the local list of
+ * elements (this.UUIDs_) at the specified index. If this is a local add
+ * originated by the engine, the element is already present in the engine and is
+ * simply removed from this.pendingUUIDs_.
+ *
+ * @param {!Object<string, string>} bundle
+ * @param {number} idx index to add the element at
+ * @param {boolean} isLocal
+ */
+ink.CanvasManager.prototype.addElement = function(bundle, idx, isLocal) {
+ if (!bundle['id']) {
+ bundle['id'] = 'local-' + this.nextLocalId_++;
+ }
+
+ var uuid = bundle['id'];
+ goog.array.insertAt(this.UUIDs_, uuid, idx);
+
+ // If the element originated in the engine, it should be present in the
+ // pending UUID set, so we just remove it from that set so that future adds by
+ // undo/redo will work as expected.
+ if (this.pendingUUIDs_.contains(uuid)) {
+ this.pendingUUIDs_.remove(uuid);
+ } else {
+ // If the element is a remote add or an add by undo or redo, it may not be
+ // the top element, in which case we add it below the element after it.
+ if (idx < this.UUIDs_.length - 1) {
+ this.engine_.addElementBelow(bundle, this.UUIDs_[idx + 1]);
+ } else {
+ this.engine_.addElement(bundle);
+ }
+ }
+
+ // TODO(wfurr): Figure out how to have the engine wake itself up.
+ // See b/18830720.
+ this.engine_.poke();
+};
+
+
+/**
+ * Removes a number of elements.
+ * @param {number} idx index to start removing.
+ * @param {number} count number of items to remove.
+ */
+ink.CanvasManager.prototype.removeElements = function(idx, count) {
+ for (var i = 0; i < count; i++) {
+ var uuid = this.UUIDs_[idx];
+ this.engine_.removeElement(uuid);
+ goog.array.removeAt(this.UUIDs_, idx);
+ }
+
+ // TODO(wfurr): Figure out how to have the engine wake itself up.
+ // See b/18830720.
+ this.engine_.poke();
+};
+
+
+/**
+ * Resets the engine but does not dispatch any Brix related events. Used to
+ * clear the canvas to reuse it to display another drawing.
+ */
+ink.CanvasManager.prototype.resetCanvas = function() {
+ this.engine_.clear();
+ this.engine_.setBackgroundColor(ink.Color.DEFAULT_BACKGROUND_COLOR);
+ this.pendingUUIDs_.clear();
+ this.UUIDs_ = [];
+ this.engine_.poke();
+};
+
+
+/**
+ * Clears the canvas.
+ */
+ink.CanvasManager.prototype.clear = function() {
+ this.engine_.removeAll();
+};
+
+
+/**
+ * Sets element transforms.
+ * @param {Array.<string>} uuids
+ * @param {Array.<string>} encodedTransforms
+ */
+ink.CanvasManager.prototype.setElementTransforms = function(
+ uuids, encodedTransforms) {
+ this.engine_.setElementTransforms(uuids, encodedTransforms);
+};
+
+
+/**
+ * Set callback flags
+ * @param {!sketchology.proto.SetCallbackFlags} setCallbackFlags
+ */
+ink.CanvasManager.prototype.setCallbackFlags = function(setCallbackFlags) {
+ this.engine_.setCallbackFlags(setCallbackFlags);
+};
+
+
+/**
+ * Sets the size of the page.
+ * @param {number} left
+ * @param {number} top
+ * @param {number} right
+ * @param {number} bottom
+ */
+ink.CanvasManager.prototype.setPageBounds = function(left, top, right, bottom) {
+ this.engine_.setPageBounds(left, top, right, bottom);
+};
+
+
+/**
+ * Deselects anything selected with the edit tool.
+ */
+ink.CanvasManager.prototype.deselectAll = function() {
+ this.engine_.deselectAll();
+};
+
+
+/**
+ * Sets the border image.
+ * @private
+ */
+ink.CanvasManager.prototype.setBorderImage_ = function() {
+ var self = this;
+ var uri = 'sketchology://border0';
+ var borderImageProto = new sketchology.proto.Border();
+ borderImageProto.setUri(uri);
+ borderImageProto.setScale(1);
+
+ ink.util.getImageBytes(ink.CanvasManager.BORDER_IMAGE, function(data, size) {
+ self.engine_.setBorderImage(
+ data, size, uri, borderImageProto,
+ ink.CanvasManager.OUT_OF_BOUNDS_COLOR);
+ });
+};
+
+
+/**
+ * Dispatches a FATAL_ERROR event, and throws an Error if it isn't handled.
+ * @param {Error=} opt_cause
+ * @private
+ */
+ink.CanvasManager.prototype.dispatchFatalError_ = function(opt_cause) {
+ if (this.dispatchEvent(new ink.embed.events.FatalErrorEvent(opt_cause))) {
+ // Unless one of the listeners returns false or preventDefaults, throw an
+ // error to trigger default exception handlers on the page.
+ throw opt_cause || new Error('Unhandled fatal ink error');
+ }
+};
+
+
+/**
+ * Enable or disable an engine flag.
+ * @param {sketchology.proto.Flag} which
+ * @param {boolean} enable
+ */
+ink.CanvasManager.prototype.assignFlag = function(which, enable) {
+ this.engine_.assignFlag(which, enable);
+};
+
+
+/**
+ * Simple undo. This only works if the SEngine was constructed with a
+ * SingleUserDocument with InMemoryStorage.
+ */
+ink.CanvasManager.prototype.undo = function() {
+ this.engine_.undo();
+};
+
+
+/**
+ * Simple redo. This only works if the SEngine was constructed with a
+ * SingleUserDocument with InMemoryStorage.
+ */
+ink.CanvasManager.prototype.redo = function() {
+ this.engine_.redo();
+};
+
+
+/**
+ * Returns the current snapshot.
+ * @param {function(!sketchology.proto.Snapshot)} callback
+ */
+ink.CanvasManager.prototype.getSnapshot = function(callback) {
+ this.engine_.getSnapshot(callback);
+};
+
+
+/**
+ * Loads a document from a snapshot.
+ *
+ * @param {!sketchology.proto.Snapshot} snapshotProto
+ */
+ink.CanvasManager.prototype.loadFromSnapshot = function(snapshotProto) {
+ this.engine_.loadFromSnapshot(snapshotProto);
+};
+
+
+/**
+ * Allows the user to execute arbitrary commands on the engine.
+ * @param {!sketchology.proto.Command} command
+ */
+ink.CanvasManager.prototype.handleCommand = function(command) {
+ this.engine_.handleCommand(command);
+};
+
+
+/**
+ * Gets the raw engine object. Do not use this.
+ * @return {Object}
+ */
+ink.CanvasManager.prototype.getRawEngineObject = function() {
+ return this.engine_.getRawEngineObject();
+};
+
+
+/**
+ * Generates a snapshot based on a brix document.
+ * @param {!ink.util.RealtimeDocument} brixDoc
+ * @param {function(!sketchology.proto.Snapshot)} callback
+ */
+ink.CanvasManager.prototype.convertBrixDocumentToSnapshot =
+ function(brixDoc, callback) {
+ this.engine_.convertBrixDocumentToSnapshot(brixDoc, callback);
+};
+
+
+/**
+ * @param {!sketchology.proto.Snapshot} snapshot
+ * @param {function(boolean)} callback
+ */
+ink.CanvasManager.prototype.snapshotHasPendingMutations =
+ function(snapshot, callback) {
+ this.engine_.snapshotHasPendingMutations(snapshot, callback);
+};
+
+
+/**
+ * @param {!sketchology.proto.Snapshot} snapshot
+ * @param {function(sketchology.proto.MutationPacket)} callback
+ */
+ink.CanvasManager.prototype.extractMutationPacket =
+ function(snapshot, callback) {
+ this.engine_.extractMutationPacket(snapshot, callback);
+};
+
+
+/**
+ * @param {!sketchology.proto.Snapshot} snapshot
+ * @param {function(sketchology.proto.Snapshot)} callback
+ */
+ink.CanvasManager.prototype.clearPendingMutations =
+ function(snapshot, callback) {
+ this.engine_.clearPendingMutations(snapshot, callback);
+};
+
+
+/**
+ * Calls the given callback once all previous asynchronous engine operations
+ * have been applied.
+ * @param {!Function} callback
+ */
+ink.CanvasManager.prototype.flush = function(callback) {
+ this.engine_.flush(callback);
+};
diff --git a/chromium/third_party/ink/ink/web/js/cursor_updater.js b/chromium/third_party/ink/ink/web/js/cursor_updater.js
new file mode 100644
index 00000000000..5f4803e687c
--- /dev/null
+++ b/chromium/third_party/ink/ink/web/js/cursor_updater.js
@@ -0,0 +1,124 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+goog.provide('ink.CursorUpdater');
+
+goog.require('goog.ui.Component');
+goog.require('ink.BrushModel');
+goog.require('ink.Color');
+goog.require('ink.embed.events');
+goog.require('ink.util');
+
+
+
+/**
+ * @constructor
+ * @extends {goog.ui.Component}
+ * @struct
+ */
+ink.CursorUpdater = function() {
+ ink.CursorUpdater.base(this, 'constructor');
+
+ /** @private {ink.BrushModel} */
+ this.brushModel_ = null;
+};
+goog.inherits(ink.CursorUpdater, goog.ui.Component);
+
+
+/** @override */
+ink.CursorUpdater.prototype.enterDocument = function() {
+ ink.CursorUpdater.base(this, 'enterDocument');
+
+ this.brushModel_ = ink.BrushModel.getInstance(this);
+
+ var handler = this.getHandler();
+
+ handler.listen(this.brushModel_,
+ ink.BrushModel.EventType.CHANGE,
+ this.updateCursor_);
+
+ handler.listen(ink.util.getRootParentComponent(this),
+ ink.embed.events.EventType.DONE_LOADING,
+ this.handleDoneLoadingEvent_);
+};
+
+
+/**
+ * @param {!ink.embed.events.DoneLoadingEvent} evt
+ * @private
+ */
+ink.CursorUpdater.prototype.handleDoneLoadingEvent_ = function(evt) {
+ if (evt.isReadOnly) {
+ // If we aren't editable, use a default cursor.
+ this.getElement().style.cursor = '';
+ } else {
+ this.updateCursor_();
+ }
+};
+
+
+/**
+ * Updates the cursor icon for the drawable area based on the current selection.
+ * @private
+ */
+ink.CursorUpdater.prototype.updateCursor_ = function() {
+ var rgb = this.brushModel_.getActiveColorNumericRbg();
+ var r = 8;
+
+ var url = 'url(' + this.getCursorDataUrlImage(r, rgb) + ')';
+ var target = r + ' ' + r; // target is center of cursor
+ var fallback = ', auto';
+ var cursorStyle = url + target + fallback;
+
+ this.getElement().style.cursor = cursorStyle;
+};
+
+/**
+ * @param {number} radius
+ * @param {number} rgb
+ * @return {string} A data url for a cursor with the provided radius and color.
+ */
+ink.CursorUpdater.prototype.getCursorDataUrlImage = function(radius, rgb) {
+
+ // TODO(esrauch): We avoid initializing with a fixed brush width for normal
+ // rendering ahead of time since the android client has the same brush at a
+ // very large number of different radiuses. Since this is only used for
+ // cursors, we should really just precompute these as images offline and just
+ // splice in the colors.
+ var scratchCanvas =
+ /** @type {!HTMLCanvasElement} */ (document.createElement('canvas'));
+
+ var context =
+ /** @type {!CanvasRenderingContext2D} */ (scratchCanvas.getContext('2d'));
+
+ // Make the cursors opaque.
+ var color = new ink.Color(rgb | 0xFF000000);
+
+ // Cap the minimum radius at 2.
+ radius = Math.max(radius, 2);
+ var diameter = Math.ceil(2 * radius);
+ scratchCanvas.width = diameter;
+ scratchCanvas.height = diameter;
+
+ // If we have a dark color, use a white outline. For a light color, use a
+ // black outline.
+ // Compute the lightness value as defined by HSL.
+ var max = Math.max(color.r, color.g, color.b);
+ var min = Math.min(color.r, color.g, color.b);
+ var lightness = 0.5 * (max + min);
+ var outlineColor = lightness > 127 ? ink.Color.BLACK : ink.Color.WHITE;
+
+ context.fillStyle = outlineColor.getRgbString();
+ context.beginPath();
+ context.arc(radius, radius, radius, 0, 2 * Math.PI);
+ context.closePath();
+ context.fill();
+
+ context.fillStyle = color.getRgbString();
+ context.beginPath();
+ context.arc(radius, radius, radius - 1, 0, 2 * Math.PI);
+ context.closePath();
+ context.fill();
+
+ return scratchCanvas.toDataURL();
+};
diff --git a/chromium/third_party/ink/ink/web/js/embed/embed.js b/chromium/third_party/ink/ink/web/js/embed/embed.js
new file mode 100644
index 00000000000..1c159444c53
--- /dev/null
+++ b/chromium/third_party/ink/ink/web/js/embed/embed.js
@@ -0,0 +1,67 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+goog.provide('ink.embed.Config');
+
+goog.require('goog.ui.Component');
+goog.require('ink.util');
+goog.require('protos.research.ink.InkEvent');
+
+
+
+/**
+ * @constructor
+ * @struct
+ */
+ink.embed.Config = function() {
+ /**
+ * The parent element to render into (required).
+ * @type {?Element}
+ */
+ this.parentEl = null;
+
+ /**
+ * The parent component to set (optional);
+ * TODO(esrauch): This is only necessary because of the cross-package events
+ * that are currently going in both directions. Remove this from the config
+ * after this is cleaned up to avoid Whiteboard events being listened to
+ * directly in Embed code.
+ * @type {?goog.ui.Component}
+ */
+ this.parentComponent = null;
+
+ /**
+ * If true, allows ink to show its own error dialogs for certain cases.
+ * @type {boolean}
+ */
+ this.allowDialogs = false;
+
+ /**
+ * Path to NaCl binary.
+ *
+ * If you are using the Native Client build, you must specify the url for the
+ * Ink Native Client NMF file.
+ *
+ * @type {?string}
+ */
+ this.nativeClientManifestUrl = null;
+
+ /**
+ * The source of the embedder.
+ *
+ * From //logs/proto/research/ink/ink_event.proto
+ *
+ * @type {protos.research.ink.InkEvent.Host}
+ */
+ this.logsHost = protos.research.ink.InkEvent.Host.UNKNOWN_HOST;
+
+ /**
+ * The type of the document the SEngine should be constructed with.
+ *
+ * For Brix documents, this should be PASSTHROUGH_DOCUMENT.
+ *
+ * @type {ink.util.SEngineType}
+ */
+ this.sengineType =
+ ink.util.SEngineType.PASSTHROUGH_DOCUMENT;
+};
diff --git a/chromium/third_party/ink/ink/web/js/embed/embed_component.js b/chromium/third_party/ink/ink/web/js/embed/embed_component.js
new file mode 100644
index 00000000000..60206d96afd
--- /dev/null
+++ b/chromium/third_party/ink/ink/web/js/embed/embed_component.js
@@ -0,0 +1,417 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+/**
+ * @fileoverview The embeddable ink component. Generally should be constructed
+ * by using {@code ink.embed.Config.execute()}.
+ */
+goog.provide('ink.embed.EmbedComponent');
+
+goog.require('goog.dom');
+goog.require('goog.events');
+goog.require('goog.events.Event');
+goog.require('goog.math.Size');
+goog.require('goog.soy');
+goog.require('goog.ui.Component');
+goog.require('ink.CanvasManager');
+goog.require('ink.Color');
+goog.require('ink.CursorUpdater');
+goog.require('ink.embed.Config');
+goog.require('ink.embed.events');
+goog.require('ink.soy.embedContent');
+goog.require('ink.util');
+goog.require('protos.research.ink.InkEvent');
+goog.require('sketchology.proto.SetCallbackFlags');
+
+
+
+/**
+ * @param {!ink.embed.Config} config
+ * @param {!Function} callback
+ * @constructor
+ * @extends {goog.ui.Component}
+ * @struct
+ */
+ink.embed.EmbedComponent = function(config, callback) {
+ ink.embed.EmbedComponent.base(this, 'constructor');
+
+ /** @private {!ink.embed.Config} */
+ this.config_ = config;
+
+ /** @public {boolean} */
+ this.allowDialogs = config.allowDialogs;
+
+ /** @private {!ink.CursorUpdater} */
+ this.cursorUpdater_ = new ink.CursorUpdater();
+ this.addChild(this.cursorUpdater_);
+
+ /** @private {!ink.CanvasManager} */
+ this.canvasManager_ =
+ new ink.CanvasManager(config.nativeClientManifestUrl, config.sengineType);
+ this.addChild(this.canvasManager_);
+
+ /** @private {!Function} */
+ this.callback_ = callback;
+};
+goog.inherits(ink.embed.EmbedComponent, goog.ui.Component);
+
+
+////////////////////////////////////////////////////////////////
+// Public API for embedders.
+////////////////////////////////////////////////////////////////
+
+
+/**
+ * @param {!ink.embed.Config} config
+ * @param {function(ink.embed.EmbedComponent)} callback Callback function that
+ * returns the component that is configured based on the settings in this
+ * Config. This component will raise the relevant ink.embed.Events and provides
+ * an interface to change the brush color, size, etc. Null is returned if the
+ * config is invalid.
+ */
+ink.embed.EmbedComponent.execute = function(config, callback) {
+ var embed = new ink.embed.EmbedComponent(config, callback);
+ embed.setParent(config.parentComponent);
+ embed.render(config.parentEl);
+};
+
+
+/** Removes all elements from the drawing space. */
+ink.embed.EmbedComponent.prototype.clear = function() {
+ var e = new goog.events.Event(ink.embed.events.EventType.CLEAR_REQUESTED);
+ this.dispatchEvent(e);
+ if (!e.defaultPrevented) {
+ this.canvasManager_.clear();
+ }
+};
+
+
+/** Undoes the last modification to the document taken by the user. */
+ink.embed.EmbedComponent.prototype.undo = function() {
+ var e = new goog.events.Event(ink.embed.events.EventType.UNDO_REQUESTED);
+ this.dispatchEvent(e);
+ if (!e.defaultPrevented) {
+ this.canvasManager_.undo();
+ }
+ var eventProto = ink.util.createDocumentEvent(
+ this.getLogsHost(),
+ protos.research.ink.InkEvent.DocumentEvent.DocumentEventType.UNDO);
+ var logEvent = new ink.embed.events.LogEvent(eventProto);
+ this.dispatchEvent(logEvent);
+};
+
+
+/** Redoes the last undone action. */
+ink.embed.EmbedComponent.prototype.redo = function() {
+ var e = new goog.events.Event(ink.embed.events.EventType.REDO_REQUESTED);
+ this.dispatchEvent(e);
+ if (!e.defaultPrevented) {
+ this.canvasManager_.redo();
+ }
+ var eventProto = ink.util.createDocumentEvent(
+ this.getLogsHost(),
+ protos.research.ink.InkEvent.DocumentEvent.DocumentEventType.REDO);
+ var logEvent = new ink.embed.events.LogEvent(eventProto);
+ this.dispatchEvent(logEvent);
+};
+
+
+/**
+ * Adds a background image. Sets page bounds to match the given image.
+ * @param {string} imgSrc
+ * @param {Function=} opt_cb callback on image load complete
+ */
+ink.embed.EmbedComponent.prototype.setBackgroundImage =
+ function(imgSrc, opt_cb) {
+ ink.util.getImageBytes(imgSrc,
+ (data, size) => {
+ this.canvasManager_.setBackgroundImage(data, size);
+ if (opt_cb) opt_cb();
+ });
+};
+
+
+/**
+ * Sets a background image to match the existing page bounds. Will not
+ * display if no page bounds are set.
+ * @param {string} imgSrc
+ * @param {Function=} opt_cb callback on image load complete
+ */
+ink.embed.EmbedComponent.prototype.setImageToUseForPageBackground =
+ function(imgSrc, opt_cb) {
+ ink.util.getImageBytes(imgSrc,
+ (data, size) => {
+ this.canvasManager_.setImageToUseForPageBackground(data, size);
+ if (opt_cb) opt_cb();
+ });
+};
+
+
+/**
+ * Set background color.
+ * @param {ink.Color} color
+ */
+ink.embed.EmbedComponent.prototype.setBackgroundColor = function(color) {
+ this.canvasManager_.setBackgroundColor(color);
+};
+
+
+/**
+ * Export the scene as a PNG from the engine.
+ * @param {number} maxWidth
+ * @param {boolean} drawBackground
+ * @param {function(!goog.html.SafeUrl)} callback
+ * @param {boolean=} opt_asBlob If true, returns a blob uri.
+ */
+ink.embed.EmbedComponent.prototype.exportPng = function(
+ maxWidth, drawBackground, callback, opt_asBlob) {
+ this.canvasManager_.exportPng(maxWidth, drawBackground, callback, opt_asBlob);
+};
+
+
+/**
+ * Set callback flags, for whether to receive callbacks and what data to attach.
+ * @param {!sketchology.proto.SetCallbackFlags} setCallbackFlags
+ */
+ink.embed.EmbedComponent.prototype.setCallbackFlags =
+ function(setCallbackFlags) {
+ this.canvasManager_.setCallbackFlags(setCallbackFlags);
+};
+
+
+/**
+ * Sets the size of the page.
+ * @param {number} left
+ * @param {number} top
+ * @param {number} right
+ * @param {number} bottom
+ */
+ink.embed.EmbedComponent.prototype.setPageBounds =
+ function(left, top, right, bottom) {
+ this.canvasManager_.setPageBounds(left, top, right, bottom);
+};
+
+
+/**
+ * Deselects anything selected with the edit tool.
+ */
+ink.embed.EmbedComponent.prototype.deselectAll = function() {
+ this.canvasManager_.deselectAll();
+};
+
+
+////////////////////////////////////////////////////////////////
+// Internal code.
+////////////////////////////////////////////////////////////////
+
+
+/** @override */
+ink.embed.EmbedComponent.prototype.createDom = function() {
+ this.setElementInternal(goog.soy.renderAsElement(ink.soy.embedContent));
+};
+
+
+/** @override */
+ink.embed.EmbedComponent.prototype.enterDocument = function() {
+ ink.embed.EmbedComponent.base(this, 'enterDocument');
+
+ var container = goog.dom.getElement('layer-container');
+
+ this.canvasManager_.render(container);
+ this.cursorUpdater_.decorate(container);
+
+ this.getHandler().listen(
+ this.canvasManager_, ink.embed.events.EventType.CANVAS_INITIALIZED,
+ goog.bind(this.callback_, this, this));
+};
+
+
+/**
+ * Manually adds an element.
+ * @param {!sketchology.proto.Element} elem
+ * @param {number} idx index to add the element at
+ * @param {boolean} isLocal
+ */
+ink.embed.EmbedComponent.prototype.addElement = function(elem, idx, isLocal) {
+ this.canvasManager_.addElement(elem, idx, isLocal);
+};
+
+
+/**
+ * Manually removes a number of elements.
+ * @param {number} idx index to start removing.
+ * @param {number} count number of items to remove.
+ */
+ink.embed.EmbedComponent.prototype.removeElements = function(idx, count) {
+ this.canvasManager_.removeElements(idx, count);
+};
+
+
+/**
+ * Resets the canvas associated with the embed component.
+ *
+ * Note: Does not affect any attached Brix documents.
+ */
+ink.embed.EmbedComponent.prototype.resetCanvas = function() {
+ this.canvasManager_.resetCanvas();
+};
+
+
+/**
+ * Sets or unsets readOnly on the canvas.
+ * @param {boolean} readOnly
+ */
+ink.embed.EmbedComponent.prototype.setReadOnly = function(readOnly) {
+ this.canvasManager_.setReadOnly(readOnly);
+};
+
+
+/**
+ * Sets element transforms.
+ * @param {Array.<string>} uuids
+ * @param {Array.<string>} encodedTransforms
+ */
+ink.embed.EmbedComponent.prototype.setElementTransforms = function(
+ uuids, encodedTransforms) {
+ this.canvasManager_.setElementTransforms(uuids, encodedTransforms);
+};
+
+
+/**
+ * Returns true if the document is empty, false if it has content, and
+ * undefined if not a brix document.
+ * @param {Function} callback
+ */
+ink.embed.EmbedComponent.prototype.isEmpty = function(callback) {
+ var e = new ink.embed.events.EmptyStatusRequestedEvent(callback);
+ this.dispatchEvent(e);
+ if (!e.defaultPrevented) {
+ callback(undefined);
+ }
+};
+
+
+/**
+ * Returns the current dimensions of the canvas element.
+ * @return {goog.math.Size} The width and height of the canvas.
+ */
+ink.embed.EmbedComponent.prototype.getCanvasDimensions = function() {
+ var element =
+ this.canvasManager_.getElementStrict().querySelector('canvas,embed');
+ return new goog.math.Size(element.clientWidth, element.clientHeight);
+};
+
+
+/**
+ * Returns the logs host id.
+ * @return {protos.research.ink.InkEvent.Host}
+ */
+ink.embed.EmbedComponent.prototype.getLogsHost = function() {
+ return this.config_.logsHost;
+};
+
+
+/**
+ * Enable or disable an engine flag.
+ * @param {sketchology.proto.Flag} which
+ * @param {boolean} enable
+ */
+ink.embed.EmbedComponent.prototype.assignFlag = function(which, enable) {
+ this.canvasManager_.assignFlag(which, enable);
+};
+
+
+/**
+ * Returns the current snapshot. Only works if sengineType is set to
+ * ink.util.SEngineType.IN_MEMORY.
+ * @param {function(!sketchology.proto.Snapshot)} callback
+ */
+ink.embed.EmbedComponent.prototype.getSnapshot = function(callback) {
+ if (this.config_.sengineType !== ink.util.SEngineType.IN_MEMORY) {
+ throw new Error(`Can't getSnapshot without sengineType IN_MEMORY.`);
+ }
+ this.canvasManager_.getSnapshot(callback);
+};
+
+
+/**
+ * Loads a document from a snapshot. Only works if sengineType is set to
+ * ink.util.SEngineType.IN_MEMORY.
+ *
+ * @param {!sketchology.proto.Snapshot} snapshotProto
+ */
+ink.embed.EmbedComponent.prototype.loadFromSnapshot = function(snapshotProto) {
+ if (this.config_.sengineType !== ink.util.SEngineType.IN_MEMORY) {
+ throw new Error(`Can't loadFromSnapshot without sengineType IN_MEMORY.`);
+ }
+ this.canvasManager_.loadFromSnapshot(snapshotProto);
+};
+
+
+/**
+ * Allows the user to execute arbitrary commands on the engine.
+ * @param {!sketchology.proto.Command} command
+ */
+ink.embed.EmbedComponent.prototype.handleCommand = function(command) {
+ this.canvasManager_.handleCommand(command);
+};
+
+
+/**
+ * Gets the raw engine object. Do not use this.
+ * @return {Object}
+ */
+ink.embed.EmbedComponent.prototype.getRawEngineObject = function() {
+ return this.canvasManager_.getRawEngineObject();
+};
+
+
+/**
+ * Generates a snapshot based on a brix document.
+ * @param {!ink.util.RealtimeDocument} brixDoc
+ * @param {function(!sketchology.proto.Snapshot)} callback
+ */
+ink.embed.EmbedComponent.prototype.convertBrixDocumentToSnapshot =
+ function(brixDoc, callback) {
+ this.canvasManager_.convertBrixDocumentToSnapshot(brixDoc, callback);
+};
+
+
+/**
+ * @param {!sketchology.proto.Snapshot} snapshot
+ * @param {function(boolean)} callback
+ */
+ink.embed.EmbedComponent.prototype.snapshotHasPendingMutations =
+ function(snapshot, callback) {
+ this.canvasManager_.snapshotHasPendingMutations(snapshot, callback);
+};
+
+
+/**
+ * @param {!sketchology.proto.Snapshot} snapshot
+ * @param {function(sketchology.proto.MutationPacket)} callback
+ */
+ink.embed.EmbedComponent.prototype.extractMutationPacket =
+ function(snapshot, callback) {
+ this.canvasManager_.extractMutationPacket(snapshot, callback);
+};
+
+
+/**
+ * @param {!sketchology.proto.Snapshot} snapshot
+ * @param {function(sketchology.proto.Snapshot)} callback
+ */
+ink.embed.EmbedComponent.prototype.clearPendingMutations =
+ function(snapshot, callback) {
+ this.canvasManager_.clearPendingMutations(snapshot, callback);
+};
+
+
+/**
+ * Calls the given callback once all previous asynchronous engine operations
+ * have been applied.
+ * @param {!Function} callback
+ */
+ink.embed.EmbedComponent.prototype.flush = function(callback) {
+ this.canvasManager_.flush(callback);
+};
diff --git a/chromium/third_party/ink/ink/web/js/embed/events.js b/chromium/third_party/ink/ink/web/js/embed/events.js
new file mode 100644
index 00000000000..2997c5ee056
--- /dev/null
+++ b/chromium/third_party/ink/ink/web/js/embed/events.js
@@ -0,0 +1,225 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+/**
+ * @fileoverview Events that embedders can listen to. Note: currently embedders
+ * are able to listen to other internal events, but only these events should be
+ * treated as a public API.
+ */
+goog.provide('ink.embed.events');
+goog.provide('ink.embed.events.DoneLoadingEvent');
+goog.provide('ink.embed.events.ElementCreatedEvent');
+goog.provide('ink.embed.events.ElementsMutatedEvent');
+goog.provide('ink.embed.events.ElementsRemovedEvent');
+goog.provide('ink.embed.events.EmptyStatusRequestedEvent');
+goog.provide('ink.embed.events.EventType');
+goog.provide('ink.embed.events.FatalErrorEvent');
+goog.provide('ink.embed.events.LogEvent');
+
+goog.require('goog.events');
+goog.require('protos.research.ink.InkEvent');
+
+
+/** @enum {string} */
+ink.embed.events.EventType = {
+ // Dispatched when the space is ready to be drawn onto.
+ DONE_LOADING: goog.events.getUniqueId('done_loading'),
+
+ // Dispatched when pen mode is enabled.
+ PEN_MODE_ENABLED: goog.events.getUniqueId('pen_mode_enabled'),
+
+ // Dispatched when the GL canvas is initialized.
+ CANVAS_INITIALIZED: goog.events.getUniqueId('canvas_initialized'),
+
+ // Dispatched when a new element has been created on the canvas.
+ ELEMENT_CREATED: goog.events.getUniqueId('element_created'),
+
+ // Dispatched when an element is mutaed.
+ ELEMENTS_MUTATED: goog.events.getUniqueId('elements_mutated'),
+
+ // Dispatched when elements are removed.
+ ELEMENTS_REMOVED: goog.events.getUniqueId('elements_removed'),
+
+ // Dispatched when something wants to know if the document is empty.
+ EMPTY_STATUS_REQUESTED: goog.events.getUniqueId('empty_status_requested'),
+
+ // Dispatched when something needs to be logged.
+ LOG: goog.events.getUniqueId('log'),
+
+ // Dispatched to request an undo.
+ UNDO_REQUESTED: goog.events.getUniqueId('undo_requested'),
+
+ // Dispatched to request a redo.
+ REDO_REQUESTED: goog.events.getUniqueId('redo_requested'),
+
+ // Dispatched to request a clear.
+ CLEAR_REQUESTED: goog.events.getUniqueId('clear_requested'),
+
+ // Dispatched when a fatal error occurs and the canvas is no longer valid.
+ // If this is not handled, an Error is thrown (or re-raised). The canvas
+ // should be discarded and a new one constructed.
+ FATAL_ERROR: goog.events.getUniqueId('fatal_error')
+};
+
+
+
+/**
+ * An event fired when the embed is ready to be drawn onto.
+ *
+ * @param {Object} brixDoc The brix realtime document associated with
+ * the document that has been loaded.
+ * @param {boolean} isReadOnly Whether the document is read only.
+ *
+ * @extends {goog.events.Event}
+ * @constructor
+ * @struct
+ */
+ink.embed.events.DoneLoadingEvent = function(brixDoc, isReadOnly) {
+ ink.embed.events.DoneLoadingEvent.base(
+ this, 'constructor', ink.embed.events.EventType.DONE_LOADING);
+
+ /** @type {Object} */
+ this.brixDoc = brixDoc;
+
+ /** @type {boolean} */
+ this.isReadOnly = isReadOnly;
+};
+goog.inherits(ink.embed.events.DoneLoadingEvent, goog.events.Event);
+
+
+/**
+ * An event fired when pen mode is enabled or disabled.
+ *
+ * @param {boolean} enabled
+ *
+ * @extends {goog.events.Event}
+ * @constructor
+ * @struct
+ */
+ink.embed.events.PenModeEnabled = function(enabled) {
+ ink.embed.events.PenModeEnabled.base(this, 'constructor',
+ ink.embed.events.EventType.PEN_MODE_ENABLED);
+
+ /** @type {boolean} */
+ this.enabled = enabled;
+};
+goog.inherits(ink.embed.events.PenModeEnabled, goog.events.Event);
+
+
+/**
+ * An event fired when a new element is created in the embed.
+ *
+ * @param {string} uuid
+ * @param {string} encodedElement
+ * @param {string} encodedTransform
+ *
+ * @extends {goog.events.Event}
+ * @constructor
+ * @struct
+ */
+ink.embed.events.ElementCreatedEvent = function(uuid, encodedElement,
+ encodedTransform) {
+ ink.embed.events.ElementCreatedEvent.base(
+ this, 'constructor', ink.embed.events.EventType.ELEMENT_CREATED);
+
+ this.uuid = uuid;
+ this.encodedElement = encodedElement;
+ this.encodedTransform = encodedTransform;
+};
+goog.inherits(ink.embed.events.ElementCreatedEvent, goog.events.Event);
+
+
+/**
+ * An event fired when an element is mutated.
+ *
+ * @param {Array.<string>} uuids
+ * @param {Array.<string>} encodedTransforms
+ *
+ * @extends {goog.events.Event}
+ * @constructor
+ * @struct
+ */
+ink.embed.events.ElementsMutatedEvent = function(uuids, encodedTransforms) {
+ ink.embed.events.ElementsMutatedEvent.base(
+ this, 'constructor', ink.embed.events.EventType.ELEMENTS_MUTATED);
+
+ this.uuids = uuids;
+ this.encodedTransforms = encodedTransforms;
+};
+goog.inherits(ink.embed.events.ElementsMutatedEvent, goog.events.Event);
+
+
+/**
+ * An event fired when elements are removed.
+ *
+ * @param {Array.<string>} uuids
+ *
+ * @extends {goog.events.Event}
+ * @constructor
+ * @struct
+ */
+ink.embed.events.ElementsRemovedEvent = function(uuids) {
+ ink.embed.events.ElementsRemovedEvent.base(
+ this, 'constructor', ink.embed.events.EventType.ELEMENTS_REMOVED);
+
+ this.uuids = uuids;
+};
+goog.inherits(ink.embed.events.ElementsRemovedEvent, goog.events.Event);
+
+
+/**
+ * An event fired when something wants to know if the document is empty.
+ *
+ * @param {Function} callback
+ *
+ * @extends {goog.events.Event}
+ * @constructor
+ * @struct
+ */
+ink.embed.events.EmptyStatusRequestedEvent = function(callback) {
+ ink.embed.events.EmptyStatusRequestedEvent.base(
+ this, 'constructor', ink.embed.events.EventType.EMPTY_STATUS_REQUESTED);
+
+ this.callback = callback;
+};
+goog.inherits(ink.embed.events.EmptyStatusRequestedEvent, goog.events.Event);
+
+
+/**
+ * An event fired when an event should be logged by the embedder.
+ *
+ * @param {protos.research.ink.InkEvent} proto The ink event proto.
+ *
+ * @extends {goog.events.Event}
+ * @constructor
+ * @struct
+ */
+ink.embed.events.LogEvent = function(proto) {
+ ink.embed.events.LogEvent.base(
+ this, 'constructor', ink.embed.events.EventType.LOG);
+
+ this.proto = proto;
+};
+goog.inherits(ink.embed.events.LogEvent, goog.events.Event);
+
+
+/**
+ * An event fired when a fatal error has occured.
+ *
+ * If this error is not handled by the embedder, the component will throw an
+ * error. The embedder should discard this component and construct a new one.
+ *
+ * @param {Error=} opt_cause Optional cause of the fatal error
+ *
+ * @extends {goog.events.Event}
+ * @constructor
+ * @struct
+ */
+ink.embed.events.FatalErrorEvent = function(opt_cause) {
+ ink.embed.events.FatalErrorEvent.base(
+ this, 'constructor', ink.embed.events.EventType.FATAL_ERROR);
+
+ /** @type {Error} */
+ this.cause = opt_cause || null;
+};
+goog.inherits(ink.embed.events.FatalErrorEvent, goog.events.Event);
diff --git a/chromium/third_party/ink/ink/web/js/main.soy.js b/chromium/third_party/ink/ink/web/js/main.soy.js
new file mode 100644
index 00000000000..3883d56def6
--- /dev/null
+++ b/chromium/third_party/ink/ink/web/js/main.soy.js
@@ -0,0 +1,31 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+// This file was automatically generated from main.soy.
+// Please don't edit this file by hand.
+
+/**
+ * @fileoverview Templates in namespace ink.soy.
+ * @public
+ */
+
+goog.provide('ink.soy.embedContent');
+
+goog.require('soy');
+goog.require('soydata.VERY_UNSAFE');
+
+
+/**
+ * @param {Object<string, *>=} opt_data
+ * @param {Object<string, *>=} opt_ijData
+ * @param {Object<string, *>=} opt_ijData_deprecated
+ * @return {!goog.soy.data.SanitizedHtml}
+ * @suppress {checkTypes}
+ */
+ink.soy.embedContent = function(opt_data, opt_ijData, opt_ijData_deprecated) {
+ opt_ijData = opt_ijData_deprecated || opt_ijData;
+ return soydata.VERY_UNSAFE.ordainSanitizedHtml(((goog.DEBUG && soy.$$debugSoyTemplateInfo) ? '<!--dta_of(ink.soy.embedContent, research/ink/web/js/main.soy, 7)-->' : '') + '<div id="canvas-parent"><style' + (opt_ijData && opt_ijData.csp_nonce ? ' nonce="' + soy.$$escapeHtmlAttribute(opt_ijData && opt_ijData.csp_nonce) + '"' : '') + '>\n #canvas-parent {\n height: 100%;\n position: relative;\n width: 100%;\n }\n #layer-container {\n height: 100%;\n position: relative;\n width: 100%;\n }\n #ink-engine {\n height: 100%;\n left: 0;\n position: absolute;\n top: 0;\n width: 100%;\n touch-action: none;\n }\n .above-ink-canvas {\n display: none;\n }\n </style><div class="above-ink-canvas"></div><div id="layer-container"></div><div class="below-ink-canvas"></div></div>' + ((goog.DEBUG && soy.$$debugSoyTemplateInfo) ? '<!--dta_cf(ink.soy.embedContent)-->' : ''));
+};
+if (goog.DEBUG) {
+ ink.soy.embedContent.soyTemplateName = 'ink.soy.embedContent';
+}
diff --git a/chromium/third_party/ink/ink_event.pb.js b/chromium/third_party/ink/ink_event.pb.js
new file mode 100644
index 00000000000..26cd567fe25
--- /dev/null
+++ b/chromium/third_party/ink/ink_event.pb.js
@@ -0,0 +1,2654 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+// Protocol Buffer 2 Copyright 2008 Google Inc.
+// All other code copyright its respective owners.
+
+/**
+ * @fileoverview Generated Protocol Buffer code for file
+ * logs/proto/research/ink/ink_event.proto.
+ * Generated by //net/proto2/compiler/public:protocol_compiler.
+ * @suppress {messageConventions}
+ */
+
+goog.provide('protos.research.ink.InkEvent');
+goog.provide('protos.research.ink.InkEvent.DocumentEvent');
+goog.provide('protos.research.ink.InkEvent.DocumentEvent.OpenedEvent');
+goog.provide('protos.research.ink.InkEvent.DocumentEvent.OpenCancelledEvent');
+goog.provide('protos.research.ink.InkEvent.DocumentEvent.CollaboratorJoined');
+goog.provide('protos.research.ink.InkEvent.DocumentEvent.DocumentState');
+goog.provide('protos.research.ink.InkEvent.DocumentEvent.DocumentState.TextField');
+goog.provide('protos.research.ink.InkEvent.DocumentEvent.DocumentEventType');
+goog.provide('protos.research.ink.InkEvent.ToolbarEvent');
+goog.provide('protos.research.ink.InkEvent.ToolbarEvent.ToolEventType');
+goog.provide('protos.research.ink.InkEvent.ToolbarEvent.ToolType');
+goog.provide('protos.research.ink.InkEvent.ToolbarEvent.ExpandMethod');
+goog.provide('protos.research.ink.InkEvent.EngineEvent');
+goog.provide('protos.research.ink.InkEvent.EngineEvent.EngineEventType');
+goog.provide('protos.research.ink.InkEvent.GmsEvent');
+goog.provide('protos.research.ink.InkEvent.GmsEvent.GmsEventType');
+goog.provide('protos.research.ink.InkEvent.Host');
+goog.provide('protos.research.ink.InkEvent.EventType');
+
+goog.require('goog.proto2.Message');
+
+
+
+/**
+ * Message InkEvent.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+protos.research.ink.InkEvent = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(protos.research.ink.InkEvent, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+protos.research.ink.InkEvent.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!protos.research.ink.InkEvent} The cloned message.
+ * @override
+ */
+protos.research.ink.InkEvent.prototype.clone;
+
+
+/**
+ * Gets the value of the host field.
+ * @return {?protos.research.ink.InkEvent.Host} The value.
+ */
+protos.research.ink.InkEvent.prototype.getHost = function() {
+ return /** @type {?protos.research.ink.InkEvent.Host} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the host field or the default value if not set.
+ * @return {!protos.research.ink.InkEvent.Host} The value.
+ */
+protos.research.ink.InkEvent.prototype.getHostOrDefault = function() {
+ return /** @type {!protos.research.ink.InkEvent.Host} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the host field.
+ * @param {!protos.research.ink.InkEvent.Host} value The value.
+ */
+protos.research.ink.InkEvent.prototype.setHost = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the host field has a value.
+ */
+protos.research.ink.InkEvent.prototype.hasHost = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the host field.
+ */
+protos.research.ink.InkEvent.prototype.hostCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the host field.
+ */
+protos.research.ink.InkEvent.prototype.clearHost = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the event_type field.
+ * @return {?protos.research.ink.InkEvent.EventType} The value.
+ */
+protos.research.ink.InkEvent.prototype.getEventType = function() {
+ return /** @type {?protos.research.ink.InkEvent.EventType} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the event_type field or the default value if not set.
+ * @return {!protos.research.ink.InkEvent.EventType} The value.
+ */
+protos.research.ink.InkEvent.prototype.getEventTypeOrDefault = function() {
+ return /** @type {!protos.research.ink.InkEvent.EventType} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the event_type field.
+ * @param {!protos.research.ink.InkEvent.EventType} value The value.
+ */
+protos.research.ink.InkEvent.prototype.setEventType = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the event_type field has a value.
+ */
+protos.research.ink.InkEvent.prototype.hasEventType = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the event_type field.
+ */
+protos.research.ink.InkEvent.prototype.eventTypeCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the event_type field.
+ */
+protos.research.ink.InkEvent.prototype.clearEventType = function() {
+ this.clear$Field(2);
+};
+
+
+/**
+ * Gets the value of the document_event field.
+ * @return {?protos.research.ink.InkEvent.DocumentEvent} The value.
+ */
+protos.research.ink.InkEvent.prototype.getDocumentEvent = function() {
+ return /** @type {?protos.research.ink.InkEvent.DocumentEvent} */ (this.get$Value(3));
+};
+
+
+/**
+ * Gets the value of the document_event field or the default value if not set.
+ * @return {!protos.research.ink.InkEvent.DocumentEvent} The value.
+ */
+protos.research.ink.InkEvent.prototype.getDocumentEventOrDefault = function() {
+ return /** @type {!protos.research.ink.InkEvent.DocumentEvent} */ (this.get$ValueOrDefault(3));
+};
+
+
+/**
+ * Sets the value of the document_event field.
+ * @param {!protos.research.ink.InkEvent.DocumentEvent} value The value.
+ */
+protos.research.ink.InkEvent.prototype.setDocumentEvent = function(value) {
+ this.set$Value(3, value);
+};
+
+
+/**
+ * @return {boolean} Whether the document_event field has a value.
+ */
+protos.research.ink.InkEvent.prototype.hasDocumentEvent = function() {
+ return this.has$Value(3);
+};
+
+
+/**
+ * @return {number} The number of values in the document_event field.
+ */
+protos.research.ink.InkEvent.prototype.documentEventCount = function() {
+ return this.count$Values(3);
+};
+
+
+/**
+ * Clears the values in the document_event field.
+ */
+protos.research.ink.InkEvent.prototype.clearDocumentEvent = function() {
+ this.clear$Field(3);
+};
+
+
+/**
+ * Gets the value of the toolbar_event field.
+ * @return {?protos.research.ink.InkEvent.ToolbarEvent} The value.
+ */
+protos.research.ink.InkEvent.prototype.getToolbarEvent = function() {
+ return /** @type {?protos.research.ink.InkEvent.ToolbarEvent} */ (this.get$Value(4));
+};
+
+
+/**
+ * Gets the value of the toolbar_event field or the default value if not set.
+ * @return {!protos.research.ink.InkEvent.ToolbarEvent} The value.
+ */
+protos.research.ink.InkEvent.prototype.getToolbarEventOrDefault = function() {
+ return /** @type {!protos.research.ink.InkEvent.ToolbarEvent} */ (this.get$ValueOrDefault(4));
+};
+
+
+/**
+ * Sets the value of the toolbar_event field.
+ * @param {!protos.research.ink.InkEvent.ToolbarEvent} value The value.
+ */
+protos.research.ink.InkEvent.prototype.setToolbarEvent = function(value) {
+ this.set$Value(4, value);
+};
+
+
+/**
+ * @return {boolean} Whether the toolbar_event field has a value.
+ */
+protos.research.ink.InkEvent.prototype.hasToolbarEvent = function() {
+ return this.has$Value(4);
+};
+
+
+/**
+ * @return {number} The number of values in the toolbar_event field.
+ */
+protos.research.ink.InkEvent.prototype.toolbarEventCount = function() {
+ return this.count$Values(4);
+};
+
+
+/**
+ * Clears the values in the toolbar_event field.
+ */
+protos.research.ink.InkEvent.prototype.clearToolbarEvent = function() {
+ this.clear$Field(4);
+};
+
+
+/**
+ * Gets the value of the engine_event field.
+ * @return {?protos.research.ink.InkEvent.EngineEvent} The value.
+ */
+protos.research.ink.InkEvent.prototype.getEngineEvent = function() {
+ return /** @type {?protos.research.ink.InkEvent.EngineEvent} */ (this.get$Value(5));
+};
+
+
+/**
+ * Gets the value of the engine_event field or the default value if not set.
+ * @return {!protos.research.ink.InkEvent.EngineEvent} The value.
+ */
+protos.research.ink.InkEvent.prototype.getEngineEventOrDefault = function() {
+ return /** @type {!protos.research.ink.InkEvent.EngineEvent} */ (this.get$ValueOrDefault(5));
+};
+
+
+/**
+ * Sets the value of the engine_event field.
+ * @param {!protos.research.ink.InkEvent.EngineEvent} value The value.
+ */
+protos.research.ink.InkEvent.prototype.setEngineEvent = function(value) {
+ this.set$Value(5, value);
+};
+
+
+/**
+ * @return {boolean} Whether the engine_event field has a value.
+ */
+protos.research.ink.InkEvent.prototype.hasEngineEvent = function() {
+ return this.has$Value(5);
+};
+
+
+/**
+ * @return {number} The number of values in the engine_event field.
+ */
+protos.research.ink.InkEvent.prototype.engineEventCount = function() {
+ return this.count$Values(5);
+};
+
+
+/**
+ * Clears the values in the engine_event field.
+ */
+protos.research.ink.InkEvent.prototype.clearEngineEvent = function() {
+ this.clear$Field(5);
+};
+
+
+/**
+ * Gets the value of the gms_event field.
+ * @return {?protos.research.ink.InkEvent.GmsEvent} The value.
+ */
+protos.research.ink.InkEvent.prototype.getGmsEvent = function() {
+ return /** @type {?protos.research.ink.InkEvent.GmsEvent} */ (this.get$Value(6));
+};
+
+
+/**
+ * Gets the value of the gms_event field or the default value if not set.
+ * @return {!protos.research.ink.InkEvent.GmsEvent} The value.
+ */
+protos.research.ink.InkEvent.prototype.getGmsEventOrDefault = function() {
+ return /** @type {!protos.research.ink.InkEvent.GmsEvent} */ (this.get$ValueOrDefault(6));
+};
+
+
+/**
+ * Sets the value of the gms_event field.
+ * @param {!protos.research.ink.InkEvent.GmsEvent} value The value.
+ */
+protos.research.ink.InkEvent.prototype.setGmsEvent = function(value) {
+ this.set$Value(6, value);
+};
+
+
+/**
+ * @return {boolean} Whether the gms_event field has a value.
+ */
+protos.research.ink.InkEvent.prototype.hasGmsEvent = function() {
+ return this.has$Value(6);
+};
+
+
+/**
+ * @return {number} The number of values in the gms_event field.
+ */
+protos.research.ink.InkEvent.prototype.gmsEventCount = function() {
+ return this.count$Values(6);
+};
+
+
+/**
+ * Clears the values in the gms_event field.
+ */
+protos.research.ink.InkEvent.prototype.clearGmsEvent = function() {
+ this.clear$Field(6);
+};
+
+
+/**
+ * Enumeration Host.
+ * @enum {number}
+ */
+protos.research.ink.InkEvent.Host = {
+ UNKNOWN_HOST: 0,
+ FISHFOOD: 1,
+ KEEP: 2,
+ CLASSROOM: 3,
+ FIREBALL: 4
+};
+
+
+/**
+ * Enumeration EventType.
+ * @enum {number}
+ */
+protos.research.ink.InkEvent.EventType = {
+ UNKNOWN_TYPE: 0,
+ DOCUMENT_EVENT: 1,
+ TOOLBAR_EVENT: 2,
+ ENGINE_EVENT: 3,
+ GMS_EVENT: 4
+};
+
+
+
+/**
+ * Message DocumentEvent.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+protos.research.ink.InkEvent.DocumentEvent = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(protos.research.ink.InkEvent.DocumentEvent, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+protos.research.ink.InkEvent.DocumentEvent.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!protos.research.ink.InkEvent.DocumentEvent} The cloned message.
+ * @override
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.clone;
+
+
+/**
+ * Gets the value of the event_type field.
+ * @return {?protos.research.ink.InkEvent.DocumentEvent.DocumentEventType} The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.getEventType = function() {
+ return /** @type {?protos.research.ink.InkEvent.DocumentEvent.DocumentEventType} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the event_type field or the default value if not set.
+ * @return {!protos.research.ink.InkEvent.DocumentEvent.DocumentEventType} The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.getEventTypeOrDefault = function() {
+ return /** @type {!protos.research.ink.InkEvent.DocumentEvent.DocumentEventType} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the event_type field.
+ * @param {!protos.research.ink.InkEvent.DocumentEvent.DocumentEventType} value The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.setEventType = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the event_type field has a value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.hasEventType = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the event_type field.
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.eventTypeCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the event_type field.
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.clearEventType = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the opened_event field.
+ * @return {?protos.research.ink.InkEvent.DocumentEvent.OpenedEvent} The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.getOpenedEvent = function() {
+ return /** @type {?protos.research.ink.InkEvent.DocumentEvent.OpenedEvent} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the opened_event field or the default value if not set.
+ * @return {!protos.research.ink.InkEvent.DocumentEvent.OpenedEvent} The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.getOpenedEventOrDefault = function() {
+ return /** @type {!protos.research.ink.InkEvent.DocumentEvent.OpenedEvent} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the opened_event field.
+ * @param {!protos.research.ink.InkEvent.DocumentEvent.OpenedEvent} value The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.setOpenedEvent = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the opened_event field has a value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.hasOpenedEvent = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the opened_event field.
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.openedEventCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the opened_event field.
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.clearOpenedEvent = function() {
+ this.clear$Field(2);
+};
+
+
+/**
+ * Gets the value of the open_cancelled_event field.
+ * @return {?protos.research.ink.InkEvent.DocumentEvent.OpenCancelledEvent} The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.getOpenCancelledEvent = function() {
+ return /** @type {?protos.research.ink.InkEvent.DocumentEvent.OpenCancelledEvent} */ (this.get$Value(3));
+};
+
+
+/**
+ * Gets the value of the open_cancelled_event field or the default value if not set.
+ * @return {!protos.research.ink.InkEvent.DocumentEvent.OpenCancelledEvent} The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.getOpenCancelledEventOrDefault = function() {
+ return /** @type {!protos.research.ink.InkEvent.DocumentEvent.OpenCancelledEvent} */ (this.get$ValueOrDefault(3));
+};
+
+
+/**
+ * Sets the value of the open_cancelled_event field.
+ * @param {!protos.research.ink.InkEvent.DocumentEvent.OpenCancelledEvent} value The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.setOpenCancelledEvent = function(value) {
+ this.set$Value(3, value);
+};
+
+
+/**
+ * @return {boolean} Whether the open_cancelled_event field has a value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.hasOpenCancelledEvent = function() {
+ return this.has$Value(3);
+};
+
+
+/**
+ * @return {number} The number of values in the open_cancelled_event field.
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.openCancelledEventCount = function() {
+ return this.count$Values(3);
+};
+
+
+/**
+ * Clears the values in the open_cancelled_event field.
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.clearOpenCancelledEvent = function() {
+ this.clear$Field(3);
+};
+
+
+/**
+ * Gets the value of the error_code field.
+ * @return {?string} The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.getErrorCode = function() {
+ return /** @type {?string} */ (this.get$Value(4));
+};
+
+
+/**
+ * Gets the value of the error_code field or the default value if not set.
+ * @return {string} The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.getErrorCodeOrDefault = function() {
+ return /** @type {string} */ (this.get$ValueOrDefault(4));
+};
+
+
+/**
+ * Sets the value of the error_code field.
+ * @param {string} value The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.setErrorCode = function(value) {
+ this.set$Value(4, value);
+};
+
+
+/**
+ * @return {boolean} Whether the error_code field has a value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.hasErrorCode = function() {
+ return this.has$Value(4);
+};
+
+
+/**
+ * @return {number} The number of values in the error_code field.
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.errorCodeCount = function() {
+ return this.count$Values(4);
+};
+
+
+/**
+ * Clears the values in the error_code field.
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.clearErrorCode = function() {
+ this.clear$Field(4);
+};
+
+
+/**
+ * Gets the value of the brix_error_code field.
+ * @return {?string} The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.getBrixErrorCode = function() {
+ return /** @type {?string} */ (this.get$Value(5));
+};
+
+
+/**
+ * Gets the value of the brix_error_code field or the default value if not set.
+ * @return {string} The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.getBrixErrorCodeOrDefault = function() {
+ return /** @type {string} */ (this.get$ValueOrDefault(5));
+};
+
+
+/**
+ * Sets the value of the brix_error_code field.
+ * @param {string} value The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.setBrixErrorCode = function(value) {
+ this.set$Value(5, value);
+};
+
+
+/**
+ * @return {boolean} Whether the brix_error_code field has a value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.hasBrixErrorCode = function() {
+ return this.has$Value(5);
+};
+
+
+/**
+ * @return {number} The number of values in the brix_error_code field.
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.brixErrorCodeCount = function() {
+ return this.count$Values(5);
+};
+
+
+/**
+ * Clears the values in the brix_error_code field.
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.clearBrixErrorCode = function() {
+ this.clear$Field(5);
+};
+
+
+/**
+ * Gets the value of the collaborator_joined_event field.
+ * @return {?protos.research.ink.InkEvent.DocumentEvent.CollaboratorJoined} The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.getCollaboratorJoinedEvent = function() {
+ return /** @type {?protos.research.ink.InkEvent.DocumentEvent.CollaboratorJoined} */ (this.get$Value(6));
+};
+
+
+/**
+ * Gets the value of the collaborator_joined_event field or the default value if not set.
+ * @return {!protos.research.ink.InkEvent.DocumentEvent.CollaboratorJoined} The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.getCollaboratorJoinedEventOrDefault = function() {
+ return /** @type {!protos.research.ink.InkEvent.DocumentEvent.CollaboratorJoined} */ (this.get$ValueOrDefault(6));
+};
+
+
+/**
+ * Sets the value of the collaborator_joined_event field.
+ * @param {!protos.research.ink.InkEvent.DocumentEvent.CollaboratorJoined} value The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.setCollaboratorJoinedEvent = function(value) {
+ this.set$Value(6, value);
+};
+
+
+/**
+ * @return {boolean} Whether the collaborator_joined_event field has a value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.hasCollaboratorJoinedEvent = function() {
+ return this.has$Value(6);
+};
+
+
+/**
+ * @return {number} The number of values in the collaborator_joined_event field.
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.collaboratorJoinedEventCount = function() {
+ return this.count$Values(6);
+};
+
+
+/**
+ * Clears the values in the collaborator_joined_event field.
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.clearCollaboratorJoinedEvent = function() {
+ this.clear$Field(6);
+};
+
+
+/**
+ * Gets the value of the document_state field.
+ * @return {?protos.research.ink.InkEvent.DocumentEvent.DocumentState} The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.getDocumentState = function() {
+ return /** @type {?protos.research.ink.InkEvent.DocumentEvent.DocumentState} */ (this.get$Value(7));
+};
+
+
+/**
+ * Gets the value of the document_state field or the default value if not set.
+ * @return {!protos.research.ink.InkEvent.DocumentEvent.DocumentState} The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.getDocumentStateOrDefault = function() {
+ return /** @type {!protos.research.ink.InkEvent.DocumentEvent.DocumentState} */ (this.get$ValueOrDefault(7));
+};
+
+
+/**
+ * Sets the value of the document_state field.
+ * @param {!protos.research.ink.InkEvent.DocumentEvent.DocumentState} value The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.setDocumentState = function(value) {
+ this.set$Value(7, value);
+};
+
+
+/**
+ * @return {boolean} Whether the document_state field has a value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.hasDocumentState = function() {
+ return this.has$Value(7);
+};
+
+
+/**
+ * @return {number} The number of values in the document_state field.
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.documentStateCount = function() {
+ return this.count$Values(7);
+};
+
+
+/**
+ * Clears the values in the document_state field.
+ */
+protos.research.ink.InkEvent.DocumentEvent.prototype.clearDocumentState = function() {
+ this.clear$Field(7);
+};
+
+
+/**
+ * Enumeration DocumentEventType.
+ * @enum {number}
+ */
+protos.research.ink.InkEvent.DocumentEvent.DocumentEventType = {
+ UNKNOWN_DOCUMENT_EVENT: 0,
+ CREATED: 1,
+ OPENED: 2,
+ OPEN_FAILED: 3,
+ KICKED_USER_OUT: 4,
+ OPEN_CANCELLED: 5,
+ BRIX_DOCUMENT_CONNECT: 6,
+ UNDO: 7,
+ REDO: 8,
+ COLLABORATOR_JOINED: 9,
+ SEND: 10,
+ ABANDON: 11,
+ EXTERNAL_SHARE: 12
+};
+
+
+
+/**
+ * Message OpenedEvent.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+protos.research.ink.InkEvent.DocumentEvent.OpenedEvent = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(protos.research.ink.InkEvent.DocumentEvent.OpenedEvent, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+protos.research.ink.InkEvent.DocumentEvent.OpenedEvent.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!protos.research.ink.InkEvent.DocumentEvent.OpenedEvent} The cloned message.
+ * @override
+ */
+protos.research.ink.InkEvent.DocumentEvent.OpenedEvent.prototype.clone;
+
+
+/**
+ * Gets the value of the millis_until_first_byte_loaded field.
+ * @return {?string} The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.OpenedEvent.prototype.getMillisUntilFirstByteLoaded = function() {
+ return /** @type {?string} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the millis_until_first_byte_loaded field or the default value if not set.
+ * @return {string} The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.OpenedEvent.prototype.getMillisUntilFirstByteLoadedOrDefault = function() {
+ return /** @type {string} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the millis_until_first_byte_loaded field.
+ * @param {string} value The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.OpenedEvent.prototype.setMillisUntilFirstByteLoaded = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the millis_until_first_byte_loaded field has a value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.OpenedEvent.prototype.hasMillisUntilFirstByteLoaded = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the millis_until_first_byte_loaded field.
+ */
+protos.research.ink.InkEvent.DocumentEvent.OpenedEvent.prototype.millisUntilFirstByteLoadedCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the millis_until_first_byte_loaded field.
+ */
+protos.research.ink.InkEvent.DocumentEvent.OpenedEvent.prototype.clearMillisUntilFirstByteLoaded = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the millis_until_editable field.
+ * @return {?string} The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.OpenedEvent.prototype.getMillisUntilEditable = function() {
+ return /** @type {?string} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the millis_until_editable field or the default value if not set.
+ * @return {string} The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.OpenedEvent.prototype.getMillisUntilEditableOrDefault = function() {
+ return /** @type {string} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the millis_until_editable field.
+ * @param {string} value The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.OpenedEvent.prototype.setMillisUntilEditable = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the millis_until_editable field has a value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.OpenedEvent.prototype.hasMillisUntilEditable = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the millis_until_editable field.
+ */
+protos.research.ink.InkEvent.DocumentEvent.OpenedEvent.prototype.millisUntilEditableCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the millis_until_editable field.
+ */
+protos.research.ink.InkEvent.DocumentEvent.OpenedEvent.prototype.clearMillisUntilEditable = function() {
+ this.clear$Field(2);
+};
+
+
+/**
+ * Gets the value of the missing_document_bounds field.
+ * @return {?boolean} The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.OpenedEvent.prototype.getMissingDocumentBounds = function() {
+ return /** @type {?boolean} */ (this.get$Value(3));
+};
+
+
+/**
+ * Gets the value of the missing_document_bounds field or the default value if not set.
+ * @return {boolean} The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.OpenedEvent.prototype.getMissingDocumentBoundsOrDefault = function() {
+ return /** @type {boolean} */ (this.get$ValueOrDefault(3));
+};
+
+
+/**
+ * Sets the value of the missing_document_bounds field.
+ * @param {boolean} value The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.OpenedEvent.prototype.setMissingDocumentBounds = function(value) {
+ this.set$Value(3, value);
+};
+
+
+/**
+ * @return {boolean} Whether the missing_document_bounds field has a value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.OpenedEvent.prototype.hasMissingDocumentBounds = function() {
+ return this.has$Value(3);
+};
+
+
+/**
+ * @return {number} The number of values in the missing_document_bounds field.
+ */
+protos.research.ink.InkEvent.DocumentEvent.OpenedEvent.prototype.missingDocumentBoundsCount = function() {
+ return this.count$Values(3);
+};
+
+
+/**
+ * Clears the values in the missing_document_bounds field.
+ */
+protos.research.ink.InkEvent.DocumentEvent.OpenedEvent.prototype.clearMissingDocumentBounds = function() {
+ this.clear$Field(3);
+};
+
+
+/**
+ * Gets the value of the was_opened_by_cosmoid field.
+ * @return {?boolean} The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.OpenedEvent.prototype.getWasOpenedByCosmoid = function() {
+ return /** @type {?boolean} */ (this.get$Value(4));
+};
+
+
+/**
+ * Gets the value of the was_opened_by_cosmoid field or the default value if not set.
+ * @return {boolean} The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.OpenedEvent.prototype.getWasOpenedByCosmoidOrDefault = function() {
+ return /** @type {boolean} */ (this.get$ValueOrDefault(4));
+};
+
+
+/**
+ * Sets the value of the was_opened_by_cosmoid field.
+ * @param {boolean} value The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.OpenedEvent.prototype.setWasOpenedByCosmoid = function(value) {
+ this.set$Value(4, value);
+};
+
+
+/**
+ * @return {boolean} Whether the was_opened_by_cosmoid field has a value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.OpenedEvent.prototype.hasWasOpenedByCosmoid = function() {
+ return this.has$Value(4);
+};
+
+
+/**
+ * @return {number} The number of values in the was_opened_by_cosmoid field.
+ */
+protos.research.ink.InkEvent.DocumentEvent.OpenedEvent.prototype.wasOpenedByCosmoidCount = function() {
+ return this.count$Values(4);
+};
+
+
+/**
+ * Clears the values in the was_opened_by_cosmoid field.
+ */
+protos.research.ink.InkEvent.DocumentEvent.OpenedEvent.prototype.clearWasOpenedByCosmoid = function() {
+ this.clear$Field(4);
+};
+
+
+/**
+ * Gets the value of the active_users field.
+ * @return {?string} The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.OpenedEvent.prototype.getActiveUsers = function() {
+ return /** @type {?string} */ (this.get$Value(5));
+};
+
+
+/**
+ * Gets the value of the active_users field or the default value if not set.
+ * @return {string} The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.OpenedEvent.prototype.getActiveUsersOrDefault = function() {
+ return /** @type {string} */ (this.get$ValueOrDefault(5));
+};
+
+
+/**
+ * Sets the value of the active_users field.
+ * @param {string} value The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.OpenedEvent.prototype.setActiveUsers = function(value) {
+ this.set$Value(5, value);
+};
+
+
+/**
+ * @return {boolean} Whether the active_users field has a value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.OpenedEvent.prototype.hasActiveUsers = function() {
+ return this.has$Value(5);
+};
+
+
+/**
+ * @return {number} The number of values in the active_users field.
+ */
+protos.research.ink.InkEvent.DocumentEvent.OpenedEvent.prototype.activeUsersCount = function() {
+ return this.count$Values(5);
+};
+
+
+/**
+ * Clears the values in the active_users field.
+ */
+protos.research.ink.InkEvent.DocumentEvent.OpenedEvent.prototype.clearActiveUsers = function() {
+ this.clear$Field(5);
+};
+
+
+
+/**
+ * Message OpenCancelledEvent.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+protos.research.ink.InkEvent.DocumentEvent.OpenCancelledEvent = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(protos.research.ink.InkEvent.DocumentEvent.OpenCancelledEvent, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+protos.research.ink.InkEvent.DocumentEvent.OpenCancelledEvent.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!protos.research.ink.InkEvent.DocumentEvent.OpenCancelledEvent} The cloned message.
+ * @override
+ */
+protos.research.ink.InkEvent.DocumentEvent.OpenCancelledEvent.prototype.clone;
+
+
+/**
+ * Gets the value of the time_until_cancelled field.
+ * @return {?string} The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.OpenCancelledEvent.prototype.getTimeUntilCancelled = function() {
+ return /** @type {?string} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the time_until_cancelled field or the default value if not set.
+ * @return {string} The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.OpenCancelledEvent.prototype.getTimeUntilCancelledOrDefault = function() {
+ return /** @type {string} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the time_until_cancelled field.
+ * @param {string} value The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.OpenCancelledEvent.prototype.setTimeUntilCancelled = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the time_until_cancelled field has a value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.OpenCancelledEvent.prototype.hasTimeUntilCancelled = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the time_until_cancelled field.
+ */
+protos.research.ink.InkEvent.DocumentEvent.OpenCancelledEvent.prototype.timeUntilCancelledCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the time_until_cancelled field.
+ */
+protos.research.ink.InkEvent.DocumentEvent.OpenCancelledEvent.prototype.clearTimeUntilCancelled = function() {
+ this.clear$Field(1);
+};
+
+
+
+/**
+ * Message CollaboratorJoined.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+protos.research.ink.InkEvent.DocumentEvent.CollaboratorJoined = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(protos.research.ink.InkEvent.DocumentEvent.CollaboratorJoined, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+protos.research.ink.InkEvent.DocumentEvent.CollaboratorJoined.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!protos.research.ink.InkEvent.DocumentEvent.CollaboratorJoined} The cloned message.
+ * @override
+ */
+protos.research.ink.InkEvent.DocumentEvent.CollaboratorJoined.prototype.clone;
+
+
+/**
+ * Gets the value of the is_me field.
+ * @return {?boolean} The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.CollaboratorJoined.prototype.getIsMe = function() {
+ return /** @type {?boolean} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the is_me field or the default value if not set.
+ * @return {boolean} The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.CollaboratorJoined.prototype.getIsMeOrDefault = function() {
+ return /** @type {boolean} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the is_me field.
+ * @param {boolean} value The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.CollaboratorJoined.prototype.setIsMe = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the is_me field has a value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.CollaboratorJoined.prototype.hasIsMe = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the is_me field.
+ */
+protos.research.ink.InkEvent.DocumentEvent.CollaboratorJoined.prototype.isMeCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the is_me field.
+ */
+protos.research.ink.InkEvent.DocumentEvent.CollaboratorJoined.prototype.clearIsMe = function() {
+ this.clear$Field(1);
+};
+
+
+
+/**
+ * Message DocumentState.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+protos.research.ink.InkEvent.DocumentEvent.DocumentState = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(protos.research.ink.InkEvent.DocumentEvent.DocumentState, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+protos.research.ink.InkEvent.DocumentEvent.DocumentState.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!protos.research.ink.InkEvent.DocumentEvent.DocumentState} The cloned message.
+ * @override
+ */
+protos.research.ink.InkEvent.DocumentEvent.DocumentState.prototype.clone;
+
+
+/**
+ * Gets the value of the stroke_count field.
+ * @return {?string} The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.DocumentState.prototype.getStrokeCount = function() {
+ return /** @type {?string} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the stroke_count field or the default value if not set.
+ * @return {string} The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.DocumentState.prototype.getStrokeCountOrDefault = function() {
+ return /** @type {string} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the stroke_count field.
+ * @param {string} value The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.DocumentState.prototype.setStrokeCount = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the stroke_count field has a value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.DocumentState.prototype.hasStrokeCount = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the stroke_count field.
+ */
+protos.research.ink.InkEvent.DocumentEvent.DocumentState.prototype.strokeCountCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the stroke_count field.
+ */
+protos.research.ink.InkEvent.DocumentEvent.DocumentState.prototype.clearStrokeCount = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the text_field field at the index given.
+ * @param {number} index The index to lookup.
+ * @return {?protos.research.ink.InkEvent.DocumentEvent.DocumentState.TextField} The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.DocumentState.prototype.getTextField = function(index) {
+ return /** @type {?protos.research.ink.InkEvent.DocumentEvent.DocumentState.TextField} */ (this.get$Value(2, index));
+};
+
+
+/**
+ * Gets the value of the text_field field at the index given or the default value if not set.
+ * @param {number} index The index to lookup.
+ * @return {!protos.research.ink.InkEvent.DocumentEvent.DocumentState.TextField} The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.DocumentState.prototype.getTextFieldOrDefault = function(index) {
+ return /** @type {!protos.research.ink.InkEvent.DocumentEvent.DocumentState.TextField} */ (this.get$ValueOrDefault(2, index));
+};
+
+
+/**
+ * Adds a value to the text_field field.
+ * @param {!protos.research.ink.InkEvent.DocumentEvent.DocumentState.TextField} value The value to add.
+ */
+protos.research.ink.InkEvent.DocumentEvent.DocumentState.prototype.addTextField = function(value) {
+ this.add$Value(2, value);
+};
+
+
+/**
+ * Returns the array of values in the text_field field.
+ * @return {!Array<!protos.research.ink.InkEvent.DocumentEvent.DocumentState.TextField>} The values in the field.
+ */
+protos.research.ink.InkEvent.DocumentEvent.DocumentState.prototype.textFieldArray = function() {
+ return /** @type {!Array<!protos.research.ink.InkEvent.DocumentEvent.DocumentState.TextField>} */ (this.array$Values(2));
+};
+
+
+/**
+ * @return {boolean} Whether the text_field field has a value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.DocumentState.prototype.hasTextField = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the text_field field.
+ */
+protos.research.ink.InkEvent.DocumentEvent.DocumentState.prototype.textFieldCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the text_field field.
+ */
+protos.research.ink.InkEvent.DocumentEvent.DocumentState.prototype.clearTextField = function() {
+ this.clear$Field(2);
+};
+
+
+/**
+ * Gets the value of the sticker_count field.
+ * @return {?string} The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.DocumentState.prototype.getStickerCount = function() {
+ return /** @type {?string} */ (this.get$Value(3));
+};
+
+
+/**
+ * Gets the value of the sticker_count field or the default value if not set.
+ * @return {string} The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.DocumentState.prototype.getStickerCountOrDefault = function() {
+ return /** @type {string} */ (this.get$ValueOrDefault(3));
+};
+
+
+/**
+ * Sets the value of the sticker_count field.
+ * @param {string} value The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.DocumentState.prototype.setStickerCount = function(value) {
+ this.set$Value(3, value);
+};
+
+
+/**
+ * @return {boolean} Whether the sticker_count field has a value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.DocumentState.prototype.hasStickerCount = function() {
+ return this.has$Value(3);
+};
+
+
+/**
+ * @return {number} The number of values in the sticker_count field.
+ */
+protos.research.ink.InkEvent.DocumentEvent.DocumentState.prototype.stickerCountCount = function() {
+ return this.count$Values(3);
+};
+
+
+/**
+ * Clears the values in the sticker_count field.
+ */
+protos.research.ink.InkEvent.DocumentEvent.DocumentState.prototype.clearStickerCount = function() {
+ this.clear$Field(3);
+};
+
+
+
+/**
+ * Message TextField.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+protos.research.ink.InkEvent.DocumentEvent.DocumentState.TextField = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(protos.research.ink.InkEvent.DocumentEvent.DocumentState.TextField, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+protos.research.ink.InkEvent.DocumentEvent.DocumentState.TextField.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!protos.research.ink.InkEvent.DocumentEvent.DocumentState.TextField} The cloned message.
+ * @override
+ */
+protos.research.ink.InkEvent.DocumentEvent.DocumentState.TextField.prototype.clone;
+
+
+/**
+ * Gets the value of the character_count field.
+ * @return {?string} The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.DocumentState.TextField.prototype.getCharacterCount = function() {
+ return /** @type {?string} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the character_count field or the default value if not set.
+ * @return {string} The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.DocumentState.TextField.prototype.getCharacterCountOrDefault = function() {
+ return /** @type {string} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the character_count field.
+ * @param {string} value The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.DocumentState.TextField.prototype.setCharacterCount = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the character_count field has a value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.DocumentState.TextField.prototype.hasCharacterCount = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the character_count field.
+ */
+protos.research.ink.InkEvent.DocumentEvent.DocumentState.TextField.prototype.characterCountCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the character_count field.
+ */
+protos.research.ink.InkEvent.DocumentEvent.DocumentState.TextField.prototype.clearCharacterCount = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the line_count field.
+ * @return {?string} The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.DocumentState.TextField.prototype.getLineCount = function() {
+ return /** @type {?string} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the line_count field or the default value if not set.
+ * @return {string} The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.DocumentState.TextField.prototype.getLineCountOrDefault = function() {
+ return /** @type {string} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the line_count field.
+ * @param {string} value The value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.DocumentState.TextField.prototype.setLineCount = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the line_count field has a value.
+ */
+protos.research.ink.InkEvent.DocumentEvent.DocumentState.TextField.prototype.hasLineCount = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the line_count field.
+ */
+protos.research.ink.InkEvent.DocumentEvent.DocumentState.TextField.prototype.lineCountCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the line_count field.
+ */
+protos.research.ink.InkEvent.DocumentEvent.DocumentState.TextField.prototype.clearLineCount = function() {
+ this.clear$Field(2);
+};
+
+
+
+/**
+ * Message ToolbarEvent.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+protos.research.ink.InkEvent.ToolbarEvent = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(protos.research.ink.InkEvent.ToolbarEvent, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+protos.research.ink.InkEvent.ToolbarEvent.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!protos.research.ink.InkEvent.ToolbarEvent} The cloned message.
+ * @override
+ */
+protos.research.ink.InkEvent.ToolbarEvent.prototype.clone;
+
+
+/**
+ * Gets the value of the tool_event_type field.
+ * @return {?protos.research.ink.InkEvent.ToolbarEvent.ToolEventType} The value.
+ */
+protos.research.ink.InkEvent.ToolbarEvent.prototype.getToolEventType = function() {
+ return /** @type {?protos.research.ink.InkEvent.ToolbarEvent.ToolEventType} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the tool_event_type field or the default value if not set.
+ * @return {!protos.research.ink.InkEvent.ToolbarEvent.ToolEventType} The value.
+ */
+protos.research.ink.InkEvent.ToolbarEvent.prototype.getToolEventTypeOrDefault = function() {
+ return /** @type {!protos.research.ink.InkEvent.ToolbarEvent.ToolEventType} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the tool_event_type field.
+ * @param {!protos.research.ink.InkEvent.ToolbarEvent.ToolEventType} value The value.
+ */
+protos.research.ink.InkEvent.ToolbarEvent.prototype.setToolEventType = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the tool_event_type field has a value.
+ */
+protos.research.ink.InkEvent.ToolbarEvent.prototype.hasToolEventType = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the tool_event_type field.
+ */
+protos.research.ink.InkEvent.ToolbarEvent.prototype.toolEventTypeCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the tool_event_type field.
+ */
+protos.research.ink.InkEvent.ToolbarEvent.prototype.clearToolEventType = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the tool_type field.
+ * @return {?protos.research.ink.InkEvent.ToolbarEvent.ToolType} The value.
+ */
+protos.research.ink.InkEvent.ToolbarEvent.prototype.getToolType = function() {
+ return /** @type {?protos.research.ink.InkEvent.ToolbarEvent.ToolType} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the tool_type field or the default value if not set.
+ * @return {!protos.research.ink.InkEvent.ToolbarEvent.ToolType} The value.
+ */
+protos.research.ink.InkEvent.ToolbarEvent.prototype.getToolTypeOrDefault = function() {
+ return /** @type {!protos.research.ink.InkEvent.ToolbarEvent.ToolType} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the tool_type field.
+ * @param {!protos.research.ink.InkEvent.ToolbarEvent.ToolType} value The value.
+ */
+protos.research.ink.InkEvent.ToolbarEvent.prototype.setToolType = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the tool_type field has a value.
+ */
+protos.research.ink.InkEvent.ToolbarEvent.prototype.hasToolType = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the tool_type field.
+ */
+protos.research.ink.InkEvent.ToolbarEvent.prototype.toolTypeCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the tool_type field.
+ */
+protos.research.ink.InkEvent.ToolbarEvent.prototype.clearToolType = function() {
+ this.clear$Field(2);
+};
+
+
+/**
+ * Gets the value of the expand_method field.
+ * @return {?protos.research.ink.InkEvent.ToolbarEvent.ExpandMethod} The value.
+ */
+protos.research.ink.InkEvent.ToolbarEvent.prototype.getExpandMethod = function() {
+ return /** @type {?protos.research.ink.InkEvent.ToolbarEvent.ExpandMethod} */ (this.get$Value(3));
+};
+
+
+/**
+ * Gets the value of the expand_method field or the default value if not set.
+ * @return {!protos.research.ink.InkEvent.ToolbarEvent.ExpandMethod} The value.
+ */
+protos.research.ink.InkEvent.ToolbarEvent.prototype.getExpandMethodOrDefault = function() {
+ return /** @type {!protos.research.ink.InkEvent.ToolbarEvent.ExpandMethod} */ (this.get$ValueOrDefault(3));
+};
+
+
+/**
+ * Sets the value of the expand_method field.
+ * @param {!protos.research.ink.InkEvent.ToolbarEvent.ExpandMethod} value The value.
+ */
+protos.research.ink.InkEvent.ToolbarEvent.prototype.setExpandMethod = function(value) {
+ this.set$Value(3, value);
+};
+
+
+/**
+ * @return {boolean} Whether the expand_method field has a value.
+ */
+protos.research.ink.InkEvent.ToolbarEvent.prototype.hasExpandMethod = function() {
+ return this.has$Value(3);
+};
+
+
+/**
+ * @return {number} The number of values in the expand_method field.
+ */
+protos.research.ink.InkEvent.ToolbarEvent.prototype.expandMethodCount = function() {
+ return this.count$Values(3);
+};
+
+
+/**
+ * Clears the values in the expand_method field.
+ */
+protos.research.ink.InkEvent.ToolbarEvent.prototype.clearExpandMethod = function() {
+ this.clear$Field(3);
+};
+
+
+/**
+ * Gets the value of the color field.
+ * @return {?number} The value.
+ */
+protos.research.ink.InkEvent.ToolbarEvent.prototype.getColor = function() {
+ return /** @type {?number} */ (this.get$Value(4));
+};
+
+
+/**
+ * Gets the value of the color field or the default value if not set.
+ * @return {number} The value.
+ */
+protos.research.ink.InkEvent.ToolbarEvent.prototype.getColorOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(4));
+};
+
+
+/**
+ * Sets the value of the color field.
+ * @param {number} value The value.
+ */
+protos.research.ink.InkEvent.ToolbarEvent.prototype.setColor = function(value) {
+ this.set$Value(4, value);
+};
+
+
+/**
+ * @return {boolean} Whether the color field has a value.
+ */
+protos.research.ink.InkEvent.ToolbarEvent.prototype.hasColor = function() {
+ return this.has$Value(4);
+};
+
+
+/**
+ * @return {number} The number of values in the color field.
+ */
+protos.research.ink.InkEvent.ToolbarEvent.prototype.colorCount = function() {
+ return this.count$Values(4);
+};
+
+
+/**
+ * Clears the values in the color field.
+ */
+protos.research.ink.InkEvent.ToolbarEvent.prototype.clearColor = function() {
+ this.clear$Field(4);
+};
+
+
+/**
+ * Enumeration ToolEventType.
+ * @enum {number}
+ */
+protos.research.ink.InkEvent.ToolbarEvent.ToolEventType = {
+ UNKNOWN_TOOL_EVENT: 0,
+ TOOLBAR_EXPANDED: 1,
+ TOOLBAR_EXTRA_COLORS_EXPANDED: 2,
+ TOOLBAR_CONTRACTED_BY_USER: 3,
+ TOOL_TYPE_CHANGED: 4,
+ TOOL_COLOR_SELECTED: 5,
+ TOOL_SIZE_SELECTED: 6,
+ CLEAR_CANVAS: 7,
+ SELECT_NONE: 8
+};
+
+
+/**
+ * Enumeration ToolType.
+ * @enum {number}
+ */
+protos.research.ink.InkEvent.ToolbarEvent.ToolType = {
+ UNKNOWN_TOOL_TYPE: 0,
+ EDIT_TOOL: 1,
+ CALLIGRAPHY: 2,
+ MARKER: 3,
+ HIGHLIGHTER: 4,
+ MAGIC_ERASER: 5
+};
+
+
+/**
+ * Enumeration ExpandMethod.
+ * @enum {number}
+ */
+protos.research.ink.InkEvent.ToolbarEvent.ExpandMethod = {
+ UNKNOWN_EXPAND_METHOD: 0,
+ SECOND_TAP: 1,
+ DRAG: 2,
+ EXPAND_BUTTON_TAP: 3
+};
+
+
+
+/**
+ * Message EngineEvent.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+protos.research.ink.InkEvent.EngineEvent = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(protos.research.ink.InkEvent.EngineEvent, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+protos.research.ink.InkEvent.EngineEvent.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!protos.research.ink.InkEvent.EngineEvent} The cloned message.
+ * @override
+ */
+protos.research.ink.InkEvent.EngineEvent.prototype.clone;
+
+
+/**
+ * Gets the value of the engine_event_type field.
+ * @return {?protos.research.ink.InkEvent.EngineEvent.EngineEventType} The value.
+ */
+protos.research.ink.InkEvent.EngineEvent.prototype.getEngineEventType = function() {
+ return /** @type {?protos.research.ink.InkEvent.EngineEvent.EngineEventType} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the engine_event_type field or the default value if not set.
+ * @return {!protos.research.ink.InkEvent.EngineEvent.EngineEventType} The value.
+ */
+protos.research.ink.InkEvent.EngineEvent.prototype.getEngineEventTypeOrDefault = function() {
+ return /** @type {!protos.research.ink.InkEvent.EngineEvent.EngineEventType} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the engine_event_type field.
+ * @param {!protos.research.ink.InkEvent.EngineEvent.EngineEventType} value The value.
+ */
+protos.research.ink.InkEvent.EngineEvent.prototype.setEngineEventType = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the engine_event_type field has a value.
+ */
+protos.research.ink.InkEvent.EngineEvent.prototype.hasEngineEventType = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the engine_event_type field.
+ */
+protos.research.ink.InkEvent.EngineEvent.prototype.engineEventTypeCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the engine_event_type field.
+ */
+protos.research.ink.InkEvent.EngineEvent.prototype.clearEngineEventType = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the error_code field.
+ * @return {?string} The value.
+ */
+protos.research.ink.InkEvent.EngineEvent.prototype.getErrorCode = function() {
+ return /** @type {?string} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the error_code field or the default value if not set.
+ * @return {string} The value.
+ */
+protos.research.ink.InkEvent.EngineEvent.prototype.getErrorCodeOrDefault = function() {
+ return /** @type {string} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the error_code field.
+ * @param {string} value The value.
+ */
+protos.research.ink.InkEvent.EngineEvent.prototype.setErrorCode = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the error_code field has a value.
+ */
+protos.research.ink.InkEvent.EngineEvent.prototype.hasErrorCode = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the error_code field.
+ */
+protos.research.ink.InkEvent.EngineEvent.prototype.errorCodeCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the error_code field.
+ */
+protos.research.ink.InkEvent.EngineEvent.prototype.clearErrorCode = function() {
+ this.clear$Field(2);
+};
+
+
+/**
+ * Enumeration EngineEventType.
+ * @enum {number}
+ */
+protos.research.ink.InkEvent.EngineEvent.EngineEventType = {
+ UNKNOWN_ENGINE_EVENT: 0,
+ LOST_GL_CONTEXT: 1,
+ RAISED_FATAL_EXCEPTION: 2
+};
+
+
+
+/**
+ * Message GmsEvent.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+protos.research.ink.InkEvent.GmsEvent = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(protos.research.ink.InkEvent.GmsEvent, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+protos.research.ink.InkEvent.GmsEvent.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!protos.research.ink.InkEvent.GmsEvent} The cloned message.
+ * @override
+ */
+protos.research.ink.InkEvent.GmsEvent.prototype.clone;
+
+
+/**
+ * Gets the value of the gms_event_type field.
+ * @return {?protos.research.ink.InkEvent.GmsEvent.GmsEventType} The value.
+ */
+protos.research.ink.InkEvent.GmsEvent.prototype.getGmsEventType = function() {
+ return /** @type {?protos.research.ink.InkEvent.GmsEvent.GmsEventType} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the gms_event_type field or the default value if not set.
+ * @return {!protos.research.ink.InkEvent.GmsEvent.GmsEventType} The value.
+ */
+protos.research.ink.InkEvent.GmsEvent.prototype.getGmsEventTypeOrDefault = function() {
+ return /** @type {!protos.research.ink.InkEvent.GmsEvent.GmsEventType} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the gms_event_type field.
+ * @param {!protos.research.ink.InkEvent.GmsEvent.GmsEventType} value The value.
+ */
+protos.research.ink.InkEvent.GmsEvent.prototype.setGmsEventType = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the gms_event_type field has a value.
+ */
+protos.research.ink.InkEvent.GmsEvent.prototype.hasGmsEventType = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the gms_event_type field.
+ */
+protos.research.ink.InkEvent.GmsEvent.prototype.gmsEventTypeCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the gms_event_type field.
+ */
+protos.research.ink.InkEvent.GmsEvent.prototype.clearGmsEventType = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the time_since_connect_start field.
+ * @return {?string} The value.
+ */
+protos.research.ink.InkEvent.GmsEvent.prototype.getTimeSinceConnectStart = function() {
+ return /** @type {?string} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the time_since_connect_start field or the default value if not set.
+ * @return {string} The value.
+ */
+protos.research.ink.InkEvent.GmsEvent.prototype.getTimeSinceConnectStartOrDefault = function() {
+ return /** @type {string} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the time_since_connect_start field.
+ * @param {string} value The value.
+ */
+protos.research.ink.InkEvent.GmsEvent.prototype.setTimeSinceConnectStart = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the time_since_connect_start field has a value.
+ */
+protos.research.ink.InkEvent.GmsEvent.prototype.hasTimeSinceConnectStart = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the time_since_connect_start field.
+ */
+protos.research.ink.InkEvent.GmsEvent.prototype.timeSinceConnectStartCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the time_since_connect_start field.
+ */
+protos.research.ink.InkEvent.GmsEvent.prototype.clearTimeSinceConnectStart = function() {
+ this.clear$Field(2);
+};
+
+
+/**
+ * Gets the value of the failure_has_resolution field.
+ * @return {?boolean} The value.
+ */
+protos.research.ink.InkEvent.GmsEvent.prototype.getFailureHasResolution = function() {
+ return /** @type {?boolean} */ (this.get$Value(3));
+};
+
+
+/**
+ * Gets the value of the failure_has_resolution field or the default value if not set.
+ * @return {boolean} The value.
+ */
+protos.research.ink.InkEvent.GmsEvent.prototype.getFailureHasResolutionOrDefault = function() {
+ return /** @type {boolean} */ (this.get$ValueOrDefault(3));
+};
+
+
+/**
+ * Sets the value of the failure_has_resolution field.
+ * @param {boolean} value The value.
+ */
+protos.research.ink.InkEvent.GmsEvent.prototype.setFailureHasResolution = function(value) {
+ this.set$Value(3, value);
+};
+
+
+/**
+ * @return {boolean} Whether the failure_has_resolution field has a value.
+ */
+protos.research.ink.InkEvent.GmsEvent.prototype.hasFailureHasResolution = function() {
+ return this.has$Value(3);
+};
+
+
+/**
+ * @return {number} The number of values in the failure_has_resolution field.
+ */
+protos.research.ink.InkEvent.GmsEvent.prototype.failureHasResolutionCount = function() {
+ return this.count$Values(3);
+};
+
+
+/**
+ * Clears the values in the failure_has_resolution field.
+ */
+protos.research.ink.InkEvent.GmsEvent.prototype.clearFailureHasResolution = function() {
+ this.clear$Field(3);
+};
+
+
+/**
+ * Gets the value of the gms_error_code field.
+ * @return {?string} The value.
+ */
+protos.research.ink.InkEvent.GmsEvent.prototype.getGmsErrorCode = function() {
+ return /** @type {?string} */ (this.get$Value(4));
+};
+
+
+/**
+ * Gets the value of the gms_error_code field or the default value if not set.
+ * @return {string} The value.
+ */
+protos.research.ink.InkEvent.GmsEvent.prototype.getGmsErrorCodeOrDefault = function() {
+ return /** @type {string} */ (this.get$ValueOrDefault(4));
+};
+
+
+/**
+ * Sets the value of the gms_error_code field.
+ * @param {string} value The value.
+ */
+protos.research.ink.InkEvent.GmsEvent.prototype.setGmsErrorCode = function(value) {
+ this.set$Value(4, value);
+};
+
+
+/**
+ * @return {boolean} Whether the gms_error_code field has a value.
+ */
+protos.research.ink.InkEvent.GmsEvent.prototype.hasGmsErrorCode = function() {
+ return this.has$Value(4);
+};
+
+
+/**
+ * @return {number} The number of values in the gms_error_code field.
+ */
+protos.research.ink.InkEvent.GmsEvent.prototype.gmsErrorCodeCount = function() {
+ return this.count$Values(4);
+};
+
+
+/**
+ * Clears the values in the gms_error_code field.
+ */
+protos.research.ink.InkEvent.GmsEvent.prototype.clearGmsErrorCode = function() {
+ this.clear$Field(4);
+};
+
+
+/**
+ * Enumeration GmsEventType.
+ * @enum {number}
+ */
+protos.research.ink.InkEvent.GmsEvent.GmsEventType = {
+ CONNECT_SUCCESS: 0,
+ CONNECT_FAILED: 1,
+ STOPPED_WHEN_PENDING_CONNECT: 2
+};
+
+
+/** @override */
+protos.research.ink.InkEvent.prototype.getDescriptor = function() {
+ var descriptor = protos.research.ink.InkEvent.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'InkEvent',
+ fullName: 'logs.proto.research.ink.InkEvent'
+ },
+ 1: {
+ name: 'host',
+ fieldType: goog.proto2.Message.FieldType.ENUM,
+ defaultValue: protos.research.ink.InkEvent.Host.UNKNOWN_HOST,
+ type: protos.research.ink.InkEvent.Host
+ },
+ 2: {
+ name: 'event_type',
+ fieldType: goog.proto2.Message.FieldType.ENUM,
+ defaultValue: protos.research.ink.InkEvent.EventType.UNKNOWN_TYPE,
+ type: protos.research.ink.InkEvent.EventType
+ },
+ 3: {
+ name: 'document_event',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: protos.research.ink.InkEvent.DocumentEvent
+ },
+ 4: {
+ name: 'toolbar_event',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: protos.research.ink.InkEvent.ToolbarEvent
+ },
+ 5: {
+ name: 'engine_event',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: protos.research.ink.InkEvent.EngineEvent
+ },
+ 6: {
+ name: 'gms_event',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: protos.research.ink.InkEvent.GmsEvent
+ }
+ };
+ protos.research.ink.InkEvent.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ protos.research.ink.InkEvent, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+protos.research.ink.InkEvent.getDescriptor =
+ protos.research.ink.InkEvent.prototype.getDescriptor;
+
+
+/** @override */
+protos.research.ink.InkEvent.DocumentEvent.prototype.getDescriptor = function() {
+ var descriptor = protos.research.ink.InkEvent.DocumentEvent.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'DocumentEvent',
+ containingType: protos.research.ink.InkEvent,
+ fullName: 'logs.proto.research.ink.InkEvent.DocumentEvent'
+ },
+ 1: {
+ name: 'event_type',
+ fieldType: goog.proto2.Message.FieldType.ENUM,
+ defaultValue: protos.research.ink.InkEvent.DocumentEvent.DocumentEventType.UNKNOWN_DOCUMENT_EVENT,
+ type: protos.research.ink.InkEvent.DocumentEvent.DocumentEventType
+ },
+ 2: {
+ name: 'opened_event',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: protos.research.ink.InkEvent.DocumentEvent.OpenedEvent
+ },
+ 3: {
+ name: 'open_cancelled_event',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: protos.research.ink.InkEvent.DocumentEvent.OpenCancelledEvent
+ },
+ 4: {
+ name: 'error_code',
+ fieldType: goog.proto2.Message.FieldType.INT64,
+ type: String
+ },
+ 5: {
+ name: 'brix_error_code',
+ fieldType: goog.proto2.Message.FieldType.INT64,
+ type: String
+ },
+ 6: {
+ name: 'collaborator_joined_event',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: protos.research.ink.InkEvent.DocumentEvent.CollaboratorJoined
+ },
+ 7: {
+ name: 'document_state',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: protos.research.ink.InkEvent.DocumentEvent.DocumentState
+ }
+ };
+ protos.research.ink.InkEvent.DocumentEvent.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ protos.research.ink.InkEvent.DocumentEvent, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+protos.research.ink.InkEvent.DocumentEvent.getDescriptor =
+ protos.research.ink.InkEvent.DocumentEvent.prototype.getDescriptor;
+
+
+/** @override */
+protos.research.ink.InkEvent.DocumentEvent.OpenedEvent.prototype.getDescriptor = function() {
+ var descriptor = protos.research.ink.InkEvent.DocumentEvent.OpenedEvent.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'OpenedEvent',
+ containingType: protos.research.ink.InkEvent.DocumentEvent,
+ fullName: 'logs.proto.research.ink.InkEvent.DocumentEvent.OpenedEvent'
+ },
+ 1: {
+ name: 'millis_until_first_byte_loaded',
+ fieldType: goog.proto2.Message.FieldType.INT64,
+ type: String
+ },
+ 2: {
+ name: 'millis_until_editable',
+ fieldType: goog.proto2.Message.FieldType.INT64,
+ type: String
+ },
+ 3: {
+ name: 'missing_document_bounds',
+ fieldType: goog.proto2.Message.FieldType.BOOL,
+ type: Boolean
+ },
+ 4: {
+ name: 'was_opened_by_cosmoid',
+ fieldType: goog.proto2.Message.FieldType.BOOL,
+ type: Boolean
+ },
+ 5: {
+ name: 'active_users',
+ fieldType: goog.proto2.Message.FieldType.INT64,
+ type: String
+ }
+ };
+ protos.research.ink.InkEvent.DocumentEvent.OpenedEvent.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ protos.research.ink.InkEvent.DocumentEvent.OpenedEvent, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+protos.research.ink.InkEvent.DocumentEvent.OpenedEvent.getDescriptor =
+ protos.research.ink.InkEvent.DocumentEvent.OpenedEvent.prototype.getDescriptor;
+
+
+/** @override */
+protos.research.ink.InkEvent.DocumentEvent.OpenCancelledEvent.prototype.getDescriptor = function() {
+ var descriptor = protos.research.ink.InkEvent.DocumentEvent.OpenCancelledEvent.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'OpenCancelledEvent',
+ containingType: protos.research.ink.InkEvent.DocumentEvent,
+ fullName: 'logs.proto.research.ink.InkEvent.DocumentEvent.OpenCancelledEvent'
+ },
+ 1: {
+ name: 'time_until_cancelled',
+ fieldType: goog.proto2.Message.FieldType.INT64,
+ type: String
+ }
+ };
+ protos.research.ink.InkEvent.DocumentEvent.OpenCancelledEvent.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ protos.research.ink.InkEvent.DocumentEvent.OpenCancelledEvent, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+protos.research.ink.InkEvent.DocumentEvent.OpenCancelledEvent.getDescriptor =
+ protos.research.ink.InkEvent.DocumentEvent.OpenCancelledEvent.prototype.getDescriptor;
+
+
+/** @override */
+protos.research.ink.InkEvent.DocumentEvent.CollaboratorJoined.prototype.getDescriptor = function() {
+ var descriptor = protos.research.ink.InkEvent.DocumentEvent.CollaboratorJoined.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'CollaboratorJoined',
+ containingType: protos.research.ink.InkEvent.DocumentEvent,
+ fullName: 'logs.proto.research.ink.InkEvent.DocumentEvent.CollaboratorJoined'
+ },
+ 1: {
+ name: 'is_me',
+ fieldType: goog.proto2.Message.FieldType.BOOL,
+ type: Boolean
+ }
+ };
+ protos.research.ink.InkEvent.DocumentEvent.CollaboratorJoined.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ protos.research.ink.InkEvent.DocumentEvent.CollaboratorJoined, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+protos.research.ink.InkEvent.DocumentEvent.CollaboratorJoined.getDescriptor =
+ protos.research.ink.InkEvent.DocumentEvent.CollaboratorJoined.prototype.getDescriptor;
+
+
+/** @override */
+protos.research.ink.InkEvent.DocumentEvent.DocumentState.prototype.getDescriptor = function() {
+ var descriptor = protos.research.ink.InkEvent.DocumentEvent.DocumentState.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'DocumentState',
+ containingType: protos.research.ink.InkEvent.DocumentEvent,
+ fullName: 'logs.proto.research.ink.InkEvent.DocumentEvent.DocumentState'
+ },
+ 1: {
+ name: 'stroke_count',
+ fieldType: goog.proto2.Message.FieldType.INT64,
+ type: String
+ },
+ 2: {
+ name: 'text_field',
+ repeated: true,
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: protos.research.ink.InkEvent.DocumentEvent.DocumentState.TextField
+ },
+ 3: {
+ name: 'sticker_count',
+ fieldType: goog.proto2.Message.FieldType.INT64,
+ type: String
+ }
+ };
+ protos.research.ink.InkEvent.DocumentEvent.DocumentState.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ protos.research.ink.InkEvent.DocumentEvent.DocumentState, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+protos.research.ink.InkEvent.DocumentEvent.DocumentState.getDescriptor =
+ protos.research.ink.InkEvent.DocumentEvent.DocumentState.prototype.getDescriptor;
+
+
+/** @override */
+protos.research.ink.InkEvent.DocumentEvent.DocumentState.TextField.prototype.getDescriptor = function() {
+ var descriptor = protos.research.ink.InkEvent.DocumentEvent.DocumentState.TextField.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'TextField',
+ containingType: protos.research.ink.InkEvent.DocumentEvent.DocumentState,
+ fullName: 'logs.proto.research.ink.InkEvent.DocumentEvent.DocumentState.TextField'
+ },
+ 1: {
+ name: 'character_count',
+ fieldType: goog.proto2.Message.FieldType.INT64,
+ type: String
+ },
+ 2: {
+ name: 'line_count',
+ fieldType: goog.proto2.Message.FieldType.INT64,
+ type: String
+ }
+ };
+ protos.research.ink.InkEvent.DocumentEvent.DocumentState.TextField.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ protos.research.ink.InkEvent.DocumentEvent.DocumentState.TextField, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+protos.research.ink.InkEvent.DocumentEvent.DocumentState.TextField.getDescriptor =
+ protos.research.ink.InkEvent.DocumentEvent.DocumentState.TextField.prototype.getDescriptor;
+
+
+/** @override */
+protos.research.ink.InkEvent.ToolbarEvent.prototype.getDescriptor = function() {
+ var descriptor = protos.research.ink.InkEvent.ToolbarEvent.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'ToolbarEvent',
+ containingType: protos.research.ink.InkEvent,
+ fullName: 'logs.proto.research.ink.InkEvent.ToolbarEvent'
+ },
+ 1: {
+ name: 'tool_event_type',
+ fieldType: goog.proto2.Message.FieldType.ENUM,
+ defaultValue: protos.research.ink.InkEvent.ToolbarEvent.ToolEventType.UNKNOWN_TOOL_EVENT,
+ type: protos.research.ink.InkEvent.ToolbarEvent.ToolEventType
+ },
+ 2: {
+ name: 'tool_type',
+ fieldType: goog.proto2.Message.FieldType.ENUM,
+ defaultValue: protos.research.ink.InkEvent.ToolbarEvent.ToolType.UNKNOWN_TOOL_TYPE,
+ type: protos.research.ink.InkEvent.ToolbarEvent.ToolType
+ },
+ 3: {
+ name: 'expand_method',
+ fieldType: goog.proto2.Message.FieldType.ENUM,
+ defaultValue: protos.research.ink.InkEvent.ToolbarEvent.ExpandMethod.UNKNOWN_EXPAND_METHOD,
+ type: protos.research.ink.InkEvent.ToolbarEvent.ExpandMethod
+ },
+ 4: {
+ name: 'color',
+ fieldType: goog.proto2.Message.FieldType.INT32,
+ type: Number
+ }
+ };
+ protos.research.ink.InkEvent.ToolbarEvent.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ protos.research.ink.InkEvent.ToolbarEvent, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+protos.research.ink.InkEvent.ToolbarEvent.getDescriptor =
+ protos.research.ink.InkEvent.ToolbarEvent.prototype.getDescriptor;
+
+
+/** @override */
+protos.research.ink.InkEvent.EngineEvent.prototype.getDescriptor = function() {
+ var descriptor = protos.research.ink.InkEvent.EngineEvent.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'EngineEvent',
+ containingType: protos.research.ink.InkEvent,
+ fullName: 'logs.proto.research.ink.InkEvent.EngineEvent'
+ },
+ 1: {
+ name: 'engine_event_type',
+ fieldType: goog.proto2.Message.FieldType.ENUM,
+ defaultValue: protos.research.ink.InkEvent.EngineEvent.EngineEventType.UNKNOWN_ENGINE_EVENT,
+ type: protos.research.ink.InkEvent.EngineEvent.EngineEventType
+ },
+ 2: {
+ name: 'error_code',
+ fieldType: goog.proto2.Message.FieldType.INT64,
+ type: String
+ }
+ };
+ protos.research.ink.InkEvent.EngineEvent.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ protos.research.ink.InkEvent.EngineEvent, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+protos.research.ink.InkEvent.EngineEvent.getDescriptor =
+ protos.research.ink.InkEvent.EngineEvent.prototype.getDescriptor;
+
+
+/** @override */
+protos.research.ink.InkEvent.GmsEvent.prototype.getDescriptor = function() {
+ var descriptor = protos.research.ink.InkEvent.GmsEvent.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'GmsEvent',
+ containingType: protos.research.ink.InkEvent,
+ fullName: 'logs.proto.research.ink.InkEvent.GmsEvent'
+ },
+ 1: {
+ name: 'gms_event_type',
+ fieldType: goog.proto2.Message.FieldType.ENUM,
+ defaultValue: protos.research.ink.InkEvent.GmsEvent.GmsEventType.CONNECT_SUCCESS,
+ type: protos.research.ink.InkEvent.GmsEvent.GmsEventType
+ },
+ 2: {
+ name: 'time_since_connect_start',
+ fieldType: goog.proto2.Message.FieldType.INT64,
+ type: String
+ },
+ 3: {
+ name: 'failure_has_resolution',
+ fieldType: goog.proto2.Message.FieldType.BOOL,
+ type: Boolean
+ },
+ 4: {
+ name: 'gms_error_code',
+ fieldType: goog.proto2.Message.FieldType.INT64,
+ type: String
+ }
+ };
+ protos.research.ink.InkEvent.GmsEvent.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ protos.research.ink.InkEvent.GmsEvent, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+protos.research.ink.InkEvent.GmsEvent.getDescriptor =
+ protos.research.ink.InkEvent.GmsEvent.prototype.getDescriptor;
diff --git a/chromium/third_party/ink/ink_scripts.js b/chromium/third_party/ink/ink_scripts.js
new file mode 100644
index 00000000000..b056e6d6a2a
--- /dev/null
+++ b/chromium/third_party/ink/ink_scripts.js
@@ -0,0 +1,118 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The include directives are put into Javascript-style comments to prevent
+// parsing errors in non-flattened mode. The flattener still sees them.
+// Note that this makes the flattener to comment out the first line of the
+// included file but that's all right since any javascript file should start
+// with a copyright comment anyway.
+
+// <include src="ink/web/js/main.soy.js">
+// <include src="ink/web/js/cursor_updater.js">
+// <include src="ink/web/js/canvas_manager/canvas_manager.js">
+// <include src="ink/web/js/embed/embed_component.js">
+// <include src="ink/web/js/embed/events.js">
+// <include src="ink/web/js/embed/embed.js">
+// <include src="template/soy/soyutils_usegoog.js">
+// <include src="ink_event.pb.js">
+// <include src="sketchology/public/nacl/embed.soy.js">
+// <include src="sketchology/public/nacl/sketchology_engine_wrapper.js">
+// <include src="sketchology/public/js/common/color.js">
+// <include src="sketchology/public/js/common/model.js">
+// <include src="sketchology/public/js/common/undo_state_change_event.js">
+// <include src="sketchology/public/js/common/proto_serializer.js">
+// <include src="sketchology/public/js/common/util.js">
+// <include src="sketchology/public/js/common/element_listener.js">
+// <include src="sketchology/public/js/common/brush_model.js">
+// <include src="sketchology/proto/rect_bounds.pb.js">
+// <include src="sketchology/proto/elements.pb.js">
+// <include src="sketchology/proto/animations.pb.js">
+// <include src="sketchology/proto/sengine.pb.js">
+// <include src="sketchology/proto/document.pb.js">
+// <include src="wireserializer.js">
+// <include src="closure/functions/functions.js">
+// <include src="closure/base.js">
+// <include src="closure/fs/url.js">
+// <include src="closure/uri/utils.js">
+// <include src="closure/uri/uri.js">
+// <include src="closure/style/style.js">
+// <include src="closure/crypt/crypt.js">
+// <include src="closure/crypt/base64.js">
+// <include src="closure/ui/component.js">
+// <include src="closure/ui/idgenerator.js">
+// <include src="closure/labs/useragent/platform.js">
+// <include src="closure/labs/useragent/browser.js">
+// <include src="closure/labs/useragent/util.js">
+// <include src="closure/labs/useragent/engine.js">
+// <include src="closure/string/const.js">
+// <include src="closure/string/string.js">
+// <include src="closure/string/typedstring.js">
+// <include src="closure/structs/set.js">
+// <include src="closure/structs/inversionmap.js">
+// <include src="closure/structs/collection.js">
+// <include src="closure/structs/structs.js">
+// <include src="closure/structs/map.js">
+// <include src="closure/proto2/serializer.js">
+// <include src="closure/proto2/objectserializer.js">
+// <include src="closure/proto2/message.js">
+// <include src="closure/proto2/fielddescriptor.js">
+// <include src="closure/proto2/descriptor.js">
+// <include src="closure/html/safehtml.js">
+// <include src="closure/html/safescript.js">
+// <include src="closure/html/safestyle.js">
+// <include src="closure/html/uncheckedconversions.js">
+// <include src="closure/html/safestylesheet.js">
+// <include src="closure/html/legacyconversions.js">
+// <include src="closure/html/trustedresourceurl.js">
+// <include src="closure/html/safeurl.js">
+// <include src="closure/math/long.js">
+// <include src="closure/math/irect.js">
+// <include src="closure/math/size.js">
+// <include src="closure/math/math.js">
+// <include src="closure/math/rect.js">
+// <include src="closure/math/coordinate.js">
+// <include src="closure/math/box.js">
+// <include src="closure/object/object.js">
+// <include src="closure/reflect/reflect.js">
+// <include src="closure/useragent/useragent.js">
+// <include src="closure/useragent/product.js">
+// <include src="closure/i18n/uchar.js">
+// <include src="closure/i18n/bidi.js">
+// <include src="closure/i18n/graphemebreak.js">
+// <include src="closure/i18n/bidiformatter.js">
+// <include src="closure/dom/tagname.js">
+// <include src="closure/dom/classlist.js">
+// <include src="closure/dom/asserts.js">
+// <include src="closure/dom/nodetype.js">
+// <include src="closure/dom/dom.js">
+// <include src="closure/dom/tags.js">
+// <include src="closure/dom/safe.js">
+// <include src="closure/dom/browserfeature.js">
+// <include src="closure/dom/htmlelement.js">
+// <include src="closure/dom/vendor.js">
+// <include src="closure/soy/soy.js">
+// <include src="closure/soy/data.js">
+// <include src="closure/format/format.js">
+// <include src="closure/asserts/asserts.js">
+// <include src="closure/array/array.js">
+// <include src="closure/iter/iter.js">
+// <include src="closure/events/eventid.js">
+// <include src="closure/events/event.js">
+// <include src="closure/events/listener.js">
+// <include src="closure/events/listenable.js">
+// <include src="closure/events/browserevent.js">
+// <include src="closure/events/events.js">
+// <include src="closure/events/browserfeature.js">
+// <include src="closure/events/eventtarget.js">
+// <include src="closure/events/eventhandler.js">
+// <include src="closure/events/listenermap.js">
+// <include src="closure/events/keycodes.js">
+// <include src="closure/events/wheelevent.js">
+// <include src="closure/events/eventtype.js">
+// <include src="closure/disposable/idisposable.js">
+// <include src="closure/disposable/disposable.js">
+// <include src="closure/debug/debug.js">
+// <include src="closure/debug/errorcontext.js">
+// <include src="closure/debug/entrypointregistry.js">
+// <include src="closure/debug/error.js">
diff --git a/chromium/third_party/ink/prebuilt/_platform_specific/arm/ink_arm.nexe b/chromium/third_party/ink/prebuilt/_platform_specific/arm/ink_arm.nexe
new file mode 100644
index 00000000000..58c3b7cd2ba
--- /dev/null
+++ b/chromium/third_party/ink/prebuilt/_platform_specific/arm/ink_arm.nexe
Binary files differ
diff --git a/chromium/third_party/ink/prebuilt/_platform_specific/x86-32/ink_x86_32.nexe b/chromium/third_party/ink/prebuilt/_platform_specific/x86-32/ink_x86_32.nexe
new file mode 100644
index 00000000000..ff8676bcd9f
--- /dev/null
+++ b/chromium/third_party/ink/prebuilt/_platform_specific/x86-32/ink_x86_32.nexe
Binary files differ
diff --git a/chromium/third_party/ink/prebuilt/_platform_specific/x86-64/ink_x86_64.nexe b/chromium/third_party/ink/prebuilt/_platform_specific/x86-64/ink_x86_64.nexe
new file mode 100644
index 00000000000..8d53e2bd7d2
--- /dev/null
+++ b/chromium/third_party/ink/prebuilt/_platform_specific/x86-64/ink_x86_64.nexe
Binary files differ
diff --git a/chromium/third_party/ink/prebuilt/index.html b/chromium/third_party/ink/prebuilt/index.html
new file mode 100644
index 00000000000..bd953395cc2
--- /dev/null
+++ b/chromium/third_party/ink/prebuilt/index.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <style type="text/css">
+ body { margin: 0; }
+ #ink-canvas {
+ height: 600px;
+ width: 800px;
+ }
+ </style>
+ <link rel="icon" href="data:,"> <!-- Prevent favicon.ico requests -->
+ </head>
+ <body>
+ <div id="ink-canvas"></div>
+ <script src="ink_lib_binary.js"></script>
+ <script src="ink_demo.js"></script>
+ </body>
+</html>
diff --git a/chromium/third_party/ink/prebuilt/ink.nmf b/chromium/third_party/ink/prebuilt/ink.nmf
new file mode 100644
index 00000000000..b901943fb91
--- /dev/null
+++ b/chromium/third_party/ink/prebuilt/ink.nmf
@@ -0,0 +1,14 @@
+{
+ "program": {
+ "arm": {
+ "url": "_platform_specific/arm/ink_arm.nexe"
+ },
+ "x86-32": {
+ "url": "_platform_specific/x86-32/ink_x86_32.nexe"
+ },
+ "x86-64": {
+ "url": "_platform_specific/x86-64/ink_x86_64.nexe"
+ }
+ },
+ "files": {}
+}
diff --git a/chromium/third_party/ink/prebuilt/ink_demo.js b/chromium/third_party/ink/prebuilt/ink_demo.js
new file mode 100644
index 00000000000..ca284edef4c
--- /dev/null
+++ b/chromium/third_party/ink/prebuilt/ink_demo.js
@@ -0,0 +1,15 @@
+(function() {
+ var config = new ink.embed.Config({
+ parentEl: document.querySelector('#ink-canvas'),
+ nativeClientManifestUrl: 'ink.nmf',
+ sengineType: 'makeSEngineInMemory'
+ });
+ ink.embed.EmbedComponent.execute(config, (embed) => {
+ // put the embed component on the window so it's easy to experiment in the console.
+ window.embed = embed;
+
+ // change the brush color
+ var brushModel = ink.BrushModel.getInstance(embed);
+ brushModel.setColor('#FF00FF');
+ });
+}());
diff --git a/chromium/third_party/ink/prebuilt/ink_lib_binary.js b/chromium/third_party/ink/prebuilt/ink_lib_binary.js
new file mode 100644
index 00000000000..7eda636307e
--- /dev/null
+++ b/chromium/third_party/ink/prebuilt/ink_lib_binary.js
@@ -0,0 +1,154 @@
+var g,aa="function"==typeof Object.defineProperties?Object.defineProperty:function(a,b,c){a!=Array.prototype&&a!=Object.prototype&&(a[b]=c.value)},ba="undefined"!=typeof window&&window===this?this:"undefined"!=typeof global&&null!=global?global:this,ca=function(a,b){if(b){var c=ba;a=a.split(".");for(var d=0;d<a.length-1;d++){var e=a[d];e in c||(c[e]={});c=c[e]}a=a[a.length-1];d=c[a];b=b(d);b!=d&&null!=b&&aa(c,a,{configurable:!0,writable:!0,value:b})}},da=function(){da=function(){};ba.Symbol||(ba.Symbol=
+ea)},ea=function(){var a=0;return function(b){return"jscomp_symbol_"+(b||"")+a++}}(),ha=function(){da();var a=ba.Symbol.iterator;a||(a=ba.Symbol.iterator=ba.Symbol("iterator"));"function"!=typeof Array.prototype[a]&&aa(Array.prototype,a,{configurable:!0,writable:!0,value:function(){return fa(this)}});ha=function(){}},fa=function(a){var b=0;return ia(function(){return b<a.length?{done:!1,value:a[b++]}:{done:!0}})},ia=function(a){ha();a={next:a};a[ba.Symbol.iterator]=function(){return this};return a},
+ja=function(a){ha();var b=a[Symbol.iterator];return b?b.call(a):fa(a)},k=function(a,b){return Object.prototype.hasOwnProperty.call(a,b)};
+ca("WeakMap",function(a){function b(a){k(a,d)||aa(a,d,{value:{}})}function c(a){var c=Object[a];c&&(Object[a]=function(a){b(a);return c(a)})}if(function(){if(!a||!Object.seal)return!1;try{var b=Object.seal({}),c=Object.seal({}),d=new a([[b,2],[c,3]]);if(2!=d.get(b)||3!=d.get(c))return!1;d.delete(b);d.set(c,4);return!d.has(b)&&4==d.get(c)}catch(C){return!1}}())return a;var d="$jscomp_hidden_"+Math.random();c("freeze");c("preventExtensions");c("seal");var e=0,f=function(a){this.a=(e+=Math.random()+
+1).toString();if(a){da();ha();a=ja(a);for(var b;!(b=a.next()).done;)b=b.value,this.set(b[0],b[1])}};f.prototype.set=function(a,c){b(a);if(!k(a,d))throw Error("WeakMap key fail: "+a);a[d][this.a]=c;return this};f.prototype.get=function(a){return k(a,d)?a[d][this.a]:void 0};f.prototype.has=function(a){return k(a,d)&&k(a[d],this.a)};f.prototype.delete=function(a){return k(a,d)&&k(a[d],this.a)?delete a[d][this.a]:!1};return f});
+ca("Map",function(a){if(function(){if(!a||!a.prototype.entries||"function"!=typeof Object.seal)return!1;try{var b=Object.seal({x:4}),c=new a(ja([[b,"s"]]));if("s"!=c.get(b)||1!=c.size||c.get({x:4})||c.set({x:4},"t")!=c||2!=c.size)return!1;var d=c.entries(),e=d.next();if(e.done||e.value[0]!=b||"s"!=e.value[1])return!1;e=d.next();return e.done||4!=e.value[0].x||"t"!=e.value[1]||!d.next().done?!1:!0}catch(N){return!1}}())return a;da();ha();var b=new WeakMap,c=function(a){this.f={};this.a=f();this.size=
+0;if(a){a=ja(a);for(var b;!(b=a.next()).done;)b=b.value,this.set(b[0],b[1])}};c.prototype.set=function(a,b){var c=d(this,a);c.list||(c.list=this.f[c.id]=[]);c.o?c.o.value=b:(c.o={next:this.a,D:this.a.D,head:this.a,key:a,value:b},c.list.push(c.o),this.a.D.next=c.o,this.a.D=c.o,this.size++);return this};c.prototype.delete=function(a){a=d(this,a);return a.o&&a.list?(a.list.splice(a.index,1),a.list.length||delete this.f[a.id],a.o.D.next=a.o.next,a.o.next.D=a.o.D,a.o.head=null,this.size--,!0):!1};c.prototype.clear=
+function(){this.f={};this.a=this.a.D=f();this.size=0};c.prototype.has=function(a){return!!d(this,a).o};c.prototype.get=function(a){return(a=d(this,a).o)&&a.value};c.prototype.entries=function(){return e(this,function(a){return[a.key,a.value]})};c.prototype.keys=function(){return e(this,function(a){return a.key})};c.prototype.values=function(){return e(this,function(a){return a.value})};c.prototype.forEach=function(a,b){for(var c=this.entries(),d;!(d=c.next()).done;)d=d.value,a.call(b,d[1],d[0],this)};
+c.prototype[Symbol.iterator]=c.prototype.entries;var d=function(a,c){var d=c&&typeof c;"object"==d||"function"==d?b.has(c)?d=b.get(c):(d=""+ ++h,b.set(c,d)):d="p_"+c;var e=a.f[d];if(e&&k(a.f,d))for(a=0;a<e.length;a++){var f=e[a];if(c!==c&&f.key!==f.key||c===f.key)return{id:d,list:e,index:a,o:f}}return{id:d,list:e,index:-1,o:void 0}},e=function(a,b){var c=a.a;return ia(function(){if(c){for(;c.head!=a.a;)c=c.D;for(;c.next!=c.head;)return c=c.next,{done:!1,value:b(c)};c=null}return{done:!0,value:void 0}})},
+f=function(){var a={};return a.D=a.next=a.head=a},h=0;return c});
+ca("Set",function(a){if(function(){if(!a||!a.prototype.entries||"function"!=typeof Object.seal)return!1;try{var b=Object.seal({x:4}),d=new a(ja([b]));if(!d.has(b)||1!=d.size||d.add(b)!=d||1!=d.size||d.add({x:4})!=d||2!=d.size)return!1;var e=d.entries(),f=e.next();if(f.done||f.value[0]!=b||f.value[1]!=b)return!1;f=e.next();return f.done||f.value[0]==b||4!=f.value[0].x||f.value[1]!=f.value[0]?!1:e.next().done}catch(h){return!1}}())return a;da();ha();var b=function(a){this.a=new Map;if(a){a=ja(a);for(var b;!(b=
+a.next()).done;)this.add(b.value)}this.size=this.a.size};b.prototype.add=function(a){this.a.set(a,a);this.size=this.a.size;return this};b.prototype.delete=function(a){a=this.a.delete(a);this.size=this.a.size;return a};b.prototype.clear=function(){this.a.clear();this.size=0};b.prototype.has=function(a){return this.a.has(a)};b.prototype.entries=function(){return this.a.entries()};b.prototype.values=function(){return this.a.values()};b.prototype.keys=b.prototype.values;b.prototype[Symbol.iterator]=b.prototype.values;
+b.prototype.forEach=function(a,b){var c=this;this.a.forEach(function(d){return a.call(b,d,d,c)})};return b});
+var l=this,m=function(a){return"string"==typeof a},ka=function(a,b){a=a.split(".");var c=l;a[0]in c||!c.execScript||c.execScript("var "+a[0]);for(var d;a.length&&(d=a.shift());)a.length||void 0===b?c[d]&&c[d]!==Object.prototype[d]?c=c[d]:c=c[d]={}:c[d]=b},la=function(){},n=function(a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==
+typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!=typeof a.call&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("call"))return"function"}else return"null";else if("function"==b&&"undefined"==typeof a.call)return"object";return b},ma=function(a){return"array"==n(a)},na=function(a){var b=typeof a;return"object"==b&&null!=a||"function"==b},oa="closure_uid_"+
+(1E9*Math.random()>>>0),pa=0,qa=function(a,b,c){return a.call.apply(a.bind,arguments)},ra=function(a,b,c){if(!a)throw Error();if(2<arguments.length){var d=Array.prototype.slice.call(arguments,2);return function(){var c=Array.prototype.slice.call(arguments);Array.prototype.unshift.apply(c,d);return a.apply(b,c)}}return function(){return a.apply(b,arguments)}},sa=function(a,b,c){Function.prototype.bind&&-1!=Function.prototype.bind.toString().indexOf("native code")?sa=qa:sa=ra;return sa.apply(null,arguments)},
+q=function(a,b){function c(){}c.prototype=b.prototype;a.R=b.prototype;a.prototype=new c;a.prototype.constructor=a;a.rd=function(a,c,f){for(var d=Array(arguments.length-2),e=2;e<arguments.length;e++)d[e-2]=arguments[e];return b.prototype[c].apply(a,d)}};var ta=function(a){if(Error.captureStackTrace)Error.captureStackTrace(this,ta);else{var b=Error().stack;b&&(this.stack=b)}a&&(this.message=String(a))};q(ta,Error);ta.prototype.name="CustomError";var ua;var va=function(a,b){a=a.split("%s");for(var c="",d=a.length-1,e=0;e<d;e++)c+=a[e]+(e<b.length?b[e]:"%s");ta.call(this,c+a[d])};q(va,ta);va.prototype.name="AssertionError";
+var wa=function(a,b,c,d){var e="Assertion failed";if(c){e+=": "+c;var f=d}else a&&(e+=": "+a,f=b);throw new va(""+e,f||[]);},r=function(a,b,c){a||wa("",null,b,Array.prototype.slice.call(arguments,2));return a},xa=function(a,b){throw new va("Failure"+(a?": "+a:""),Array.prototype.slice.call(arguments,1));},ya=function(a,b,c){"number"==typeof a||wa("Expected number but got %s: %s.",[n(a),a],b,Array.prototype.slice.call(arguments,2))},za=function(a,b,c){m(a)||wa("Expected string but got %s: %s.",[n(a),
+a],b,Array.prototype.slice.call(arguments,2));return a},Aa=function(a,b,c){na(a)||wa("Expected object but got %s: %s.",[n(a),a],b,Array.prototype.slice.call(arguments,2))};var Ba=Array.prototype.indexOf?function(a,b){r(null!=a.length);return Array.prototype.indexOf.call(a,b,void 0)}:function(a,b){if(m(a))return m(b)&&1==b.length?a.indexOf(b,0):-1;for(var c=0;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1},Ca=Array.prototype.forEach?function(a,b,c){r(null!=a.length);Array.prototype.forEach.call(a,b,c)}:function(a,b,c){for(var d=a.length,e=m(a)?a.split(""):a,f=0;f<d;f++)f in e&&b.call(c,e[f],f,a)},Da=Array.prototype.filter?function(a,b){r(null!=a.length);return Array.prototype.filter.call(a,
+b,void 0)}:function(a,b){for(var c=a.length,d=[],e=0,f=m(a)?a.split(""):a,h=0;h<c;h++)if(h in f){var p=f[h];b.call(void 0,p,h,a)&&(d[e++]=p)}return d},Fa=function(a,b){b=Ba(a,b);var c;(c=0<=b)&&Ea(a,b);return c},Ea=function(a,b){r(null!=a.length);Array.prototype.splice.call(a,b,1)},Ga=function(a,b){for(var c=1;c<arguments.length;c++){var d=arguments[c],e=d,f=n(e);if("array"==f||"object"==f&&"number"==typeof e.length){e=a.length||0;f=d.length||0;a.length=e+f;for(var h=0;h<f;h++)a[e+h]=d[h]}else a.push(d)}},
+Ia=function(a,b,c,d){r(null!=a.length);Array.prototype.splice.apply(a,Ha(arguments,1))},Ha=function(a,b,c){r(null!=a.length);return 2>=arguments.length?Array.prototype.slice.call(a,b):Array.prototype.slice.call(a,b,c)},Ka=function(a,b){a.sort(b||Ja)},Ja=function(a,b){return a>b?1:a<b?-1:0};var La=String.prototype.trim?function(a){return a.trim()}:function(a){return/^[\s\xa0]*([\s\S]*?)[\s\xa0]*$/.exec(a)[1]},Ta=function(a){if(!Ma.test(a))return a;-1!=a.indexOf("&")&&(a=a.replace(Na,"&amp;"));-1!=a.indexOf("<")&&(a=a.replace(Oa,"&lt;"));-1!=a.indexOf(">")&&(a=a.replace(Pa,"&gt;"));-1!=a.indexOf('"')&&(a=a.replace(Qa,"&quot;"));-1!=a.indexOf("'")&&(a=a.replace(Ra,"&#39;"));-1!=a.indexOf("\x00")&&(a=a.replace(Sa,"&#0;"));return a},Na=/&/g,Oa=/</g,Pa=/>/g,Qa=/"/g,Ra=/'/g,Sa=/\x00/g,Ma=
+/[\x00&<>"']/,Va=function(a,b){var c=0;a=La(String(a)).split(".");b=La(String(b)).split(".");for(var d=Math.max(a.length,b.length),e=0;0==c&&e<d;e++){var f=a[e]||"",h=b[e]||"";do{f=/(\d*)(\D*)(.*)/.exec(f)||["","","",""];h=/(\d*)(\D*)(.*)/.exec(h)||["","","",""];if(0==f[0].length&&0==h[0].length)break;c=Ua(0==f[1].length?0:parseInt(f[1],10),0==h[1].length?0:parseInt(h[1],10))||Ua(0==f[2].length,0==h[2].length)||Ua(f[2],h[2]);f=f[3];h=h[3]}while(0==c)}return c},Ua=function(a,b){return a<b?-1:a>b?1:
+0};var Wa;a:{var Xa=l.navigator;if(Xa){var Ya=Xa.userAgent;if(Ya){Wa=Ya;break a}}Wa=""}var t=function(a){return-1!=Wa.indexOf(a)};var Za=function(a){var b=[],c=0,d;for(d in a)b[c++]=a[d];return b},$a="constructor hasOwnProperty isPrototypeOf propertyIsEnumerable toLocaleString toString valueOf".split(" "),ab=function(a,b){for(var c,d,e=1;e<arguments.length;e++){d=arguments[e];for(c in d)a[c]=d[c];for(var f=0;f<$a.length;f++)c=$a[f],Object.prototype.hasOwnProperty.call(d,c)&&(a[c]=d[c])}};var bb=function(a){bb[" "](a);return a};bb[" "]=la;var cb=function(a,b,c){return Object.prototype.hasOwnProperty.call(a,b)?a[b]:a[b]=c(b)};var db=t("Opera"),eb=t("Trident")||t("MSIE"),fb=t("Edge"),gb=t("Gecko")&&!(-1!=Wa.toLowerCase().indexOf("webkit")&&!t("Edge"))&&!(t("Trident")||t("MSIE"))&&!t("Edge"),hb=-1!=Wa.toLowerCase().indexOf("webkit")&&!t("Edge"),ib=function(){var a=l.document;return a?a.documentMode:void 0},jb;
+a:{var kb="",lb=function(){var a=Wa;if(gb)return/rv:([^\);]+)(\)|;)/.exec(a);if(fb)return/Edge\/([\d\.]+)/.exec(a);if(eb)return/\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/.exec(a);if(hb)return/WebKit\/(\S+)/.exec(a);if(db)return/(?:Version)[ \/]?(\S+)/.exec(a)}();lb&&(kb=lb?lb[1]:"");if(eb){var mb=ib();if(null!=mb&&mb>parseFloat(kb)){jb=String(mb);break a}}jb=kb}var nb=jb,ob={},pb;var qb=l.document;pb=qb&&eb?ib()||("CSS1Compat"==qb.compatMode?parseInt(nb,10):5):void 0;var rb=Object.freeze||function(a){return a};var sb=function(){this.la=this.la;this.Ea=this.Ea};sb.prototype.la=!1;var tb=function(a){if(a.classList)return a.classList;a=a.className;return m(a)&&a.match(/\S+/g)||[]},ub=function(a){a.classList?a=a.classList.contains("fullscreen"):(a=tb(a),a=0<=Ba(a,"fullscreen"));return a},vb=function(a){a.classList?a.classList.remove("fullscreen"):ub(a)&&(a.className=Da(tb(a),function(a){return"fullscreen"!=a}).join(" "))};var xb=function(){this.Da=wb};xb.prototype.toString=function(){return"TrustedResourceUrl{}"};var wb={};var zb=function(){this.ca="";this.Ba=yb};zb.prototype.toString=function(){return"SafeUrl{"+this.ca+"}"};
+var Ab=/^(?:audio\/(?:3gpp|3gpp2|aac|midi|mp4|mpeg|ogg|x-m4a|x-wav|webm)|image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|text\/csv|video\/(?:mpeg|mp4|ogg|webm))$/i,Cb=function(a){if(Ab.test(a.type)){var b=void 0!==l.URL&&void 0!==l.URL.createObjectURL?l.URL:void 0!==l.webkitURL&&void 0!==l.webkitURL.createObjectURL?l.webkitURL:void 0!==l.createObjectURL?l:null;if(null==b)throw Error("This browser doesn't seem to support blob URLs");a=b.createObjectURL(a)}else a="about:invalid#zClosurez";return Bb(a)},Db=
+/^data:([^;,]*);base64,[a-z0-9+\/]+=*$/i,Eb=function(a){var b=a.match(Db);b=b&&Ab.test(b[1]);return Bb(b?a:"about:invalid#zClosurez")},yb={},Bb=function(a){var b=new zb;b.ca=a;return b};Bb("about:blank");var Fb=function(a,b){this.width=a;this.height=b};g=Fb.prototype;g.clone=function(){return new Fb(this.width,this.height)};g.toString=function(){return"("+this.width+" x "+this.height+")"};g.aspectRatio=function(){return this.width/this.height};g.ceil=function(){this.width=Math.ceil(this.width);this.height=Math.ceil(this.height);return this};g.floor=function(){this.width=Math.floor(this.width);this.height=Math.floor(this.height);return this};
+g.round=function(){this.width=Math.round(this.width);this.height=Math.round(this.height);return this};var Ib=function(a){return a?new Gb(Hb(a)):ua||(ua=new Gb)},Jb=function(a){return m(a)?document.getElementById(a):a},Hb=function(a){r(a,"Node cannot be null or undefined.");return 9==a.nodeType?a:a.ownerDocument||a.document},Gb=function(a){this.a=a||l.document||document};Gb.prototype.B=function(){return m(void 0)?this.a.getElementById(void 0):void 0};var Kb;(Kb=!eb)||(Kb=9<=Number(pb));var Lb=Kb,Mb=eb&&!cb(ob,"9",function(){return 0<=Va(nb,"9")}),Nb=function(){if(!l.addEventListener||!Object.defineProperty)return!1;var a=!1,b=Object.defineProperty({},"passive",{get:function(){a=!0}});l.addEventListener("test",la,b);l.removeEventListener("test",la,b);return a}();var v=function(a,b){this.type=a;this.a=this.target=b;this.f=!1;this.va=!0};v.prototype.h=function(){this.f=!0;this.va=!1};var Ob=function(a,b){v.call(this,a?a.type:"");this.relatedTarget=this.a=this.target=null;this.button=this.screenY=this.screenX=this.clientY=this.clientX=0;this.key="";this.metaKey=this.shiftKey=this.altKey=this.ctrlKey=!1;this.pointerId=0;this.pointerType="";this.j=null;a&&this.init(a,b)};q(Ob,v);var Pb=rb({2:"touch",3:"pen",4:"mouse"});
+Ob.prototype.init=function(a,b){var c=this.type=a.type,d=a.changedTouches?a.changedTouches[0]:null;this.target=a.target||a.srcElement;this.a=b;if(b=a.relatedTarget){if(gb){a:{try{bb(b.nodeName);var e=!0;break a}catch(f){}e=!1}e||(b=null)}}else"mouseover"==c?b=a.fromElement:"mouseout"==c&&(b=a.toElement);this.relatedTarget=b;null===d?(this.clientX=void 0!==a.clientX?a.clientX:a.pageX,this.clientY=void 0!==a.clientY?a.clientY:a.pageY,this.screenX=a.screenX||0,this.screenY=a.screenY||0):(this.clientX=
+void 0!==d.clientX?d.clientX:d.pageX,this.clientY=void 0!==d.clientY?d.clientY:d.pageY,this.screenX=d.screenX||0,this.screenY=d.screenY||0);this.button=a.button;this.key=a.key||"";this.ctrlKey=a.ctrlKey;this.altKey=a.altKey;this.shiftKey=a.shiftKey;this.metaKey=a.metaKey;this.pointerId=a.pointerId||0;this.pointerType=m(a.pointerType)?a.pointerType:Pb[a.pointerType]||"";this.j=a;a.defaultPrevented&&this.h()};
+Ob.prototype.h=function(){Ob.R.h.call(this);var a=this.j;if(a.preventDefault)a.preventDefault();else if(a.returnValue=!1,Mb)try{if(a.ctrlKey||112<=a.keyCode&&123>=a.keyCode)a.keyCode=-1}catch(b){}};var Qb="closure_listenable_"+(1E6*Math.random()|0),Rb=0;var Sb=function(a,b,c,d,e){this.listener=a;this.a=null;this.src=b;this.type=c;this.capture=!!d;this.aa=e;this.key=++Rb;this.P=this.Z=!1},Tb=function(a){a.P=!0;a.listener=null;a.a=null;a.src=null;a.aa=null};var Ub=function(a){this.src=a;this.a={};this.f=0};Ub.prototype.add=function(a,b,c,d,e){var f=a.toString();a=this.a[f];a||(a=this.a[f]=[],this.f++);var h=Vb(a,b,d,e);-1<h?(b=a[h],c||(b.Z=!1)):(b=new Sb(b,this.src,f,!!d,e),b.Z=c,a.push(b));return b};var Wb=function(a,b){var c=b.type;c in a.a&&Fa(a.a[c],b)&&(Tb(b),0==a.a[c].length&&(delete a.a[c],a.f--))},Vb=function(a,b,c,d){for(var e=0;e<a.length;++e){var f=a[e];if(!f.P&&f.listener==b&&f.capture==!!c&&f.aa==d)return e}return-1};var Xb="closure_lm_"+(1E6*Math.random()|0),Yb={},Zb=0,ac=function(a,b,c,d,e){if(d&&d.once)return $b(a,b,c,d,e);if(ma(b)){for(var f=0;f<b.length;f++)ac(a,b[f],c,d,e);return null}c=bc(c);a&&a[Qb]?(d=na(d)?!!d.capture:!!d,cc(a),a=a.w.add(String(b),c,!1,d,e)):a=dc(a,b,c,!1,d,e);return a},dc=function(a,b,c,d,e,f){if(!b)throw Error("Invalid event type");var h=na(e)?!!e.capture:!!e,p=ec(a);p||(a[Xb]=p=new Ub(a));c=p.add(b,c,d,h,f);if(c.a)return c;d=fc();c.a=d;d.src=a;d.listener=c;if(a.addEventListener)Nb||
+(e=h),void 0===e&&(e=!1),a.addEventListener(b.toString(),d,e);else if(a.attachEvent)a.attachEvent(gc(b.toString()),d);else if(a.addListener&&a.removeListener)r("change"===b,"MediaQueryList only has a change event"),a.addListener(d);else throw Error("addEventListener and attachEvent are unavailable.");Zb++;return c},fc=function(){var a=hc,b=Lb?function(c){return a.call(b.src,b.listener,c)}:function(c){c=a.call(b.src,b.listener,c);if(!c)return c};return b},$b=function(a,b,c,d,e){if(ma(b)){for(var f=
+0;f<b.length;f++)$b(a,b[f],c,d,e);return null}c=bc(c);return a&&a[Qb]?a.w.add(String(b),c,!0,na(d)?!!d.capture:!!d,e):dc(a,b,c,!0,d,e)},ic=function(a,b,c,d,e){if(ma(b))for(var f=0;f<b.length;f++)ic(a,b[f],c,d,e);else(d=na(d)?!!d.capture:!!d,c=bc(c),a&&a[Qb])?(a=a.w,b=String(b).toString(),b in a.a&&(f=a.a[b],c=Vb(f,c,d,e),-1<c&&(Tb(f[c]),Ea(f,c),0==f.length&&(delete a.a[b],a.f--)))):a&&(a=ec(a))&&(b=a.a[b.toString()],a=-1,b&&(a=Vb(b,c,d,e)),(c=-1<a?b[a]:null)&&jc(c))},jc=function(a){if("number"!=typeof a&&
+a&&!a.P){var b=a.src;if(b&&b[Qb])Wb(b.w,a);else{var c=a.type,d=a.a;b.removeEventListener?b.removeEventListener(c,d,a.capture):b.detachEvent?b.detachEvent(gc(c),d):b.addListener&&b.removeListener&&b.removeListener(d);Zb--;(c=ec(b))?(Wb(c,a),0==c.f&&(c.src=null,b[Xb]=null)):Tb(a)}}},gc=function(a){return a in Yb?Yb[a]:Yb[a]="on"+a},lc=function(a,b,c,d){var e=!0;if(a=ec(a))if(b=a.a[b.toString()])for(b=b.concat(),a=0;a<b.length;a++){var f=b[a];f&&f.capture==c&&!f.P&&(f=kc(f,d),e=e&&!1!==f)}return e},
+kc=function(a,b){var c=a.listener,d=a.aa||a.src;a.Z&&jc(a);return c.call(d,b)},hc=function(a,b){if(a.P)return!0;if(!Lb){if(!b)a:{b=["window","event"];for(var c=l,d=0;d<b.length;d++)if(c=c[b[d]],null==c){b=null;break a}b=c}d=b;b=new Ob(d,this);c=!0;if(!(0>d.keyCode||void 0!=d.returnValue)){a:{var e=!1;if(0==d.keyCode)try{d.keyCode=-1;break a}catch(h){e=!0}if(e||void 0==d.returnValue)d.returnValue=!0}d=[];for(e=b.a;e;e=e.parentNode)d.push(e);a=a.type;for(e=d.length-1;0<=e;e--){b.a=d[e];var f=lc(d[e],
+a,!0,b);c=c&&f}for(e=0;e<d.length;e++)b.a=d[e],f=lc(d[e],a,!1,b),c=c&&f}return c}return kc(a,new Ob(b,this))},ec=function(a){a=a[Xb];return a instanceof Ub?a:null},mc="__closure_events_fn_"+(1E9*Math.random()>>>0),bc=function(a){r(a,"Listener can not be null.");if("function"==n(a))return a;r(a.handleEvent,"An object listener must have handleEvent method.");a[mc]||(a[mc]=function(b){return a.handleEvent(b)});return a[mc]};var nc=function(a){sb.call(this);this.a=a;this.f={}};q(nc,sb);var oc=[],pc=function(a,b,c,d){ma(c)||(c&&(oc[0]=c.toString()),c=oc);for(var e=0;e<c.length;e++){var f=ac(b,c[e],d||a.handleEvent,!1,a.a||a);if(!f)break;a.f[f.key]=f}},rc=function(a,b,c){qc(a,b,"o",c,void 0)},qc=function(a,b,c,d,e,f){if(ma(c))for(var h=0;h<c.length;h++)qc(a,b,c[h],d,e,f);else(b=$b(b,c,d||a.handleEvent,e,f||a.a||a))&&(a.f[b.key]=b)};
+nc.prototype.handleEvent=function(){throw Error("EventHandler.handleEvent not implemented");};var w=function(){sb.call(this);this.w=new Ub(this);this.Aa=this;this.V=null};q(w,sb);w.prototype[Qb]=!0;w.prototype.ba=function(a){this.V=a};w.prototype.removeEventListener=function(a,b,c,d){ic(this,a,b,c,d)};
+var x=function(a,b){cc(a);var c=a.V;if(c){var d=[];for(var e=1;c;c=c.V)d.push(c),r(1E3>++e,"infinite loop")}a=a.Aa;c=b.type||b;m(b)?b=new v(b,a):b instanceof v?b.target=b.target||a:(e=b,b=new v(c,a),ab(b,e));e=!0;if(d)for(var f=d.length-1;0<=f;f--){var h=b.a=d[f];e=sc(h,c,!0,b)&&e}h=b.a=a;e=sc(h,c,!0,b)&&e;e=sc(h,c,!1,b)&&e;if(d)for(f=0;f<d.length;f++)h=b.a=d[f],e=sc(h,c,!1,b)&&e;return e},sc=function(a,b,c,d){b=a.w.a[String(b)];if(!b)return!0;b=b.concat();for(var e=!0,f=0;f<b.length;++f){var h=b[f];
+if(h&&!h.P&&h.capture==c){var p=h.listener,u=h.aa||h.src;h.Z&&Wb(a.w,h);e=!1!==p.call(u,d)&&e}}return e&&0!=d.va},cc=function(a){r(a.w,"Event target is not initialized. Did you call the superclass (goog.events.EventTarget) constructor?")};var y=function(a,b){this.a=a|0;this.f=b|0},tc={},uc={},z=function(a){return cb(tc,a,function(a){return new y(a,0>a?-1:0)})},vc=function(a){var b=a|0;r(a===b,"value should be a 32-bit integer");return-128<=b&&128>b?z(b):new y(b,0>b?-1:0)},E=function(a){return isNaN(a)?z(0):a<=-wc?B():a+1>=wc?xc():0>a?D(E(-a)):new y(a%4294967296|0,a/4294967296|0)},F=function(a,b){return new y(a,b)},yc=function(a,b){if(0==a.length)throw Error("number format error: empty string");b=b||10;if(2>b||36<b)throw Error("radix out of range: "+
+b);if("-"==a.charAt(0))return D(yc(a.substring(1),b));if(0<=a.indexOf("-"))throw Error('number format error: interior "-" character: '+a);for(var c=E(Math.pow(b,8)),d=z(0),e=0;e<a.length;e+=8){var f=Math.min(8,a.length-e),h=parseInt(a.substring(e,e+f),b);8>f?(f=E(Math.pow(b,f)),d=G(d,f).add(E(h))):(d=G(d,c),d=d.add(E(h)))}return d},wc=4294967296*4294967296/2,xc=function(){return cb(uc,1,function(){return F(-1,2147483647)})},B=function(){return cb(uc,2,function(){return F(0,-2147483648)})},zc=function(){return cb(uc,
+6,function(){return vc(16777216)})};y.prototype.toString=function(a){a=a||10;if(2>a||36<a)throw Error("radix out of range: "+a);if(Ac(this))return"0";if(0>this.f){if(this.s(B())){var b=E(a),c=Bc(this,b);b=Cc(G(c,b),this);return c.toString(a)+b.a.toString(a)}return"-"+D(this).toString(a)}c=E(Math.pow(a,6));b=this;for(var d="";;){var e=Bc(b,c),f=(Cc(b,G(e,c)).a>>>0).toString(a);b=e;if(Ac(b))return f+d;for(;6>f.length;)f="0"+f;d=""+f+d}};
+var Dc=function(a){return 0<=a.a?a.a:4294967296+a.a},Ac=function(a){return 0==a.f&&0==a.a};y.prototype.s=function(a){return this.f==a.f&&this.a==a.a};var Fc=function(a){var b=zc();return 0>Ec(a,b)},Ec=function(a,b){if(a.s(b))return 0;var c=0>a.f,d=0>b.f;return c&&!d?-1:!c&&d?1:0>Cc(a,b).f?-1:1},D=function(a){return a.s(B())?B():F(~a.a,~a.f).add(z(1))};
+y.prototype.add=function(a){var b=this.f>>>16,c=this.f&65535,d=this.a>>>16,e=a.f>>>16,f=a.f&65535,h=a.a>>>16;a=(this.a&65535)+(a.a&65535);h=(a>>>16)+(d+h);d=h>>>16;d+=c+f;b=(d>>>16)+(b+e)&65535;return F((h&65535)<<16|a&65535,b<<16|d&65535)};
+var Cc=function(a,b){return a.add(D(b))},G=function(a,b){if(Ac(a)||Ac(b))return z(0);if(a.s(B()))return 1==(b.a&1)?B():z(0);if(b.s(B()))return 1==(a.a&1)?B():z(0);if(0>a.f)return 0>b.f?G(D(a),D(b)):D(G(D(a),b));if(0>b.f)return D(G(a,D(b)));if(Fc(a)&&Fc(b))return E((4294967296*a.f+Dc(a))*(4294967296*b.f+Dc(b)));var c=a.f>>>16,d=a.f&65535,e=a.a>>>16;a=a.a&65535;var f=b.f>>>16,h=b.f&65535,p=b.a>>>16;b=b.a&65535;var u=a*b;var C=(u>>>16)+e*b;var A=C>>>16;C=(C&65535)+a*p;A+=C>>>16;A+=d*b;var N=A>>>16;A=
+(A&65535)+e*p;N+=A>>>16;A=(A&65535)+a*h;N=N+(A>>>16)+(c*b+d*p+e*h+a*f)&65535;return F((C&65535)<<16|u&65535,N<<16|A&65535)},Bc=function(a,b){if(Ac(b))throw Error("division by zero");if(Ac(a))return z(0);if(a.s(B())){if(b.s(z(1))||b.s(z(-1)))return B();if(b.s(B()))return z(1);var c=1;if(0==c)c=a;else{var d=a.f;c=32>c?F(a.a>>>c|d<<32-c,d>>c):F(d>>c-32,0<=d?0:-1)}c=Gc(Bc(c,b),1);if(c.s(z(0)))return 0>b.f?z(1):z(-1);a=Cc(a,G(b,c));return c.add(Bc(a,b))}if(b.s(B()))return z(0);if(0>a.f)return 0>b.f?Bc(D(a),
+D(b)):D(Bc(D(a),b));if(0>b.f)return D(Bc(a,D(b)));for(d=z(0);0<=Ec(a,b);){c=Math.max(1,Math.floor((4294967296*a.f+Dc(a))/(4294967296*b.f+Dc(b))));var e=Math.ceil(Math.log(c)/Math.LN2);e=48>=e?1:Math.pow(2,e-48);for(var f=E(c),h=G(f,b);0>h.f||0<Ec(h,a);)c-=e,f=E(c),h=G(f,b);Ac(f)&&(f=z(1));d=d.add(f);a=Cc(a,h)}return d};y.prototype.and=function(a){return F(this.a&a.a,this.f&a.f)};y.prototype.or=function(a){return F(this.a|a.a,this.f|a.f)};y.prototype.xor=function(a){return F(this.a^a.a,this.f^a.f)};
+var Gc=function(a,b){b&=63;if(0==b)return a;var c=a.a;return 32>b?F(c<<b,a.f<<b|c>>>32-b):F(0,c<<b-32)},Hc=function(a,b){b&=63;if(0==b)return a;var c=a.f;return 32>b?F(a.a>>>b|c<<32-b,c>>>b):32==b?F(c,0):F(c>>>b-32,0)};var Ic=function(a,b){this.ea=a;this.a={};for(a=0;a<b.length;a++){var c=b[a];this.a[c.a]=c}},Jc=function(a){a=Za(a.a);Ka(a,function(a,c){return a.a-c.a});return a},Kc=function(a,b){r(!/[^0-9]/.test(b));return a.a[parseInt(b,10)]||null};var Lc=function(a,b,c){this.i=a;r(!/[^0-9]/.test(b));this.a=b;this.m=!!c.fa;this.h=!!c.l;this.f=c.b;this.j=c.type;this.w=!1;switch(this.f){case 3:case 4:case 6:case 16:case 18:case 2:case 1:this.w=!0}},Mc=function(a){return 11==a.f||10==a.f};var H=function(){this.u={};this.f=this.c().a;this.a=null};H.prototype.has=function(a){r(a.i.prototype.c()==this.c(),"The current message does not contain the given field");return null!=this.u[a.a]};var Nc=function(a,b){r(b.i.prototype.c()==a.c(),"The current message does not contain the given field");b=b.a;return a.f[b].h?null!=a.u[b]?a.u[b].length:0:null!=a.u[b]?1:0};
+H.prototype.get=function(a,b){r(a.i.prototype.c()==this.c(),"The current message does not contain the given field");return Oc(this,a.a,b)};H.prototype.set=function(a,b){r(a.i.prototype.c()==this.c(),"The current message does not contain the given field");I(this,a.a,b)};H.prototype.add=function(a,b){r(a.i.prototype.c()==this.c(),"The current message does not contain the given field");Pc(this,a.a,b)};
+H.prototype.s=function(a){if(!a||this.constructor!=a.constructor)return!1;for(var b=Jc(this.c()),c=0;c<b.length;c++){var d=b[c],e=d.a;if(null!=this.u[e]!=(null!=a.u[e]))return!1;if(null!=this.u[e]){var f=Mc(d),h=Qc(this,e);e=Qc(a,e);if(d.h){if(h.length!=e.length)return!1;for(d=0;d<h.length;d++){var p=h[d],u=e[d];if(f?!p.s(u):p!=u)return!1}}else if(f?!h.s(e):h!=e)return!1}}return!0};
+var Rc=function(a,b){r(a.constructor==b.constructor,"The source message must have the same type.");for(var c=Jc(a.c()),d=0;d<c.length;d++){var e=c[d],f=e.a;if(null!=b.u[f]){a.a&&delete a.a[e.a];var h=Mc(e);if(e.h){e=Qc(b,f)||[];for(var p=0;p<e.length;p++)Pc(a,f,h?e[p].clone():e[p])}else e=Qc(b,f),h?(h=Qc(a,f))?Rc(h,e):I(a,f,e.clone()):I(a,f,e)}}};
+H.prototype.clone=function(){var a=new this.constructor;r(a.constructor==this.constructor,"The source message must have the same type.");a!=this&&(a.u={},a.a&&(a.a={}),Rc(a,this));return a};
+var Qc=function(a,b){a=a.u[b];return null==a?null:a},Oc=function(a,b,c){var d=Qc(a,b);return a.f[b].h?(a=c||0,r(0<=a&&a<d.length,"Given index %s is out of bounds. Repeated field length: %s",a,d.length),d[a]):d},I=function(a,b,c){var d=a.f[b];14==d.f?ya(c):r(Object(c).constructor==d.j);a.u[b]=c;a.a&&(a.a[b]=c)},Pc=function(a,b,c){var d=a.f[b];14==d.f?ya(c):r(Object(c).constructor==d.j);a.u[b]||(a.u[b]=[]);a.u[b].push(c);a.a&&delete a.a[b]},J=function(a,b){var c=[],d;for(d in b)0!=d&&c.push(new Lc(a,
+d,b[d]));return new Ic(a,c)};var Sc=function(){};Sc.prototype.j=function(a,b){Mc(a)&&K(this,b)};Sc.prototype.h=function(a,b){if(Mc(a))return b instanceof H||(a=new (a.j.prototype.c().ea),Tc(this,a,b),r(a instanceof H),b=a),b;if(14==a.f)return m(b)&&Uc.test(b)&&(a=Number(b),0<a)?a:b;if(!a.w)return b;a=a.j;if(a===String){if("number"==typeof b)return String(b)}else if(a===Number&&m(b)&&("Infinity"===b||"-Infinity"===b||"NaN"===b||Uc.test(b)))return Number(b);return b};var Uc=/^-?[0-9]+$/;var Vc={sd:!0},Wc={vd:!0},Xc={ud:!0},Yc={td:!0},L=function(){throw Error("Do not instantiate directly");};L.prototype.a=null;L.prototype.toString=function(){return this.T};var Zc=function(){L.call(this)};q(Zc,L);Zc.prototype.H=Vc;var $c=function(){L.call(this)};q($c,L);$c.prototype.H=Wc;$c.prototype.a=1;var ad=function(){L.call(this)};q(ad,L);ad.prototype.H=Xc;ad.prototype.a=1;var ed=function(a,b){r(a,"Soy template may not be null.");b=a(b||bd,void 0,void 0);a=Ib().a.createElement("DIV");b=cd(b);var c=b.match(dd);r(!c,"This template starts with a %s, which cannot be a child of a <div>, as required by soy internals. Consider using goog.soy.renderElement instead.\nTemplate output: %s",c&&c[0],b);a.innerHTML=b;1==a.childNodes.length&&(b=a.firstChild,1==b.nodeType&&(a=b));return a},cd=function(a){if(!na(a))return String(a);if(a instanceof L){if(a.H===Vc)return za(a.T);if(a.H===
+Yc)return Ta(a.T)}xa("Soy template output is unsafe for use as HTML: "+a);return"zSoyz"},dd=/^<(body|caption|col|colgroup|head|html|tr|td|th|tbody|thead|tfoot)>/i,bd={};var fd=function(){};fd.a=void 0;fd.S=function(){return fd.a?fd.a:fd.a=new fd};fd.prototype.a=0;var M=function(a){w.call(this);this.U=a||Ib();this.O=null;this.A=!1;this.f=null;this.ja=void 0;this.N=this.h=this.j=null};q(M,w);M.prototype.na=fd.S();M.prototype.B=function(){return this.f};var gd=function(a){a.ja||(a.ja=new nc(a));return r(a.ja)},hd=function(a,b){if(a==b)throw Error("Unable to set parent component");var c;if(c=b&&a.j&&a.O){var d=a.j;c=a.O;d.N&&c?(d=d.N,c=(null!==d&&c in d?d[c]:void 0)||null):c=null}if(c&&a.j!=b)throw Error("Unable to set parent component");a.j=b;M.R.ba.call(a,b)};
+M.prototype.ba=function(a){if(this.j&&this.j!=a)throw Error("Method not supported");M.R.ba.call(this,a)};M.prototype.$=function(){this.f=this.U.a.createElement("DIV")};M.prototype.render=function(a){if(this.A)throw Error("Component already rendered");this.f||this.$();a?a.insertBefore(this.f,null):this.U.a.body.appendChild(this.f);this.j&&!this.j.A||this.v()};M.prototype.v=function(){this.A=!0;id(this,function(a){!a.A&&a.B()&&a.v()})};
+var jd=function(a,b){var c=a.h?a.h.length:0;r(!!b,"Provided element must not be null.");if(b.A&&!a.A)throw Error("Component already rendered");if(0>c||c>(a.h?a.h.length:0))throw Error("Child component index out of bounds");a.N&&a.h||(a.N={},a.h=[]);if(b.j==a){var d=b.O||(b.O=":"+(b.na.a++).toString(36));a.N[d]=b;Fa(a.h,b)}else{d=a.N;var e=b.O||(b.O=":"+(b.na.a++).toString(36));if(null!==d&&e in d)throw Error('The object already contains the key "'+e+'"');d[e]=b}hd(b,a);Ia(a.h,c,0,b);b.A&&a.A&&b.j==
+a?(a=a.f,c=a.childNodes[c]||null,c!=b.B()&&a.insertBefore(b.B(),c)):a.A&&!b.A&&b.f&&b.f.parentNode&&1==b.f.parentNode.nodeType&&b.v()},id=function(a,b){a.h&&Ca(a.h,b,void 0)};var kd=function(a){function b(a){this.T=a}b.prototype=a.prototype;return function(a,d){a=new b(String(a));void 0!==d&&(a.a=d);return a}}(Zc);(function(a){function b(a){this.T=a}b.prototype=a.prototype;return function(a,d){a=String(a);if(!a)return"";a=new b(a);void 0!==d&&(a.a=d);return a}})(Zc);
+var pd=function(a){return null!=a&&a.H===Vc?(r(a.constructor===Zc),String(String(a.T).replace(ld,"").replace(md,"&lt;")).replace(nd,od)):Ta(String(a))},qd=function(a,b,c){a||(a=c instanceof Function?c.displayName||c.name||"unknown type name":c instanceof Object?c.constructor.displayName||c.constructor.name||Object.prototype.toString.call(c):null===c?"null":typeof c,xa("expected param "+b+" of type !goog.soy.data.SanitizedContent|string"+(", but got "+a)+"."));return c},rd={"\x00":"&#0;","\t":"&#9;",
+"\n":"&#10;","\x0B":"&#11;","\f":"&#12;","\r":"&#13;"," ":"&#32;",'"':"&quot;","&":"&amp;","'":"&#39;","-":"&#45;","/":"&#47;","<":"&lt;","=":"&#61;",">":"&gt;","`":"&#96;","\u0085":"&#133;","\u00a0":"&#160;","\u2028":"&#8232;","\u2029":"&#8233;"},od=function(a){return rd[a]},sd={"\x00":"%00","\u0001":"%01","\u0002":"%02","\u0003":"%03","\u0004":"%04","\u0005":"%05","\u0006":"%06","\u0007":"%07","\b":"%08","\t":"%09","\n":"%0A","\x0B":"%0B","\f":"%0C","\r":"%0D","\u000e":"%0E","\u000f":"%0F","\u0010":"%10",
+"\u0011":"%11","\u0012":"%12","\u0013":"%13","\u0014":"%14","\u0015":"%15","\u0016":"%16","\u0017":"%17","\u0018":"%18","\u0019":"%19","\u001a":"%1A","\u001b":"%1B","\u001c":"%1C","\u001d":"%1D","\u001e":"%1E","\u001f":"%1F"," ":"%20",'"':"%22","'":"%27","(":"%28",")":"%29","<":"%3C",">":"%3E","\\":"%5C","{":"%7B","}":"%7D","\u007f":"%7F","\u0085":"%C2%85","\u00a0":"%C2%A0","\u2028":"%E2%80%A8","\u2029":"%E2%80%A9","\uff01":"%EF%BC%81","\uff03":"%EF%BC%83","\uff04":"%EF%BC%84","\uff06":"%EF%BC%86",
+"\uff07":"%EF%BC%87","\uff08":"%EF%BC%88","\uff09":"%EF%BC%89","\uff0a":"%EF%BC%8A","\uff0b":"%EF%BC%8B","\uff0c":"%EF%BC%8C","\uff0f":"%EF%BC%8F","\uff1a":"%EF%BC%9A","\uff1b":"%EF%BC%9B","\uff1d":"%EF%BC%9D","\uff1f":"%EF%BC%9F","\uff20":"%EF%BC%A0","\uff3b":"%EF%BC%BB","\uff3d":"%EF%BC%BD"},td=function(a){return sd[a]},nd=/[\x00\x22\x27\x3c\x3e]/g,ud=/[\x00- \x22\x27-\x29\x3c\x3e\\\x7b\x7d\x7f\x85\xa0\u2028\u2029\uff01\uff03\uff04\uff06-\uff0c\uff0f\uff1a\uff1b\uff1d\uff1f\uff20\uff3b\uff3d]/g,
+vd=/^(?![^#?]*\/(?:\.|%2E){2}(?:[\/?#]|$))(?:(?:https?|mailto):|[^&:\/?#]*(?:[\/?#]|$))/i,ld=/<(?:!|\/?([a-zA-Z][a-zA-Z0-9:\-]*))(?:[^>'"]|"[^"]*"|'[^']*')*>/g,md=/</g;var O=function(){H.call(this)};q(O,H);var wd=null,xd={fd:0,jc:1,KEEP:2,Gb:3,ic:4},yd={kd:0,Sb:1,Uc:2,dc:3,lc:4},P=function(){H.call(this)};q(P,H);var zd=null,Ad={cd:0,Mb:1,OPENED:2,Bc:3,pc:4,Ac:5,Db:6,ad:7,Lc:8,Jb:9,Pc:10,yb:11,gc:12},Bd=function(){H.call(this)};q(Bd,H);var Cd=null,Dd=function(){H.call(this)};q(Dd,H);var Ed=null,Fd=function(){H.call(this)};q(Fd,H);var Gd=null,Hd=function(){H.call(this)};q(Hd,H);var Id=null,Jd=function(){H.call(this)};q(Jd,H);var Kd=null,Ld=function(){H.call(this)};
+q(Ld,H);var Md=null,Nd={hd:0,Vc:1,Wc:2,Tc:3,Zc:4,Xc:5,Yc:6,Hb:7,Oc:8},Od={jd:0,Yb:1,wa:2,za:3,ya:4,vc:5},Pd={ed:0,Nc:1,Tb:2,fc:3},Qd=function(){H.call(this)};q(Qd,H);var Rd=null,Sd={dd:0,sc:1,Jc:2},Td=function(){H.call(this)};q(Td,H);var Ud=null,Vd={Lb:0,Kb:1,Sc:2};
+O.prototype.c=function(){var a=wd;a||(wd=a=J(O,{0:{name:"InkEvent",g:"logs.proto.research.ink.InkEvent"},1:{name:"host",b:14,defaultValue:0,type:xd},2:{name:"event_type",b:14,defaultValue:0,type:yd},3:{name:"document_event",b:11,type:P},4:{name:"toolbar_event",b:11,type:Ld},5:{name:"engine_event",b:11,type:Qd},6:{name:"gms_event",b:11,type:Td}}));return a};O.c=O.prototype.c;
+P.prototype.c=function(){var a=zd;a||(zd=a=J(P,{0:{name:"DocumentEvent",F:O,g:"logs.proto.research.ink.InkEvent.DocumentEvent"},1:{name:"event_type",b:14,defaultValue:0,type:Ad},2:{name:"opened_event",b:11,type:Bd},3:{name:"open_cancelled_event",b:11,type:Dd},4:{name:"error_code",b:3,type:String},5:{name:"brix_error_code",b:3,type:String},6:{name:"collaborator_joined_event",b:11,type:Fd},7:{name:"document_state",b:11,type:Hd}}));return a};P.c=P.prototype.c;
+Bd.prototype.c=function(){var a=Cd;a||(Cd=a=J(Bd,{0:{name:"OpenedEvent",F:P,g:"logs.proto.research.ink.InkEvent.DocumentEvent.OpenedEvent"},1:{name:"millis_until_first_byte_loaded",b:3,type:String},2:{name:"millis_until_editable",b:3,type:String},3:{name:"missing_document_bounds",b:8,type:Boolean},4:{name:"was_opened_by_cosmoid",b:8,type:Boolean},5:{name:"active_users",b:3,type:String}}));return a};Bd.c=Bd.prototype.c;
+Dd.prototype.c=function(){var a=Ed;a||(Ed=a=J(Dd,{0:{name:"OpenCancelledEvent",F:P,g:"logs.proto.research.ink.InkEvent.DocumentEvent.OpenCancelledEvent"},1:{name:"time_until_cancelled",b:3,type:String}}));return a};Dd.c=Dd.prototype.c;Fd.prototype.c=function(){var a=Gd;a||(Gd=a=J(Fd,{0:{name:"CollaboratorJoined",F:P,g:"logs.proto.research.ink.InkEvent.DocumentEvent.CollaboratorJoined"},1:{name:"is_me",b:8,type:Boolean}}));return a};Fd.c=Fd.prototype.c;
+Hd.prototype.c=function(){var a=Id;a||(Id=a=J(Hd,{0:{name:"DocumentState",F:P,g:"logs.proto.research.ink.InkEvent.DocumentEvent.DocumentState"},1:{name:"stroke_count",b:3,type:String},2:{name:"text_field",l:!0,b:11,type:Jd},3:{name:"sticker_count",b:3,type:String}}));return a};Hd.c=Hd.prototype.c;
+Jd.prototype.c=function(){var a=Kd;a||(Kd=a=J(Jd,{0:{name:"TextField",F:Hd,g:"logs.proto.research.ink.InkEvent.DocumentEvent.DocumentState.TextField"},1:{name:"character_count",b:3,type:String},2:{name:"line_count",b:3,type:String}}));return a};Jd.c=Jd.prototype.c;
+Ld.prototype.c=function(){var a=Md;a||(Md=a=J(Ld,{0:{name:"ToolbarEvent",F:O,g:"logs.proto.research.ink.InkEvent.ToolbarEvent"},1:{name:"tool_event_type",b:14,defaultValue:0,type:Nd},2:{name:"tool_type",b:14,defaultValue:0,type:Od},3:{name:"expand_method",b:14,defaultValue:0,type:Pd},4:{name:"color",b:5,type:Number}}));return a};Ld.c=Ld.prototype.c;
+Qd.prototype.c=function(){var a=Rd;a||(Rd=a=J(Qd,{0:{name:"EngineEvent",F:O,g:"logs.proto.research.ink.InkEvent.EngineEvent"},1:{name:"engine_event_type",b:14,defaultValue:0,type:Sd},2:{name:"error_code",b:3,type:String}}));return a};Qd.c=Qd.prototype.c;
+Td.prototype.c=function(){var a=Ud;a||(Ud=a=J(Td,{0:{name:"GmsEvent",F:O,g:"logs.proto.research.ink.InkEvent.GmsEvent"},1:{name:"gms_event_type",b:14,defaultValue:0,type:Vd},2:{name:"time_since_connect_start",b:3,type:String},3:{name:"failure_has_resolution",b:8,type:Boolean},4:{name:"gms_error_code",b:3,type:String}}));return a};Td.c=Td.prototype.c;var Wd=function(){this.a=[];this.i={value:0,length:0};this.w={value:z(0),length:0};this.f=new DataView(new ArrayBuffer(8))};q(Wd,Sc);
+var K=function(a,b){if(null==b)return[];a.a=[];for(var c=Jc(b.c()),d=0;d<c.length;d++){var e=c[d];if(b.has(e))if(e.h)if(e.m){var f=a,h=b,p=e;e=f.a;Q(f,p.a<<3|2);for(var u=e.length,C=0,A=Nc(h,p);C<A;C++){var N=h.get(p,C);f.j(p,N,!0)}h=e.splice(u,e.length-u);Q(f,h.length);e.splice.apply(e,[e.length,0].concat(h))}else for(f=0,h=Nc(b,e);f<h;f++)p=b.get(e,f),a.j(e,p);else a.j(e,b.get(e))}return a.a};
+Wd.prototype.j=function(a,b,c){if(c=!c){a:{switch(a.f){default:c=!1;break a;case 17:case 18:case 8:case 3:case 14:case 5:case 13:case 4:c=0;break;case 6:case 16:case 1:c=1;break;case 9:case 12:case 11:c=2;break;case 10:c=3;break;case 7:case 15:case 2:c=5}Q(this,a.a<<3|c);c=!0}c=!c}if(!c)switch(a.f){default:throw Error("Unknown field type "+a.f);case 17:Q(this,b<<1^-(b>>>31));break;case 18:a=yc(b);a=Gc(a,1).xor(D(Hc(a,63)));Xd(this,a);break;case 8:Q(this,b?1:0);break;case 5:0<b?Q(this,b):Xd(this,vc(b));
+break;case 3:case 4:Xd(this,yc(b));break;case 14:case 13:Q(this,b);break;case 6:case 16:Yd(this,yc(b),8);break;case 1:this.f.setFloat64(0,b,!0);for(a=0;8>a;a++)this.a.push(this.f.getUint8(a));break;case 9:if(null!=b)for(a=unescape(encodeURIComponent(b)),Q(this,a.length),b=0;b<a.length;b++)this.a.push(a.charCodeAt(b));break;case 12:if(null!=b)for(Q(this,b.length),a=0;a<b.length;a++)this.a.push(b.charCodeAt(a));break;case 10:b=K(new Wd,b);Ga(this.a,b);Q(this,a.a<<3|4);break;case 11:b=K(new Wd,b);Q(this,
+b.length);Ga(this.a,b);break;case 7:Yd(this,E(b),4);break;case 15:Yd(this,vc(b),4);break;case 2:for(this.f.setFloat32(0,b,!0),a=0;4>a;a++)this.a.push(this.f.getUint8(a))}};
+var Tc=function(a,b,c){if(null!=c){c instanceof ArrayBuffer&&(c=new Uint8Array(c));for(var d=b.c(),e=0;e<c.length;){var f=Zd(a,c.subarray(e)),h=f.value,p=h>>3;h&=7;e+=f.length;if(f=Kc(d,p))if(f.m)for(p=Zd(a,c.subarray(e)),h=p.value,e+=p.length;0<h&&e<c.length;){p=a.h(f,c.subarray(e));if(!p)throw Error("Expected "+f.f);b.add(f,p.value);e+=p.length;h-=p.length}else{h=a.h(f,c.subarray(e));if(!h)throw Error("Expected "+f.f);e+=h.length;f.h?b.add(f,h.value):b.set(f,h.value)}else{f=e;p=a;e=c.subarray(e);
+var u=0;switch(h){case 0:u=$d(p,e).length;break;case 1:u=8;break;case 2:e=$d(p,e);u=e.length+e.value.a;break;case 3:case 4:xa("Error deserializing group");break;case 5:u=4}e=f+u}}}};
+Wd.prototype.h=function(a,b){var c=null,d=a.f,e=$d(this,b),f=e.length;switch(d){case 17:a=e.value.a;c=a>>>1^-(a&1);break;case 18:a=e.value;c=Hc(a,1).xor(D(a.and(z(1)))).toString();break;case 8:c=e.value.s(z(1));break;case 3:case 4:c=e.value.toString();break;case 5:c=e.value.a;break;case 14:case 13:c=Dc(e.value);break;case 6:case 16:a=b.subarray(0,8);c=(new y(ae(a.subarray(0,4),!0),ae(a.subarray(4,8),!0))).toString();f=8;break;case 1:a=b.subarray(0,8);for(c=0;8>c;c++)this.f.setUint8(c,a[c]);c=this.f.getFloat64(0,
+!0);f=8;break;case 9:a=b.subarray(e.length,e.length+e.value.a);a=be(a);c=decodeURIComponent(escape(a));f=e.length+e.value.a;break;case 12:a=b.subarray(e.length,e.length+e.value.a);c=be(a);f=e.length+e.value.a;break;case 10:f=c=new (a.j.prototype.c().ea);e=b;d=f.c();for(var h=0;;){var p=Zd(this,e),u=p.value;p=p.length;var C=u>>3;if(4==(u&7))break;h+=p;u={value:void 0,length:0};(C=Kc(d,C))&&(u=this.h(C,e.subarray(p)))&&null!==u.value&&(C.h?f.add(C,u.value):f.set(C,u.value));h+=u.length;if(e.length<
+p+u.length)break;e=e.subarray(p+u.length)}f=h;b=$d(this,b.subarray(f));r(b.value.a==(a.a<<3|4),"Error deserializing group");f+=b.length;break;case 11:f=e.length+e.value.a;b=b.subarray(e.length,f);c=new (a.j.prototype.c().ea);Tc(this,c,b);break;case 7:case 15:c=ae(b.subarray(0,4),15==d);f=4;break;case 2:a=b.subarray(0,4);for(c=0;4>c;c++)this.f.setUint8(c,a[c]);c=this.f.getFloat32(0,!0);f=4}return{value:c,length:f}};
+var Q=function(a,b){do{var c=b&127;b>>>=7;0<b&&(c|=128);a.a.push(c)}while(0<b)},Xd=function(a,b){var c=vc(127);do{var d=b.and(c).a;b=Hc(b,7);0<Ec(b,z(0))&&(d|=128);a.a.push(d)}while(0<Ec(b,z(0)))},$d=function(a,b){a=a.w;for(var c=E(0),d=0;d<b.length;d++){var e=Gc(vc(b[d]&127),7*d);c=c.or(e);if(0==(b[d]&128))break}a.value=c;a.length=d+1;return a},Zd=function(a,b){a=a.i;for(var c=0,d=0;d<b.length&&(c|=(b[d]&127)<<7*d,0!=(b[d]&128));d++);a.value=c;a.length=d+1;return a},Yd=function(a,b,c){for(var d=
+vc(255),e=0;e<c;e++){var f=b.and(d).a;a.a.push(f);b=Hc(b,8)}},ae=function(a,b){for(var c=0,d=0;d<a.length;d++)c|=a[d]<<8*d;b||(c>>>=0);return c},be=function(a){var b="";a=new Uint16Array(a);for(var c=0;c<a.length;c+=65536)b+=String.fromCharCode.apply(null,a.subarray(c,c+Math.min(65536,a.length-c)));return b};var ce=function(a){for(var b=a.V;b;)a=b,b=a.V;return a},de=function(a,b){var c=document.createElement("CANVAS"),d=document.createElement("IMG");d.setAttribute("style","position:absolute;visibility:hidden;top:-1000px;left:-1000px;");d.crossOrigin="Anonymous";$b(d,"load",function(){var a=d.width,f=d.height;c.width=a;c.height=f;var h=c.getContext("2d");h.drawImage(d,0,0);h=h.getImageData(0,0,a,f);document.body.removeChild(d);b(h.data,new Fb(a,f))});d.setAttribute("src",a);document.body.appendChild(d)},
+ee=function(a,b){var c=new O;I(c,1,a);I(c,2,1);a=new P;I(a,1,b);I(c,3,a);return c};var fe=function(a){v.call(this,"b");this.enabled=a};q(fe,v);var ge=function(){v.call(this,"d")};q(ge,v);var he=function(){v.call(this,"e")};q(he,v);var ie=function(){v.call(this,"f")};q(ie,v);var je=function(a){v.call(this,"g");this.callback=a};q(je,v);var ke=function(a){v.call(this,"h");this.j=a};q(ke,v);var le=function(){v.call(this,"l")};q(le,v);var R=function(){H.call(this)};q(R,H);var me=null;R.prototype.c=function(){var a=me;a||(me=a=J(R,{0:{name:"Rect",g:"sketchology.proto.Rect"},1:{name:"xlow",b:2,type:Number},2:{name:"xhigh",b:2,type:Number},3:{name:"ylow",b:2,type:Number},4:{name:"yhigh",b:2,type:Number}}));return a};R.c=R.prototype.c;var ne={NONE:0,md:1,Qc:2,ec:3,nd:4},oe=function(){H.call(this)};q(oe,H);var pe=null,qe=function(){H.call(this)};q(qe,H);var re=null,se={Y:0,cc:1,nc:2},te=function(){H.call(this)};q(te,H);var ue=null,ve=function(){H.call(this)};q(ve,H);var we=null,xe=function(){H.call(this)};q(xe,H);var ye=null,ze=function(){H.call(this)};q(ze,H);var Ae=null,Be=function(){H.call(this)};q(Be,H);var Ce=null,De=function(){H.call(this)};q(De,H);var Ee=null,S=function(){H.call(this)};q(S,H);var Fe=null,Ge=function(){H.call(this)};
+q(Ge,H);var He=null,Ie=function(){H.call(this)};q(Ie,H);var Je=null,Ke=function(){H.call(this)};q(Ke,H);var Le=null,Me=function(){H.call(this)};q(Me,H);var Ne=null,Oe=function(){H.call(this)};q(Oe,H);var Pe=null,T=function(){H.call(this)};q(T,H);var Qe=null;T.prototype.B=function(){return Oc(this,2)};var Re=function(){H.call(this)};q(Re,H);var Se=null,Te={Y:0,xc:1,rc:2,Pb:3,Hc:4,Ib:5},Ue={Eb:1,Mc:2,Rc:3};
+oe.prototype.c=function(){var a=pe;a||(pe=a=J(oe,{0:{name:"CallbackFlags",g:"sketchology.proto.CallbackFlags"},1:{name:"mesh_data_ctm",b:8,type:Boolean},2:{name:"uncompressed_outline",b:8,type:Boolean},3:{name:"compressed_input_points",b:8,type:Boolean}}));return a};oe.c=oe.prototype.c;qe.prototype.c=function(){var a=re;a||(re=a=J(qe,{0:{name:"SourceDetails",g:"sketchology.proto.SourceDetails"},1:{name:"origin",b:14,defaultValue:0,type:se},2:{name:"host_source_details",b:13,type:Number}}));return a};
+qe.c=qe.prototype.c;te.prototype.c=function(){var a=ue;a||(ue=a=J(te,{0:{name:"BackgroundImageInfo",g:"sketchology.proto.BackgroundImageInfo"},1:{name:"uri",b:9,type:String},3:{name:"bounds",b:11,type:R}}));return a};te.c=te.prototype.c;ve.prototype.c=function(){var a=we;a||(we=a=J(ve,{0:{name:"Border",g:"sketchology.proto.Border"},1:{name:"uri",b:9,type:String},2:{name:"scale",b:2,defaultValue:1,type:Number}}));return a};ve.c=ve.prototype.c;
+xe.prototype.c=function(){var a=ye;a||(ye=a=J(xe,{0:{name:"GridInfo",g:"sketchology.proto.GridInfo"},1:{name:"uri",b:9,type:String},2:{name:"rgba_multiplier",b:13,defaultValue:4294967295,type:Number},3:{name:"size_world",b:2,defaultValue:50,type:Number},4:{name:"origin",b:11,type:Oe}}));return a};xe.c=xe.prototype.c;ze.prototype.c=function(){var a=Ae;a||(Ae=a=J(ze,{0:{name:"LOD",g:"sketchology.proto.LOD"},1:{name:"max_coverage",b:2,type:Number},2:{name:"ctm_blob",b:12,type:String}}));return a};
+ze.c=ze.prototype.c;Be.prototype.c=function(){var a=Ce;a||(Ce=a=J(Be,{0:{name:"Stroke",g:"sketchology.proto.Stroke"},1:{name:"shader_type",b:14,defaultValue:0,type:ne},3:{name:"lod",l:!0,b:11,type:ze},4:{name:"abgr",b:13,type:Number},5:{name:"point_x",l:!0,fa:!0,b:17,type:Number},6:{name:"point_y",l:!0,fa:!0,b:17,type:Number},7:{name:"point_t_ms",l:!0,fa:!0,b:13,type:Number},8:{name:"deprecated_transform",b:11,type:S},9:{name:"start_time_ms",b:4,type:String}}));return a};Be.c=Be.prototype.c;
+De.prototype.c=function(){var a=Ee;a||(Ee=a=J(De,{0:{name:"UncompressedStroke",g:"sketchology.proto.UncompressedStroke"},1:{name:"outline",l:!0,b:11,type:Oe},2:{name:"rgba",b:13,type:Number}}));return a};De.c=De.prototype.c;
+S.prototype.c=function(){var a=Fe;a||(Fe=a=J(S,{0:{name:"AffineTransform",g:"sketchology.proto.AffineTransform"},1:{name:"tx",b:2,type:Number},2:{name:"ty",b:2,type:Number},3:{name:"scale_x",b:2,defaultValue:1,type:Number},4:{name:"scale_y",b:2,defaultValue:1,type:Number},5:{name:"rotation_radians",b:2,type:Number}}));return a};S.c=S.prototype.c;
+Ge.prototype.c=function(){var a=He;a||(He=a=J(Ge,{0:{name:"Element",g:"sketchology.proto.Element"},4:{name:"deprecated_uuid",b:9,type:String},5:{name:"minimum_serializer_version",b:13,type:Number},6:{name:"stroke",b:11,type:Be},9:{name:"path",b:11,type:Re},10:{name:"attributes",b:11,type:Ie}}));return a};Ge.c=Ge.prototype.c;
+Ie.prototype.c=function(){var a=Je;a||(Je=a=J(Ie,{0:{name:"ElementAttributes",g:"sketchology.proto.ElementAttributes"},1:{name:"selectable",b:8,defaultValue:!0,type:Boolean},2:{name:"magic_erasable",b:8,defaultValue:!0,type:Boolean},3:{name:"is_sticker",b:8,defaultValue:!1,type:Boolean},4:{name:"is_text",b:8,defaultValue:!1,type:Boolean}}));return a};Ie.c=Ie.prototype.c;
+Ke.prototype.c=function(){var a=Le;a||(Le=a=J(Ke,{0:{name:"UncompressedElement",g:"sketchology.proto.UncompressedElement"},1:{name:"uncompressed_stroke",b:11,type:De}}));return a};Ke.c=Ke.prototype.c;Me.prototype.c=function(){var a=Ne;a||(Ne=a=J(Me,{0:{name:"ElementMutation",g:"sketchology.proto.ElementMutation"},1:{name:"uuid",l:!0,b:9,type:String},2:{name:"transform",l:!0,b:11,type:S}}));return a};Me.c=Me.prototype.c;
+Oe.prototype.c=function(){var a=Pe;a||(Pe=a=J(Oe,{0:{name:"Point",g:"sketchology.proto.Point"},1:{name:"x",b:2,type:Number},2:{name:"y",b:2,type:Number}}));return a};Oe.c=Oe.prototype.c;T.prototype.c=function(){var a=Qe;a||(Qe=a=J(T,{0:{name:"ElementBundle",g:"sketchology.proto.ElementBundle"},1:{name:"uuid",b:9,type:String},2:{name:"element",b:11,type:Ge},3:{name:"transform",b:11,type:S},4:{name:"uncompressed_element",b:11,type:Ke}}));return a};T.c=T.prototype.c;
+Re.prototype.c=function(){var a=Se;a||(Se=a=J(Re,{0:{name:"Path",g:"sketchology.proto.Path"},1:{name:"segment_types",l:!0,b:14,defaultValue:0,type:Te},2:{name:"segment_counts",l:!0,b:13,type:Number},3:{name:"segment_args",l:!0,b:1,type:Number},4:{name:"radius",b:1,defaultValue:1,type:Number},5:{name:"rgba",b:13,type:Number},6:{name:"end_cap",b:14,defaultValue:2,type:Ue},7:{name:"fill_rgba",b:13,type:Number}}));return a};Re.c=Re.prototype.c;var Ve={ld:0,Vb:1,Ub:2,Wb:3,Qb:4},We=function(){H.call(this)};q(We,H);var Xe=null,Ye=function(){H.call(this)};q(Ye,H);var Ze=null,$e=function(){H.call(this)};q($e,H);var af=null,bf=function(){H.call(this)};q(bf,H);var cf=null;We.prototype.c=function(){var a=Xe;a||(Xe=a=J(We,{0:{name:"AnimationCurve",g:"sketchology.proto.AnimationCurve"},1:{name:"type",b:14,defaultValue:1,type:Ve},2:{name:"params",l:!0,b:2,type:Number}}));return a};We.c=We.prototype.c;
+Ye.prototype.c=function(){var a=Ze;a||(Ze=a=J(Ye,{0:{name:"ColorAnimation",g:"sketchology.proto.ColorAnimation"},1:{name:"duration",b:1,defaultValue:.5,type:Number},2:{name:"curve",b:11,type:We},3:{name:"rgba",b:13,type:Number}}));return a};Ye.c=Ye.prototype.c;
+$e.prototype.c=function(){var a=af;a||(af=a=J($e,{0:{name:"ScaleAnimation",g:"sketchology.proto.ScaleAnimation"},1:{name:"duration",b:1,defaultValue:.5,type:Number},2:{name:"curve",b:11,type:We},3:{name:"scale_x",b:2,type:Number},4:{name:"scale_y",b:2,type:Number}}));return a};$e.c=$e.prototype.c;
+bf.prototype.c=function(){var a=cf;a||(cf=a=J(bf,{0:{name:"ElementAnimation",g:"sketchology.proto.ElementAnimation"},1:{name:"uuid",b:9,type:String},2:{name:"color_animation",b:11,type:Ye},3:{name:"scale_animation",b:11,type:$e},4:{name:"next",b:11,type:bf}}));return a};bf.c=bf.prototype.c;var df={Ab:1,Rb:2},ef=function(){H.call(this)};q(ef,H);var ff=null,gf=function(){H.call(this)};q(gf,H);var hf=null,jf=function(){H.call(this)};q(jf,H);var kf=null,lf=function(){H.call(this)};q(lf,H);var mf=null,nf=function(){H.call(this)};q(nf,H);var of=null,pf=function(){H.call(this)};q(pf,H);var qf=null,rf=function(){H.call(this)};q(rf,H);var sf=null,tf=function(){H.call(this)};q(tf,H);var uf=null,vf=function(){H.call(this)};q(vf,H);var wf=null,xf=function(){H.call(this)};q(xf,H);
+var yf=null,U=function(){H.call(this)};q(U,H);var zf=null;U.prototype.B=function(){return Oc(this,2,void 0)};var Af=function(){H.call(this)};q(Af,H);var Bf=null;Af.prototype.B=function(){return Oc(this,2,void 0)};ef.prototype.c=function(){var a=ff;a||(ff=a=J(ef,{0:{name:"Color",g:"sketchology.proto.Color"},1:{name:"argb",b:13,type:Number}}));return a};ef.c=ef.prototype.c;
+gf.prototype.c=function(){var a=hf;a||(hf=a=J(gf,{0:{name:"BackgroundColor",g:"sketchology.proto.BackgroundColor"},1:{name:"rgba",b:13,type:Number}}));return a};gf.c=gf.prototype.c;jf.prototype.c=function(){var a=kf;a||(kf=a=J(jf,{0:{name:"PageProperties",g:"sketchology.proto.PageProperties"},1:{name:"background_color",b:11,type:ef},2:{name:"background_image",b:11,type:te},3:{name:"bounds",b:11,type:R},4:{name:"border",b:11,type:ve},5:{name:"grid_info",b:11,type:xe}}));return a};jf.c=jf.prototype.c;
+lf.prototype.c=function(){var a=mf;a||(mf=a=J(lf,{0:{name:"AddAction",g:"sketchology.proto.AddAction"},1:{name:"uuid",b:9,type:String},2:{name:"below_element_with_uuid",b:9,type:String}}));return a};lf.c=lf.prototype.c;nf.prototype.c=function(){var a=of;a||(of=a=J(nf,{0:{name:"RemoveAction",g:"sketchology.proto.RemoveAction"},1:{name:"uuid",l:!0,b:9,type:String},2:{name:"was_below_uuid",l:!0,b:9,type:String}}));return a};nf.c=nf.prototype.c;
+pf.prototype.c=function(){var a=qf;a||(qf=a=J(pf,{0:{name:"ClearAction",g:"sketchology.proto.ClearAction"},1:{name:"uuid",l:!0,b:9,type:String}}));return a};pf.c=pf.prototype.c;rf.prototype.c=function(){var a=sf;a||(sf=a=J(rf,{0:{name:"ReplaceAction",g:"sketchology.proto.ReplaceAction"},1:{name:"uuid_add",l:!0,b:9,type:String},2:{name:"below_element_with_uuid",b:9,type:String},3:{name:"uuid_remove",l:!0,b:9,type:String},4:{name:"was_below_uuid",l:!0,b:9,type:String}}));return a};rf.c=rf.prototype.c;
+tf.prototype.c=function(){var a=uf;a||(uf=a=J(tf,{0:{name:"SetTransformAction",g:"sketchology.proto.SetTransformAction"},1:{name:"uuid",l:!0,b:9,type:String},2:{name:"from_transform",l:!0,b:11,type:S},3:{name:"to_transform",l:!0,b:11,type:S}}));return a};tf.c=tf.prototype.c;vf.prototype.c=function(){var a=wf;a||(wf=a=J(vf,{0:{name:"SetPageBoundsAction",g:"sketchology.proto.SetPageBoundsAction"},1:{name:"old_bounds",b:11,type:R},2:{name:"new_bounds",b:11,type:R}}));return a};vf.c=vf.prototype.c;
+xf.prototype.c=function(){var a=yf;a||(yf=a=J(xf,{0:{name:"StorageAction",g:"sketchology.proto.StorageAction"},1:{name:"add_action",b:11,type:lf},2:{name:"remove_action",b:11,type:nf},3:{name:"clear_action",b:11,type:pf},4:{name:"replace_action",b:11,type:rf},5:{name:"set_transform_action",b:11,type:tf},6:{name:"set_page_bounds_action",b:11,type:vf}}));return a};xf.c=xf.prototype.c;
+U.prototype.c=function(){var a=zf;a||(zf=a=J(U,{0:{name:"Snapshot",g:"sketchology.proto.Snapshot"},1:{name:"page_properties",b:11,type:jf},2:{name:"element",l:!0,b:11,type:T},3:{name:"dead_element",l:!0,b:11,type:T},4:{name:"undo_action",l:!0,b:11,type:xf},5:{name:"redo_action",l:!0,b:11,type:xf},6:{name:"element_state_index",l:!0,b:14,defaultValue:1,type:df},7:{name:"fingerprint",b:4,type:String}}));return a};U.c=U.prototype.c;
+Af.prototype.c=function(){var a=Bf;a||(Bf=a=J(Af,{0:{name:"MutationPacket",g:"sketchology.proto.MutationPacket"},1:{name:"mutation",l:!0,b:11,type:xf},2:{name:"element",l:!0,b:11,type:T}}));return a};Af.c=Af.prototype.c;var Cf={bd:0,wa:1,oc:2,za:3,Bb:4,Dc:5,xa:6,zb:7,ya:8,mc:9,Fb:10,Cb:11},Df={Y:0,Kc:1,$b:2,bc:3,Zb:4,ac:5,tc:6,zc:7,Ob:8,kc:9},V=function(){H.call(this)};q(V,H);var Ef=null,W=function(){H.call(this)};q(W,H);var Ff=null,Gf=function(){H.call(this)};q(Gf,H);var Hf=null,If=function(){H.call(this)};q(If,H);var Jf=null,Kf=function(){H.call(this)};q(Kf,H);var Lf=null,Mf=function(){H.call(this)};q(Mf,H);var Nf=null,Of=function(){H.call(this)};q(Of,H);var Pf=null,Qf=function(){H.call(this)};q(Qf,H);
+var Rf=null,Sf={gd:0,od:1,POINTS:2,pd:3,Ec:4,Fc:5},Tf=function(){H.call(this)};q(Tf,H);var Uf=null,Vf=function(){H.call(this)};q(Vf,H);var Wf=null,Xf={Y:0,qc:1,Xb:2,uc:3,Ic:4,yc:5,hc:6,Gc:7,Nb:8},Yf=function(){H.call(this)};q(Yf,H);var Zf=null,$f=function(){H.call(this)};q($f,H);var ag=null,bg=function(){H.call(this)};q(bg,H);var cg=null,dg=function(){H.call(this)};q(dg,H);var eg=null,fg=function(){H.call(this)};q(fg,H);var gg=null,hg={Y:0,wc:1,$c:2,Cc:3,xa:4},ig=function(){H.call(this)};q(ig,H);
+var jg=null,kg=function(){H.call(this)};q(kg,H);var lg=null,mg=function(){H.call(this)};q(mg,H);var ng=null,og=function(){H.call(this)};q(og,H);var pg=null,qg=function(){H.call(this)};q(qg,H);var rg=null;
+V.prototype.c=function(){var a=Ef;a||(Ef=a=J(V,{0:{name:"Command",g:"sketchology.proto.Command"},1:{name:"set_viewport",b:11,type:Kf},2:{name:"tool_params",b:11,type:Vf},3:{name:"add_path",b:11,type:qg},4:{name:"camera_position",b:11,type:R},5:{name:"page_bounds",b:11,type:R},6:{name:"image_export",b:11,type:Mf},7:{name:"flag_assignment",b:11,type:Yf},8:{name:"set_element_transforms",b:11,type:Me},9:{name:"add_element",b:11,type:$f},10:{name:"background_image",b:11,type:te},11:{name:"background_color",
+b:11,type:gf},12:{name:"set_out_of_bounds_color",b:11,type:bg},13:{name:"set_page_border",b:11,type:ve},14:{name:"send_input_stream",b:11,type:dg},15:{name:"sequence_point",b:11,type:ig},16:{name:"set_callback_flags",b:11,type:kg},17:{name:"set_camera_bounds_config",b:11,type:mg},18:{name:"deselect_all",b:11,type:W},19:{name:"add_image_rect",b:11,type:og},21:{name:"clear",b:11,type:W},22:{name:"remove_all_elements",b:11,type:W},23:{name:"undo",b:11,type:W},24:{name:"redo",b:11,type:W},25:{name:"evict_image_data",
+b:11,type:If},26:{name:"replace_elements",b:11,type:Gf},27:{name:"commit_crop",b:11,type:W},28:{name:"element_animation",b:11,type:bf},29:{name:"set_grid",b:11,type:xe},30:{name:"clear_grid",b:11,type:W}}));return a};V.c=V.prototype.c;W.prototype.c=function(){var a=Ff;a||(Ff=a=J(W,{0:{name:"NoArgCommand",g:"sketchology.proto.NoArgCommand"}}));return a};W.c=W.prototype.c;
+Gf.prototype.c=function(){var a=Hf;a||(Hf=a=J(Gf,{0:{name:"ReplaceElementsCommand",g:"sketchology.proto.ReplaceElementsCommand"},1:{name:"uuids_to_remove",l:!0,b:9,type:String},2:{name:"paths_to_add",l:!0,b:11,type:Re}}));return a};Gf.c=Gf.prototype.c;If.prototype.c=function(){var a=Jf;a||(Jf=a=J(If,{0:{name:"EvictImageData",g:"sketchology.proto.EvictImageData"},1:{name:"uri",b:9,type:String}}));return a};If.c=If.prototype.c;
+Kf.prototype.c=function(){var a=Lf;a||(Lf=a=J(Kf,{0:{name:"Viewport",g:"sketchology.proto.Viewport"},1:{name:"width",b:13,type:Number},2:{name:"height",b:13,type:Number},3:{name:"ppi",b:2,type:Number}}));return a};Kf.c=Kf.prototype.c;Mf.prototype.c=function(){var a=Nf;a||(Nf=a=J(Mf,{0:{name:"ImageExport",g:"sketchology.proto.ImageExport"},1:{name:"max_dimension_px",b:13,defaultValue:1024,type:Number},2:{name:"should_draw_background",b:8,defaultValue:!0,type:Boolean}}));return a};Mf.c=Mf.prototype.c;
+Of.prototype.c=function(){var a=Pf;a||(Pf=a=J(Of,{0:{name:"LinearPathAnimation",g:"sketchology.proto.LinearPathAnimation"},1:{name:"rgba_from",b:13,type:Number},2:{name:"rgba_seconds",b:1,type:Number},3:{name:"dilation_from",b:2,type:Number},4:{name:"dilation_seconds",b:1,type:Number}}));return a};Of.c=Of.prototype.c;
+Qf.prototype.c=function(){var a=Rf;a||(Rf=a=J(Qf,{0:{name:"LineSize",g:"sketchology.proto.LineSize"},7:{name:"stroke_width",b:2,type:Number},8:{name:"units",b:14,defaultValue:1,type:Sf},9:{name:"use_web_sizes",b:8,type:Boolean}}));return a};Qf.c=Qf.prototype.c;Tf.prototype.c=function(){var a=Uf;a||(Uf=a=J(Tf,{0:{name:"PusherToolParams",g:"sketchology.proto.PusherToolParams"},1:{name:"manipulate_stickers",b:8,type:Boolean},2:{name:"manipulate_text",b:8,type:Boolean}}));return a};Tf.c=Tf.prototype.c;
+Vf.prototype.c=function(){var a=Wf;a||(Wf=a=J(Vf,{0:{name:"ToolParams",g:"sketchology.proto.ToolParams"},1:{name:"tool",b:14,defaultValue:0,type:Xf},2:{name:"rgba",b:13,type:Number},3:{name:"line_size",b:11,type:Qf},4:{name:"pusher_tool_params",b:11,type:Tf},5:{name:"brush_type",b:14,defaultValue:0,type:Cf},6:{name:"linear_path_animation",b:11,type:Of}}));return a};Vf.c=Vf.prototype.c;
+Yf.prototype.c=function(){var a=Zf;a||(Zf=a=J(Yf,{0:{name:"FlagAssignment",g:"sketchology.proto.FlagAssignment"},1:{name:"flag",b:14,defaultValue:0,type:Df},2:{name:"bool_value",b:8,type:Boolean}}));return a};Yf.c=Yf.prototype.c;$f.prototype.c=function(){var a=ag;a||(ag=a=J($f,{0:{name:"AddElement",g:"sketchology.proto.AddElement"},1:{name:"bundle",b:11,type:T},2:{name:"below_element_with_uuid",b:9,type:String}}));return a};$f.c=$f.prototype.c;
+bg.prototype.c=function(){var a=cg;a||(cg=a=J(bg,{0:{name:"OutOfBoundsColor",g:"sketchology.proto.OutOfBoundsColor"},1:{name:"rgba",b:13,type:Number}}));return a};bg.c=bg.prototype.c;dg.prototype.c=function(){var a=eg;a||(eg=a=J(dg,{0:{name:"SInputStream",g:"sketchology.proto.SInputStream"},1:{name:"screen_width",b:13,type:Number},2:{name:"screen_height",b:13,type:Number},3:{name:"screen_ppi",b:2,type:Number},4:{name:"input",l:!0,b:11,type:fg}}));return a};dg.c=dg.prototype.c;
+fg.prototype.c=function(){var a=gg;a||(gg=a=J(fg,{0:{name:"SInput",g:"sketchology.proto.SInput"},1:{name:"type",b:14,defaultValue:0,type:hg},2:{name:"id",b:13,type:Number},3:{name:"flags",b:13,type:Number},4:{name:"time_s",b:1,type:Number},5:{name:"screen_pos_x",b:2,type:Number},6:{name:"screen_pos_y",b:2,type:Number},7:{name:"pressure",b:2,type:Number},8:{name:"wheel_delta",b:2,type:Number},9:{name:"tilt",b:2,type:Number},10:{name:"orientation",b:2,type:Number}}));return a};fg.c=fg.prototype.c;
+ig.prototype.c=function(){var a=jg;a||(jg=a=J(ig,{0:{name:"SequencePoint",g:"sketchology.proto.SequencePoint"},1:{name:"id",b:5,type:Number}}));return a};ig.c=ig.prototype.c;kg.prototype.c=function(){var a=lg;a||(lg=a=J(kg,{0:{name:"SetCallbackFlags",g:"sketchology.proto.SetCallbackFlags"},1:{name:"source_details",b:11,type:qe},2:{name:"callback_flags",b:11,type:oe}}));return a};kg.c=kg.prototype.c;
+mg.prototype.c=function(){var a=ng;a||(ng=a=J(mg,{0:{name:"CameraBoundsConfig",g:"sketchology.proto.CameraBoundsConfig"},1:{name:"margin_left_px",b:2,type:Number},2:{name:"margin_right_px",b:2,type:Number},3:{name:"margin_bottom_px",b:2,type:Number},4:{name:"margin_top_px",b:2,type:Number},5:{name:"fraction_padding",b:2,defaultValue:.1,type:Number}}));return a};mg.c=mg.prototype.c;
+og.prototype.c=function(){var a=pg;a||(pg=a=J(og,{0:{name:"ImageRect",g:"sketchology.proto.ImageRect"},1:{name:"rect",b:11,type:R},2:{name:"bitmap_uri",b:9,type:String},3:{name:"attributes",b:11,type:Ie},4:{name:"rotation_radians",b:2,type:Number}}));return a};og.c=og.prototype.c;qg.prototype.c=function(){var a=rg;a||(rg=a=J(qg,{0:{name:"AddPath",g:"sketchology.proto.AddPath"},1:{name:"path",b:11,type:Re},2:{name:"uuid",b:9,type:String}}));return a};qg.c=qg.prototype.c;var sg=function(){w.call(this)};q(sg,w);var tg="ink_model_instances_"+Math.random();var X=function(){w.call(this);this.f="#000000";this.j=.6;this.h=!1;this.a=this.i=1;this.m="CALLIGRAPHY"};q(X,sg);(function(a){a.S=function(b){Aa(b);var c=ce(b);(b=c[tg])||(c[tg]=b={});var d=a[oa]||(a[oa]=++pa),e=b[d];e?b=e:(c=new a(c),b=b[d]=c);return b}})(X);
+var ug={AIRBRUSH:1,CALLIGRAPHY:1,EDIT:2,ERASER:1,HIGHLIGHTER:1,INKPEN:1,MAGIC_ERASE:3,MARKER:1,PENCIL:1,BALLPOINT:1,BALLPOINT_IN_PEN_MODE_ELSE_MARKER:1,QUERY:4},vg={AIRBRUSH:7,CALLIGRAPHY:1,ERASER:6,HIGHLIGHTER:8,INKPEN:2,MARKER:3,BALLPOINT:4,BALLPOINT_IN_PEN_MODE_ELSE_MARKER:11,PENCIL:5};g=X.prototype;g.Qa=function(a){this.f=a;x(this,"m")};g.Ra=function(a){this.j=a;x(this,"m")};g.wb=function(a){this.h=a;x(this,"m")};
+g.xb=function(a){this.i=ug[a];this.a=void 0!==vg[a]?vg[a]:this.a;this.m=a;x(this,"m")};g.La=function(){return this.m};g.Na=function(){return this.f};g.ta=function(){return this.h?"#FFFFFF":this.f};g.ua=function(){return parseInt(this.ta().substring(1),16)};g.Oa=function(){return this.j};g.Ia=function(){return this.h};g.Ma=function(){return this.a};g.Pa=function(){return this.i};var Ag=function(a){this.j=wg(a);this.h=xg(a);this.f=yg(a);this.a=zg(a)},Bg=function(a){return"rgb("+[a.h,a.f,a.a].join()+")"},Cg=function(a){return new Uint32Array([a.h<<24|a.f<<16|a.a<<8|a.j])},Dg=function(a){return function(b){return b>>>a&255}},wg=Dg(24),xg=Dg(16),yg=Dg(8),zg=Dg(0),Eg=new Ag(4278190080),Fg=new Ag(4294967295),Gg=new Ag(4294638330);var Hg=function(){v.call(this,"n")};q(Hg,v);var Ig=function(a,b,c){b=c||b;c=qd(m(a.da)||a.da instanceof L,"manifestUrl",a.da);var d=qd(m(a.ga)||a.ga instanceof L,"useMSAA",a.ga),e=qd(m(a.ha)||a.ha instanceof L,"useSingleBuffer",a.ha);a=qd(m(a.I)||a.I instanceof L,"sengineType",a.I);b="<style"+(b&&b.ia?' nonce="'+pd(b&&b.ia)+'"':"")+'>\n #ink-engine-hwoverlay {\n display: none;\n position: absolute;\n width: 5px;\n height: 5px;\n left: 0px;\n top: 0px;\n /* Transforms and semi-transparent color are used to ensure the div\n * prevents use of a hardware overlay for the underlying canvas element,\n * despite future optimizations to the hardware overlay eligibility\n * detection in ChromeOS. See b/64569245 for details */\n background-color: rgba(0, 0, 0, 0.01);\n transform: translate3d(0.33, 0.14, 0);\n }\n /* TODO(b/69541198): Hack for hardware overlay in fullscreen mode */\n #canvas-parent.fullscreen {\n height: calc(100% - 5px);\n padding: 0 5px 5px 5px;\n background-color: transparent;\n }\n </style><embed id="ink-engine" use_msaa="'+
+pd(d)+'" use_single_buffer="'+pd(e)+'" src="';null!=c&&c.H===Wc?(r(c.constructor===$c),c=String(c).replace(ud,td)):null!=c&&c.H===Xc?(r(c.constructor===ad),c=String(c).replace(ud,td)):c instanceof zb?(c instanceof zb&&c.constructor===zb&&c.Ba===yb?c=c.ca:(xa("expected object of type SafeUrl, got '"+c+"' of type "+n(c)),c="type_error:SafeUrl"),c=String(c).replace(ud,td)):c instanceof xb?(c instanceof xb&&c.constructor===xb&&c.Da===wb?c="":(xa("expected object of type TrustedResourceUrl, got '"+c+"' of type "+
+n(c)),c="type_error:TrustedResourceUrl"),c=String(c).replace(ud,td)):(c=String(c),vd.test(c)?c=c.replace(ud,td):(xa("Bad value `%s` for |filterNormalizeUri",[c]),c="about:invalid#zSoyz"));return kd(b+pd(c)+'" type="application/x-nacl" sengine_type="'+pd(a)+'"><div id="ink-engine-hwoverlay"></div>')};Ig.a="ink.soy.nacl.canvasHTML";var Jg=function(a,b,c,d){M.call(this);r(a);this.Ca=a;this.m=b;this.Fa=c;this.J=0;this.L=600;this.K=800;this.G=0;this.i=new Wd;this.C=la;this.oa=this.qa=!1;this.Ga=d;this.ra=[];this.W=[];this.X=[];this.sa=[];this.ma=[];this.ka=[];this.M={};this.pa=0};q(Jg,M);
+Jg.prototype.$=function(){var a;if(!(a=!t("Macintosh"))){a=Wa;var b="";t("Windows")?(b=/Windows (?:NT|Phone) ([0-9.]+)/,b=(a=b.exec(a))?a[1]:"0.0"):t("iPhone")&&!t("iPod")&&!t("iPad")||t("iPad")||t("iPod")?(b=/(?:iPhone|iPod|iPad|CPU)\s+OS\s+(\S+)/,b=(a=b.exec(a))&&a[1].replace(/_/g,".")):t("Macintosh")?(b=/Mac OS X ([0-9_.]+)/,b=(a=b.exec(a))?a[1].replace(/_/g,"."):"10"):t("Android")?(b=/Android\s+([^\);]+)(\)|;)/,b=(a=b.exec(a))&&a[1]):t("CrOS")&&(b=/(?:CrOS\s+(?:i686|x86_64)\s+([0-9.]+))/,b=(a=
+b.exec(a))&&a[1]);a=0<=Va(b||"","10.12.5")}this.f=a=ed(Ig,{da:this.Ca,ga:!!a+"",ha:!!t("CrOS")+"",I:this.Ga});this.a=a.querySelector("#ink-engine")};Jg.prototype.v=function(){var a=this;this.a.addEventListener("load",function(){Kg(a);a.C();Lg(a,5,a.qa)})};var Mg=function(a){v.call(this,"q");this.enabled=a};q(Mg,v);
+var Ng=function(a){a.a.postMessage(["poke",""])},Y=function(a,b){b=K(a.i,b);b=new Uint8Array(b);a.a.postMessage(["handleCommand",b.buffer])},Kg=function(a){var b=a.a;Og(a,a.J,a.L,a.K,a.G);a.oa||(a.oa=!0,b.addEventListener("message",sa(function(a){if("event_type"in a.data)switch(a=a.data,a.event_type){case "exit":console.log("Engine requested exit.");break;case "debug":console.log(a.message);break;case "image_export":this.Fa(a.width,a.height,new Uint8ClampedArray(a.bytes));break;case "element_added":if(this.m){var b=
+this.m,c=a.uuid,f=a.encoded_element;a=a.encoded_transform;b.G.add(c);x(b,new ge(c,f,a))}break;case "elements_mutated":this.m&&x(this.m,new he(a.uuids,a.encoded_transforms));break;case "elements_removed":this.m&&x(this.m,new ie(a.uuids));break;case "flag_changed":5==a.which&&(this.qa=a.enabled,x(this,new Mg(a.enabled)));break;case "undo_redo_state_changed":x(this,new Hg(!!a.can_undo,!!a.can_redo));break;case "snapshot_gotten":b=new U;Tc(this.i,b,a.snapshot);this.ra.shift().call(null,b);break;case "brix_elements_converted":b=
+new U;Tc(this.i,b,a.snapshot);a=new jf;c=new R;this.X.shift();f=null.Ka().get("bounds");I(c,2,f.wd||0);I(c,1,f.xd||0);I(c,4,f.yd||0);I(c,3,f.zd||0);I(a,3,c);I(b,1,a);this.W.shift().call(null,b);break;case "snapshot_has_pending_mutations":this.sa.shift().call(null,a.has_mutations);break;case "extracted_mutation_packet":b=new Af;Tc(this.i,b,a.extraction_packet);this.ma.shift().call(null,b);break;case "cleared_pending_mutations":b=new U;Tc(this.i,b,a.snapshot);this.ka.shift().call(null,b);break;case "hwoverlay":document.querySelector("#ink-engine-hwoverlay").style.display=
+a.enable?"none":"block";break;case "single_buffer":this.a.style.transform="scaleY(-1)";break;case "sequence_point_reached":a=a.id,b=this.M[a],delete this.M[a],b()}},a)));x(a,"o")},Pg=function(a,b){var c=new gf;I(c,1,Cg(b)[0]);b=new V;I(b,11,c);Y(a,b)},Qg=function(a,b,c,d,e){a.C=function(){if(1!=d){var f=new Vf;I(f,1,d);var h=new V;I(h,2,f);Y(this,h)}else a.a.postMessage(["updateBrush",{brush:e,rgba:b[0],stroke_width:c}])};a.C()},Lg=function(a,b,c){var d=new Yf;I(d,1,b);I(d,2,!!c);b=new V;I(b,7,d);
+Y(a,b)},Og=function(a,b,c,d,e){a.J=b;a.L=c;a.K=d;a.G=e;b=new R;I(b,1,a.J);I(b,3,a.G);I(b,2,a.K);I(b,4,a.L);c=new V;I(c,5,b);Y(a,c)};var Sg=function(a,b){var c=this;M.call(this);this.i=null;this.a=new Jg(a,this,sa(this.X,this),b);jd(this,this.a);rc(gd(this),this.a,sa(function(){this.K();Rg(this);x(this,"c")},this));pc(gd(this),this.a,"p",this.L);pc(gd(this),this.a,"q",function(a){x(c,new fe(a.enabled))});this.m=[];this.G=new Set;this.J=this.W=0;this.C=null;this.M=!1};q(Sg,M);Sg.prototype.v=function(){Sg.R.v.call(this);this.a.render(this.B());var a=gd(this);r(a);this.i=X.S(this);pc(a,this.i,"m",this.K)};
+Sg.prototype.X=function(a,b,c){var d=this;if(this.C){try{var e=new ImageData(c,a,b),f=document.createElement("canvas"),h=f.getContext("2d");f.width=a;f.height=b;h.putImageData(e,0,0)}catch(p){this.L(p);return}this.M?(a=function(a){d.C(Cb(a))},f.msToBlob?f.msToBlob(a,"image/png"):f.toBlob(a,"image/png")):this.C(Eb(f.toDataURL()))}};
+var Tg=function(a,b,c,d){d=d||{};var e="sketchology://background_"+a.J;a.J++;var f=new te;I(f,1,e);if("none"!=d.bounds){d=d.bounds||{xlow:0,ylow:0,xhigh:c.width,yhigh:c.height};var h=new R;I(h,1,d.xlow);I(h,3,d.ylow);I(h,2,d.xhigh);I(h,4,d.yhigh);I(f,3,h)}a=a.a;a.a.postMessage(["addImageData",{imageData:b.buffer,uri:e,width:c.width,height:c.height,assetType:0}]);b=new V;I(b,10,f);Y(a,b)};
+Sg.prototype.K=function(){r(this.i);var a=this.i.i,b=this.i.a,c=this.i.j,d=new Ag(parseInt(this.i.f.substring(1),16));d.j=255;Qg(this.a,Cg(d),c,a,b)};
+var Rg=function(a){var b=new ve;I(b,1,"sketchology://border0");I(b,2,1);de("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFgAAABYCAYAAABxlTA0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4AgPEBYrHoEFUgAAAw1JREFUeNrt3b9uE0EQBvBvZvd8LURUkVwEkEDAm9AgQYOQKHgZWloqUMoUaVJSpAivQBEhJGhSWiAlKXz7JwXey9nE9vrOgHT3fZKVFM5F/mUyN5tIY8F2EtG/yNYucnZ21keg/57d3V1RMvzdEJjABGYITGACM+1iNz5RxGGPzCIbnT+i3RA1AjgFcADgR4zRDwTVABjHGF8AeJwOaDnYNgd29jGGED6JyMvxeDwZYvVOJpN3FxcXH0TkWfMovQpac6p39jgdMi4A7Ozs/HLOvQkhfEkurW9yDVh47+GcOxgybsre3t5P59y+937OqHUFhxAQQkBVVd85E/yOc+5bcuk0pqWLeO/hvQ+krV289x45yLqqPSRk5xym0yllZ5lOp6iqCiGEtW3C5lZwzq/DUOK9h4jUwK3GtGYFz1oEZa97cA2crJaNapozoiVk5rqCm+2h898ihn487uKiWPMPy2arYG7G7TQHM91CYAITmCEwgQnMEJjABGYITGCGwAQmMENgAhOYITCBCUwCAhOYITCBCcwQmMAEZghMYIbABCYwQ2ACE5ghMIEJzBCYwAyBCUxghsAEJjBDYAIzBP63wFkLcUX4lhAp3nu79Qr23t8lbZ37WwNuLFx7fnJycnvossfHx7dijK+WGLWrYBFBjPHh5eXl/tHR0Z0h41ZV9T7G+EREum/AThcREaiqqOpTAJ8PDw8/VlX11TnnhgArIrYsy3vn5+evy7J8NLNADrJd1xpUFcaY9BBVfSAib2foAPq7GTAZGGNSkcFaC2stjDH161+BLGsrOAEXRYGyLOsNgKqK5hbovgIng/T6R6MRiqKogVtVcPqi5k+tKIo5XOfcYICNMbDWYjQaoSzLP4BXtYqVFayqiDHCWlsjJnDnXPYG0j5UcCqy9LDWtq/gZcipHxVFMbfitec3uMX70FwP7nyTW2zyaSt236v3pipO7UJVs9pDVgU3p4n0jZqwQwBeHFlzYLPn4MXPb4Lt+5i2CJ1zgsuu4GUX2vBNk3qJnvX8jOdwv3h7O1wBYIqaD5lCtYoAAAAASUVORK5CYII=",function(c,
+d){var e=a.a,f=new bg;I(f,1,3873892095);var h=new V;I(h,12,f);Y(e,h);e.a.postMessage(["addImageData",{imageData:c.buffer,uri:"sketchology://border0",width:d.width,height:d.height,assetType:1}]);h=new V;I(h,13,b);Y(e,h)})};Sg.prototype.L=function(a){if(x(this,new le(a)))throw a||Error("Unhandled fatal ink error");};var Ug=function(){M.call(this);this.a=null};q(Ug,M);Ug.prototype.v=function(){Ug.R.v.call(this);this.a=X.S(this);var a=gd(this);pc(a,this.a,"m",this.i);pc(a,ce(this),"a",this.m)};Ug.prototype.m=function(a){a.j?this.B().style.cursor="":this.i()};
+Ug.prototype.i=function(){var a=this.a.ua(),b=8,c=document.createElement("canvas"),d=c.getContext("2d");a=new Ag(a|4278190080);b=Math.max(b,2);var e=Math.ceil(2*b);c.width=e;c.height=e;d.fillStyle=Bg(127<.5*(Math.max(a.h,a.f,a.a)+Math.min(a.h,a.f,a.a))?Eg:Fg);d.beginPath();d.arc(b,b,b,0,2*Math.PI);d.closePath();d.fill();d.fillStyle=Bg(a);d.beginPath();d.arc(b,b,b-1,0,2*Math.PI);d.closePath();d.fill();b="url("+c.toDataURL()+")8 8, auto";this.B().style.cursor=b};var Vg=function(a,b,c){b=c||b;return kd('<div id="canvas-parent"><style'+(b&&b.ia?' nonce="'+pd(b&&b.ia)+'"':"")+'>\n #canvas-parent {\n height: 100%;\n position: relative;\n width: 100%;\n }\n #layer-container {\n height: 100%;\n position: relative;\n width: 100%;\n }\n #ink-engine {\n height: 100%;\n left: 0;\n position: absolute;\n top: 0;\n width: 100%;\n touch-action: none;\n }\n .above-ink-canvas {\n display: none;\n }\n </style><div class="above-ink-canvas"></div><div id="layer-container"></div><div class="below-ink-canvas"></div></div>')};
+Vg.a="ink.soy.embedContent";var Z=function(a,b){M.call(this);this.i=a;this.m=new Ug;jd(this,this.m);this.a=new Sg(a.f,a.I);jd(this,this.a);this.C=b};q(Z,M);g=Z.prototype;g.Va=function(){var a=new v("k");x(this,a);a.f||this.a.a.a.postMessage(["removeAll",""])};g.ub=function(){var a=new v("i");x(this,a);if(!a.f){a=this.a.a;var b=new V,c=new W;I(b,23,c);Y(a,b)}a=ee(this.i.a,7);x(this,new ke(a))};g.ib=function(){var a=new v("j");x(this,a);if(!a.f){a=this.a.a;var b=new V,c=new W;I(b,24,c);Y(a,b)}a=ee(this.i.a,8);x(this,new ke(a))};
+g.mb=function(a,b){var c=this;de(a,function(a,e){Tg(c.a,a,e);b&&b()})};g.qb=function(a,b){var c=this;de(a,function(a,e){Tg(c.a,a,e,{bounds:"none"});b&&b()})};g.lb=function(a){Pg(this.a.a,a)};
+g.pb=function(a,b,c,d,e,f){var h=this;de(a,function(a,u){var p=h.a,A=new xe;I(A,1,"sketchology://grid");I(A,2,Cg(b)[0]);I(A,3,c);var N=new Oe;I(N,1,d);I(N,2,e);I(A,4,N);p=p.a;p.a.postMessage(["addImageData",{imageData:a.buffer,uri:"sketchology://grid",width:u.width,height:u.height,assetType:3}]);a=new V;I(a,29,A);Y(p,a);f&&f()})};g.Wa=function(){var a=this.a.a,b=new V,c=new W;I(b,30,c);Y(a,b)};
+g.$a=function(a,b,c,d){var e=this.a;e.C=c;e.M=!!d;c=new Mf;I(c,1,a);I(c,2,b);a=e.a;b=new V;I(b,6,c);Y(a,b)};g.nb=function(a){var b=this.a.a,c=new V;I(c,16,a);Y(b,c)};g.rb=function(a,b,c,d){Og(this.a.a,a,b,c,d)};g.Za=function(){throw Error("deselectAll not yet implemented for NaCl.");};g.$=function(){this.f=ed(Vg)};
+g.v=function(){Z.R.v.call(this);var a=Jb("layer-container");this.a.render(a);var b=this.m;if(b.A)throw Error("Component already rendered");if(a){var c=Hb(a);b.U&&b.U.a==c||(b.U=Ib(a));b.f=a;b.v()}else throw Error("Invalid element to decorate");pc(gd(this),this.a,"c",sa(this.C,this,this))};g.vb=function(a){var b=Jb("canvas-parent");a?b.classList?b.classList.add("fullscreen"):ub(b)||(b.className+=0<b.className.length?" fullscreen":"fullscreen"):vb(b)};
+g.Sa=function(a,b){var c=this.a;a.id||(a.id="local-"+c.W++);var d=a.id;Ia(c.m,b,0,d);c.G.has(d)?c.G.delete(d):b<c.m.length-1?(d=c.a,b=c.m[b+1],r(a),d.a.postMessage(["addElementToEngineBelow",{bundle:a,below_uuid:b}])):(b=c.a,r(a),b.a.postMessage(["addElementToEngine",{bundle:a}]));Ng(c.a)};g.jb=function(a,b){for(var c=this.a,d=0;d<b;d++)c.a.a.postMessage(["removeElement",c.m[a]]),Ea(c.m,a);Ng(c.a)};g.kb=function(){var a=this.a;a.a.a.postMessage(["clear",""]);Pg(a.a,Gg);a.G.clear();a.m=[];Ng(a.a)};
+g.sb=function(a){Lg(this.a.a,1,!!a)};g.ob=function(a,b){if(a.length!==b.length)throw Error("mismatch in transform array lengths");this.a.a.a.postMessage(["setElementTransforms",{uuids:a,encoded_transforms:b}])};g.gb=function(a){var b=new je(a);x(this,b);b.f||a(void 0)};g.Ha=function(){var a=this.a.f;r(a,"Can not call getElementStrict before rendering/decorating.");a=a.querySelector("canvas,embed");return new Fb(a.clientWidth,a.clientHeight)};g.Ja=function(){return this.i.a};
+g.Ua=function(a,b){Lg(this.a.a,a,b)};g.eb=function(a){if("makeSEngineInMemory"!==this.i.I)throw Error("Can't getSnapshot without sengineType IN_MEMORY.");var b=this.a.a;b.ra.push(a);b.a.postMessage(["getSnapshot"])};g.hb=function(a){if("makeSEngineInMemory"!==this.i.I)throw Error("Can't loadFromSnapshot without sengineType IN_MEMORY.");var b=this.a.a;a=K(b.i,a);a=new Uint8Array(a);b.a.postMessage(["loadFromSnapshot",a.buffer])};g.fb=function(a){Y(this.a.a,a)};
+g.cb=function(){throw Error("getRawEngineObject not supported for NaCl.");};g.Ya=function(a,b){var c=this.a.a,d=null.Ka().get("pages").get(0);if(!d)throw Error("unable to get page from brix document.");d=d.get("elements").qd();for(var e=[],f=0;f<d.length;f++){var h=d[f];e.push({id:h.get("id"),proto:h.get("proto"),transform:h.get("transform")})}c.X.push(a);c.W.push(b);c.a.postMessage(["convertBrixElements",e])};
+g.tb=function(a,b){var c=this.a.a;a=K(c.i,a);a=new Uint8Array(a);c.sa.push(b);c.a.postMessage(["snapshotHasPendingMutations",a.buffer])};g.ab=function(a,b){var c=this.a.a;a=K(c.i,a);a=new Uint8Array(a);c.ma.push(b);c.a.postMessage(["extractMutationPacket",a.buffer])};g.Xa=function(a,b){var c=this.a.a;a=K(c.i,a);a=new Uint8Array(a);c.ka.push(b);c.a.postMessage(["clearPendingMutations",a.buffer])};g.bb=function(a){var b=this.a.a,c=new V,d=new ig;I(d,1,b.pa);b.M[b.pa++]=a;I(c,15,d);Y(b,c)};
+g.Ta=function(a){de(a,function(){throw Error("stickers not implemented on nacl yet");})};ka("ink.embed.Config",function(a){a=a||{};this.j=a.parentEl||null;this.h=a.parentComponent||null;this.f=a.nativeClientManifestUrl||null;this.a=a.logsHost||0;this.I=a.sengineType||"makeSEnginePassthroughDocument"});ka("ink.embed.EmbedComponent",Z);Z.execute=function(a,b){b=new Z(a,b);hd(b,a.h);b.render(a.j)};Z.prototype.clear=Z.prototype.Va;Z.prototype.undo=Z.prototype.ub;Z.prototype.redo=Z.prototype.ib;Z.prototype.setBackgroundImage=Z.prototype.mb;Z.prototype.setImageToUseForPageBackground=Z.prototype.qb;
+Z.prototype.setBackgroundColor=Z.prototype.lb;Z.prototype.setGrid=Z.prototype.pb;Z.prototype.clearGrid=Z.prototype.Wa;Z.prototype.exportPng=Z.prototype.$a;Z.prototype.setCallbackFlags=Z.prototype.nb;Z.prototype.setPageBounds=Z.prototype.rb;Z.prototype.deselectAll=Z.prototype.Za;Z.prototype.createDom=Z.prototype.$;Z.prototype.enterDocument=Z.prototype.v;Z.prototype.setFullscreen=Z.prototype.vb;Z.prototype.addElement=Z.prototype.Sa;Z.prototype.removeElements=Z.prototype.jb;Z.prototype.resetCanvas=Z.prototype.kb;
+Z.prototype.setReadOnly=Z.prototype.sb;Z.prototype.setElementTransforms=Z.prototype.ob;Z.prototype.isEmpty=Z.prototype.gb;Z.prototype.getCanvasDimensions=Z.prototype.Ha;Z.prototype.getLogsHost=Z.prototype.Ja;Z.prototype.assignFlag=Z.prototype.Ua;Z.prototype.getSnapshot=Z.prototype.eb;Z.prototype.loadFromSnapshot=Z.prototype.hb;Z.prototype.handleCommand=Z.prototype.fb;Z.prototype.getRawEngineObject=Z.prototype.cb;Z.prototype.convertBrixDocumentToSnapshot=Z.prototype.Ya;
+Z.prototype.snapshotHasPendingMutations=Z.prototype.tb;Z.prototype.extractMutationPacket=Z.prototype.ab;Z.prototype.clearPendingMutations=Z.prototype.Xa;Z.prototype.flush=Z.prototype.bb;Z.prototype.addSticker=Z.prototype.Ta;ka("ink.BrushModel",X);X.getInstance=X.S;X.prototype.setColor=X.prototype.Qa;X.prototype.setStrokeWidth=X.prototype.Ra;X.prototype.setIsErasing=X.prototype.wb;X.prototype.setShape=X.prototype.xb;X.prototype.getShape=X.prototype.La;X.prototype.getColor=X.prototype.Na;
+X.prototype.getActiveColor=X.prototype.ta;X.prototype.getActiveColorNumericRbg=X.prototype.ua;X.prototype.getStrokeWidth=X.prototype.Oa;X.prototype.getIsErasing=X.prototype.Ia;X.prototype.getBrushType=X.prototype.Ma;X.prototype.getToolType=X.prototype.Pa;
diff --git a/chromium/third_party/ink/sketchology/proto/animations.pb.js b/chromium/third_party/ink/sketchology/proto/animations.pb.js
new file mode 100644
index 00000000000..da7702c4375
--- /dev/null
+++ b/chromium/third_party/ink/sketchology/proto/animations.pb.js
@@ -0,0 +1,984 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+// Protocol Buffer 2 Copyright 2008 Google Inc.
+// All other code copyright its respective owners.
+
+/**
+ * @fileoverview Generated Protocol Buffer code for file
+ * third_party/sketchology/proto/animations.proto.
+ * Generated by //net/proto2/compiler/public:protocol_compiler.
+ * @suppress {messageConventions}
+ */
+
+goog.provide('sketchology.proto.AnimationCurve');
+goog.provide('sketchology.proto.ColorAnimation');
+goog.provide('sketchology.proto.ScaleAnimation');
+goog.provide('sketchology.proto.ElementAnimation');
+goog.provide('sketchology.proto.CurveType');
+
+goog.require('goog.proto2.Message');
+
+
+/**
+ * Enumeration CurveType.
+ * @enum {number}
+ */
+sketchology.proto.CurveType = {
+ UNSPECIFIED_CURVE_TYPE: 0,
+ EASE_IN_OUT: 1,
+ EASE_IN: 2,
+ EASE_OUT: 3,
+ CUSTOM_CUBIC_BEZIER: 4
+};
+
+
+
+/**
+ * Message AnimationCurve.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.AnimationCurve = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.AnimationCurve, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.AnimationCurve.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.AnimationCurve} The cloned message.
+ * @override
+ */
+sketchology.proto.AnimationCurve.prototype.clone;
+
+
+/**
+ * Gets the value of the type field.
+ * @return {?sketchology.proto.CurveType} The value.
+ */
+sketchology.proto.AnimationCurve.prototype.getType = function() {
+ return /** @type {?sketchology.proto.CurveType} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the type field or the default value if not set.
+ * @return {!sketchology.proto.CurveType} The value.
+ */
+sketchology.proto.AnimationCurve.prototype.getTypeOrDefault = function() {
+ return /** @type {!sketchology.proto.CurveType} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the type field.
+ * @param {!sketchology.proto.CurveType} value The value.
+ */
+sketchology.proto.AnimationCurve.prototype.setType = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the type field has a value.
+ */
+sketchology.proto.AnimationCurve.prototype.hasType = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the type field.
+ */
+sketchology.proto.AnimationCurve.prototype.typeCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the type field.
+ */
+sketchology.proto.AnimationCurve.prototype.clearType = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the params field at the index given.
+ * @param {number} index The index to lookup.
+ * @return {?number} The value.
+ */
+sketchology.proto.AnimationCurve.prototype.getParams = function(index) {
+ return /** @type {?number} */ (this.get$Value(2, index));
+};
+
+
+/**
+ * Gets the value of the params field at the index given or the default value if not set.
+ * @param {number} index The index to lookup.
+ * @return {number} The value.
+ */
+sketchology.proto.AnimationCurve.prototype.getParamsOrDefault = function(index) {
+ return /** @type {number} */ (this.get$ValueOrDefault(2, index));
+};
+
+
+/**
+ * Adds a value to the params field.
+ * @param {number} value The value to add.
+ */
+sketchology.proto.AnimationCurve.prototype.addParams = function(value) {
+ this.add$Value(2, value);
+};
+
+
+/**
+ * Returns the array of values in the params field.
+ * @return {!Array<number>} The values in the field.
+ */
+sketchology.proto.AnimationCurve.prototype.paramsArray = function() {
+ return /** @type {!Array<number>} */ (this.array$Values(2));
+};
+
+
+/**
+ * @return {boolean} Whether the params field has a value.
+ */
+sketchology.proto.AnimationCurve.prototype.hasParams = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the params field.
+ */
+sketchology.proto.AnimationCurve.prototype.paramsCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the params field.
+ */
+sketchology.proto.AnimationCurve.prototype.clearParams = function() {
+ this.clear$Field(2);
+};
+
+
+
+/**
+ * Message ColorAnimation.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.ColorAnimation = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.ColorAnimation, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.ColorAnimation.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.ColorAnimation} The cloned message.
+ * @override
+ */
+sketchology.proto.ColorAnimation.prototype.clone;
+
+
+/**
+ * Gets the value of the duration field.
+ * @return {?number} The value.
+ */
+sketchology.proto.ColorAnimation.prototype.getDuration = function() {
+ return /** @type {?number} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the duration field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.ColorAnimation.prototype.getDurationOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the duration field.
+ * @param {number} value The value.
+ */
+sketchology.proto.ColorAnimation.prototype.setDuration = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the duration field has a value.
+ */
+sketchology.proto.ColorAnimation.prototype.hasDuration = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the duration field.
+ */
+sketchology.proto.ColorAnimation.prototype.durationCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the duration field.
+ */
+sketchology.proto.ColorAnimation.prototype.clearDuration = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the curve field.
+ * @return {?sketchology.proto.AnimationCurve} The value.
+ */
+sketchology.proto.ColorAnimation.prototype.getCurve = function() {
+ return /** @type {?sketchology.proto.AnimationCurve} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the curve field or the default value if not set.
+ * @return {!sketchology.proto.AnimationCurve} The value.
+ */
+sketchology.proto.ColorAnimation.prototype.getCurveOrDefault = function() {
+ return /** @type {!sketchology.proto.AnimationCurve} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the curve field.
+ * @param {!sketchology.proto.AnimationCurve} value The value.
+ */
+sketchology.proto.ColorAnimation.prototype.setCurve = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the curve field has a value.
+ */
+sketchology.proto.ColorAnimation.prototype.hasCurve = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the curve field.
+ */
+sketchology.proto.ColorAnimation.prototype.curveCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the curve field.
+ */
+sketchology.proto.ColorAnimation.prototype.clearCurve = function() {
+ this.clear$Field(2);
+};
+
+
+/**
+ * Gets the value of the rgba field.
+ * @return {?number} The value.
+ */
+sketchology.proto.ColorAnimation.prototype.getRgba = function() {
+ return /** @type {?number} */ (this.get$Value(3));
+};
+
+
+/**
+ * Gets the value of the rgba field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.ColorAnimation.prototype.getRgbaOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(3));
+};
+
+
+/**
+ * Sets the value of the rgba field.
+ * @param {number} value The value.
+ */
+sketchology.proto.ColorAnimation.prototype.setRgba = function(value) {
+ this.set$Value(3, value);
+};
+
+
+/**
+ * @return {boolean} Whether the rgba field has a value.
+ */
+sketchology.proto.ColorAnimation.prototype.hasRgba = function() {
+ return this.has$Value(3);
+};
+
+
+/**
+ * @return {number} The number of values in the rgba field.
+ */
+sketchology.proto.ColorAnimation.prototype.rgbaCount = function() {
+ return this.count$Values(3);
+};
+
+
+/**
+ * Clears the values in the rgba field.
+ */
+sketchology.proto.ColorAnimation.prototype.clearRgba = function() {
+ this.clear$Field(3);
+};
+
+
+
+/**
+ * Message ScaleAnimation.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.ScaleAnimation = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.ScaleAnimation, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.ScaleAnimation.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.ScaleAnimation} The cloned message.
+ * @override
+ */
+sketchology.proto.ScaleAnimation.prototype.clone;
+
+
+/**
+ * Gets the value of the duration field.
+ * @return {?number} The value.
+ */
+sketchology.proto.ScaleAnimation.prototype.getDuration = function() {
+ return /** @type {?number} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the duration field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.ScaleAnimation.prototype.getDurationOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the duration field.
+ * @param {number} value The value.
+ */
+sketchology.proto.ScaleAnimation.prototype.setDuration = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the duration field has a value.
+ */
+sketchology.proto.ScaleAnimation.prototype.hasDuration = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the duration field.
+ */
+sketchology.proto.ScaleAnimation.prototype.durationCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the duration field.
+ */
+sketchology.proto.ScaleAnimation.prototype.clearDuration = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the curve field.
+ * @return {?sketchology.proto.AnimationCurve} The value.
+ */
+sketchology.proto.ScaleAnimation.prototype.getCurve = function() {
+ return /** @type {?sketchology.proto.AnimationCurve} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the curve field or the default value if not set.
+ * @return {!sketchology.proto.AnimationCurve} The value.
+ */
+sketchology.proto.ScaleAnimation.prototype.getCurveOrDefault = function() {
+ return /** @type {!sketchology.proto.AnimationCurve} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the curve field.
+ * @param {!sketchology.proto.AnimationCurve} value The value.
+ */
+sketchology.proto.ScaleAnimation.prototype.setCurve = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the curve field has a value.
+ */
+sketchology.proto.ScaleAnimation.prototype.hasCurve = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the curve field.
+ */
+sketchology.proto.ScaleAnimation.prototype.curveCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the curve field.
+ */
+sketchology.proto.ScaleAnimation.prototype.clearCurve = function() {
+ this.clear$Field(2);
+};
+
+
+/**
+ * Gets the value of the scale_x field.
+ * @return {?number} The value.
+ */
+sketchology.proto.ScaleAnimation.prototype.getScaleX = function() {
+ return /** @type {?number} */ (this.get$Value(3));
+};
+
+
+/**
+ * Gets the value of the scale_x field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.ScaleAnimation.prototype.getScaleXOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(3));
+};
+
+
+/**
+ * Sets the value of the scale_x field.
+ * @param {number} value The value.
+ */
+sketchology.proto.ScaleAnimation.prototype.setScaleX = function(value) {
+ this.set$Value(3, value);
+};
+
+
+/**
+ * @return {boolean} Whether the scale_x field has a value.
+ */
+sketchology.proto.ScaleAnimation.prototype.hasScaleX = function() {
+ return this.has$Value(3);
+};
+
+
+/**
+ * @return {number} The number of values in the scale_x field.
+ */
+sketchology.proto.ScaleAnimation.prototype.scaleXCount = function() {
+ return this.count$Values(3);
+};
+
+
+/**
+ * Clears the values in the scale_x field.
+ */
+sketchology.proto.ScaleAnimation.prototype.clearScaleX = function() {
+ this.clear$Field(3);
+};
+
+
+/**
+ * Gets the value of the scale_y field.
+ * @return {?number} The value.
+ */
+sketchology.proto.ScaleAnimation.prototype.getScaleY = function() {
+ return /** @type {?number} */ (this.get$Value(4));
+};
+
+
+/**
+ * Gets the value of the scale_y field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.ScaleAnimation.prototype.getScaleYOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(4));
+};
+
+
+/**
+ * Sets the value of the scale_y field.
+ * @param {number} value The value.
+ */
+sketchology.proto.ScaleAnimation.prototype.setScaleY = function(value) {
+ this.set$Value(4, value);
+};
+
+
+/**
+ * @return {boolean} Whether the scale_y field has a value.
+ */
+sketchology.proto.ScaleAnimation.prototype.hasScaleY = function() {
+ return this.has$Value(4);
+};
+
+
+/**
+ * @return {number} The number of values in the scale_y field.
+ */
+sketchology.proto.ScaleAnimation.prototype.scaleYCount = function() {
+ return this.count$Values(4);
+};
+
+
+/**
+ * Clears the values in the scale_y field.
+ */
+sketchology.proto.ScaleAnimation.prototype.clearScaleY = function() {
+ this.clear$Field(4);
+};
+
+
+
+/**
+ * Message ElementAnimation.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.ElementAnimation = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.ElementAnimation, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.ElementAnimation.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.ElementAnimation} The cloned message.
+ * @override
+ */
+sketchology.proto.ElementAnimation.prototype.clone;
+
+
+/**
+ * Gets the value of the uuid field.
+ * @return {?string} The value.
+ */
+sketchology.proto.ElementAnimation.prototype.getUuid = function() {
+ return /** @type {?string} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the uuid field or the default value if not set.
+ * @return {string} The value.
+ */
+sketchology.proto.ElementAnimation.prototype.getUuidOrDefault = function() {
+ return /** @type {string} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the uuid field.
+ * @param {string} value The value.
+ */
+sketchology.proto.ElementAnimation.prototype.setUuid = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the uuid field has a value.
+ */
+sketchology.proto.ElementAnimation.prototype.hasUuid = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the uuid field.
+ */
+sketchology.proto.ElementAnimation.prototype.uuidCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the uuid field.
+ */
+sketchology.proto.ElementAnimation.prototype.clearUuid = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the color_animation field.
+ * @return {?sketchology.proto.ColorAnimation} The value.
+ */
+sketchology.proto.ElementAnimation.prototype.getColorAnimation = function() {
+ return /** @type {?sketchology.proto.ColorAnimation} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the color_animation field or the default value if not set.
+ * @return {!sketchology.proto.ColorAnimation} The value.
+ */
+sketchology.proto.ElementAnimation.prototype.getColorAnimationOrDefault = function() {
+ return /** @type {!sketchology.proto.ColorAnimation} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the color_animation field.
+ * @param {!sketchology.proto.ColorAnimation} value The value.
+ */
+sketchology.proto.ElementAnimation.prototype.setColorAnimation = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the color_animation field has a value.
+ */
+sketchology.proto.ElementAnimation.prototype.hasColorAnimation = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the color_animation field.
+ */
+sketchology.proto.ElementAnimation.prototype.colorAnimationCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the color_animation field.
+ */
+sketchology.proto.ElementAnimation.prototype.clearColorAnimation = function() {
+ this.clear$Field(2);
+};
+
+
+/**
+ * Gets the value of the scale_animation field.
+ * @return {?sketchology.proto.ScaleAnimation} The value.
+ */
+sketchology.proto.ElementAnimation.prototype.getScaleAnimation = function() {
+ return /** @type {?sketchology.proto.ScaleAnimation} */ (this.get$Value(3));
+};
+
+
+/**
+ * Gets the value of the scale_animation field or the default value if not set.
+ * @return {!sketchology.proto.ScaleAnimation} The value.
+ */
+sketchology.proto.ElementAnimation.prototype.getScaleAnimationOrDefault = function() {
+ return /** @type {!sketchology.proto.ScaleAnimation} */ (this.get$ValueOrDefault(3));
+};
+
+
+/**
+ * Sets the value of the scale_animation field.
+ * @param {!sketchology.proto.ScaleAnimation} value The value.
+ */
+sketchology.proto.ElementAnimation.prototype.setScaleAnimation = function(value) {
+ this.set$Value(3, value);
+};
+
+
+/**
+ * @return {boolean} Whether the scale_animation field has a value.
+ */
+sketchology.proto.ElementAnimation.prototype.hasScaleAnimation = function() {
+ return this.has$Value(3);
+};
+
+
+/**
+ * @return {number} The number of values in the scale_animation field.
+ */
+sketchology.proto.ElementAnimation.prototype.scaleAnimationCount = function() {
+ return this.count$Values(3);
+};
+
+
+/**
+ * Clears the values in the scale_animation field.
+ */
+sketchology.proto.ElementAnimation.prototype.clearScaleAnimation = function() {
+ this.clear$Field(3);
+};
+
+
+/**
+ * Gets the value of the next field.
+ * @return {?sketchology.proto.ElementAnimation} The value.
+ */
+sketchology.proto.ElementAnimation.prototype.getNext = function() {
+ return /** @type {?sketchology.proto.ElementAnimation} */ (this.get$Value(4));
+};
+
+
+/**
+ * Gets the value of the next field or the default value if not set.
+ * @return {!sketchology.proto.ElementAnimation} The value.
+ */
+sketchology.proto.ElementAnimation.prototype.getNextOrDefault = function() {
+ return /** @type {!sketchology.proto.ElementAnimation} */ (this.get$ValueOrDefault(4));
+};
+
+
+/**
+ * Sets the value of the next field.
+ * @param {!sketchology.proto.ElementAnimation} value The value.
+ */
+sketchology.proto.ElementAnimation.prototype.setNext = function(value) {
+ this.set$Value(4, value);
+};
+
+
+/**
+ * @return {boolean} Whether the next field has a value.
+ */
+sketchology.proto.ElementAnimation.prototype.hasNext = function() {
+ return this.has$Value(4);
+};
+
+
+/**
+ * @return {number} The number of values in the next field.
+ */
+sketchology.proto.ElementAnimation.prototype.nextCount = function() {
+ return this.count$Values(4);
+};
+
+
+/**
+ * Clears the values in the next field.
+ */
+sketchology.proto.ElementAnimation.prototype.clearNext = function() {
+ this.clear$Field(4);
+};
+
+
+/** @override */
+sketchology.proto.AnimationCurve.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.AnimationCurve.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'AnimationCurve',
+ fullName: 'sketchology.proto.AnimationCurve'
+ },
+ 1: {
+ name: 'type',
+ fieldType: goog.proto2.Message.FieldType.ENUM,
+ defaultValue: sketchology.proto.CurveType.EASE_IN_OUT,
+ type: sketchology.proto.CurveType
+ },
+ 2: {
+ name: 'params',
+ repeated: true,
+ fieldType: goog.proto2.Message.FieldType.FLOAT,
+ type: Number
+ }
+ };
+ sketchology.proto.AnimationCurve.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.AnimationCurve, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.AnimationCurve.getDescriptor =
+ sketchology.proto.AnimationCurve.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.ColorAnimation.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.ColorAnimation.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'ColorAnimation',
+ fullName: 'sketchology.proto.ColorAnimation'
+ },
+ 1: {
+ name: 'duration',
+ fieldType: goog.proto2.Message.FieldType.DOUBLE,
+ defaultValue: 0.5,
+ type: Number
+ },
+ 2: {
+ name: 'curve',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.AnimationCurve
+ },
+ 3: {
+ name: 'rgba',
+ fieldType: goog.proto2.Message.FieldType.UINT32,
+ type: Number
+ }
+ };
+ sketchology.proto.ColorAnimation.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.ColorAnimation, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.ColorAnimation.getDescriptor =
+ sketchology.proto.ColorAnimation.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.ScaleAnimation.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.ScaleAnimation.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'ScaleAnimation',
+ fullName: 'sketchology.proto.ScaleAnimation'
+ },
+ 1: {
+ name: 'duration',
+ fieldType: goog.proto2.Message.FieldType.DOUBLE,
+ defaultValue: 0.5,
+ type: Number
+ },
+ 2: {
+ name: 'curve',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.AnimationCurve
+ },
+ 3: {
+ name: 'scale_x',
+ fieldType: goog.proto2.Message.FieldType.FLOAT,
+ type: Number
+ },
+ 4: {
+ name: 'scale_y',
+ fieldType: goog.proto2.Message.FieldType.FLOAT,
+ type: Number
+ }
+ };
+ sketchology.proto.ScaleAnimation.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.ScaleAnimation, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.ScaleAnimation.getDescriptor =
+ sketchology.proto.ScaleAnimation.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.ElementAnimation.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.ElementAnimation.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'ElementAnimation',
+ fullName: 'sketchology.proto.ElementAnimation'
+ },
+ 1: {
+ name: 'uuid',
+ fieldType: goog.proto2.Message.FieldType.STRING,
+ type: String
+ },
+ 2: {
+ name: 'color_animation',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.ColorAnimation
+ },
+ 3: {
+ name: 'scale_animation',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.ScaleAnimation
+ },
+ 4: {
+ name: 'next',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.ElementAnimation
+ }
+ };
+ sketchology.proto.ElementAnimation.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.ElementAnimation, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.ElementAnimation.getDescriptor =
+ sketchology.proto.ElementAnimation.prototype.getDescriptor;
diff --git a/chromium/third_party/ink/sketchology/proto/document.pb.js b/chromium/third_party/ink/sketchology/proto/document.pb.js
new file mode 100644
index 00000000000..1826d1c5cca
--- /dev/null
+++ b/chromium/third_party/ink/sketchology/proto/document.pb.js
@@ -0,0 +1,2831 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+// Protocol Buffer 2 Copyright 2008 Google Inc.
+// All other code copyright its respective owners.
+
+/**
+ * @fileoverview Generated Protocol Buffer code for file
+ * third_party/sketchology/proto/document.proto.
+ * Generated by //net/proto2/compiler/public:protocol_compiler.
+ * @suppress {messageConventions}
+ */
+
+goog.provide('sketchology.proto.Color');
+goog.provide('sketchology.proto.BackgroundColor');
+goog.provide('sketchology.proto.PageProperties');
+goog.provide('sketchology.proto.AddAction');
+goog.provide('sketchology.proto.RemoveAction');
+goog.provide('sketchology.proto.ClearAction');
+goog.provide('sketchology.proto.ReplaceAction');
+goog.provide('sketchology.proto.SetTransformAction');
+goog.provide('sketchology.proto.SetPageBoundsAction');
+goog.provide('sketchology.proto.StorageAction');
+goog.provide('sketchology.proto.Snapshot');
+goog.provide('sketchology.proto.MutationPacket');
+goog.provide('sketchology.proto.StorageActionState');
+goog.provide('sketchology.proto.ElementState');
+
+goog.require('goog.proto2.Message');
+goog.require('sketchology.proto.AffineTransform');
+goog.require('sketchology.proto.BackgroundImageInfo');
+goog.require('sketchology.proto.Border');
+goog.require('sketchology.proto.ElementBundle');
+goog.require('sketchology.proto.Rect');
+
+
+/**
+ * Enumeration StorageActionState.
+ * @enum {number}
+ */
+sketchology.proto.StorageActionState = {
+ APPLIED: 1,
+ UNDONE: 2
+};
+
+
+/**
+ * Enumeration ElementState.
+ * @enum {number}
+ */
+sketchology.proto.ElementState = {
+ ALIVE: 1,
+ DEAD: 2
+};
+
+
+
+/**
+ * Message Color.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.Color = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.Color, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.Color.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.Color} The cloned message.
+ * @override
+ */
+sketchology.proto.Color.prototype.clone;
+
+
+/**
+ * Gets the value of the argb field.
+ * @return {?number} The value.
+ */
+sketchology.proto.Color.prototype.getArgb = function() {
+ return /** @type {?number} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the argb field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.Color.prototype.getArgbOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the argb field.
+ * @param {number} value The value.
+ */
+sketchology.proto.Color.prototype.setArgb = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the argb field has a value.
+ */
+sketchology.proto.Color.prototype.hasArgb = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the argb field.
+ */
+sketchology.proto.Color.prototype.argbCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the argb field.
+ */
+sketchology.proto.Color.prototype.clearArgb = function() {
+ this.clear$Field(1);
+};
+
+
+
+/**
+ * Message BackgroundColor.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.BackgroundColor = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.BackgroundColor, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.BackgroundColor.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.BackgroundColor} The cloned message.
+ * @override
+ */
+sketchology.proto.BackgroundColor.prototype.clone;
+
+
+/**
+ * Gets the value of the rgba field.
+ * @return {?number} The value.
+ */
+sketchology.proto.BackgroundColor.prototype.getRgba = function() {
+ return /** @type {?number} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the rgba field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.BackgroundColor.prototype.getRgbaOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the rgba field.
+ * @param {number} value The value.
+ */
+sketchology.proto.BackgroundColor.prototype.setRgba = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the rgba field has a value.
+ */
+sketchology.proto.BackgroundColor.prototype.hasRgba = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the rgba field.
+ */
+sketchology.proto.BackgroundColor.prototype.rgbaCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the rgba field.
+ */
+sketchology.proto.BackgroundColor.prototype.clearRgba = function() {
+ this.clear$Field(1);
+};
+
+
+
+/**
+ * Message PageProperties.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.PageProperties = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.PageProperties, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.PageProperties.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.PageProperties} The cloned message.
+ * @override
+ */
+sketchology.proto.PageProperties.prototype.clone;
+
+
+/**
+ * Gets the value of the background_color field.
+ * @return {?sketchology.proto.Color} The value.
+ */
+sketchology.proto.PageProperties.prototype.getBackgroundColor = function() {
+ return /** @type {?sketchology.proto.Color} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the background_color field or the default value if not set.
+ * @return {!sketchology.proto.Color} The value.
+ */
+sketchology.proto.PageProperties.prototype.getBackgroundColorOrDefault = function() {
+ return /** @type {!sketchology.proto.Color} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the background_color field.
+ * @param {!sketchology.proto.Color} value The value.
+ */
+sketchology.proto.PageProperties.prototype.setBackgroundColor = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the background_color field has a value.
+ */
+sketchology.proto.PageProperties.prototype.hasBackgroundColor = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the background_color field.
+ */
+sketchology.proto.PageProperties.prototype.backgroundColorCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the background_color field.
+ */
+sketchology.proto.PageProperties.prototype.clearBackgroundColor = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the background_image field.
+ * @return {?sketchology.proto.BackgroundImageInfo} The value.
+ */
+sketchology.proto.PageProperties.prototype.getBackgroundImage = function() {
+ return /** @type {?sketchology.proto.BackgroundImageInfo} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the background_image field or the default value if not set.
+ * @return {!sketchology.proto.BackgroundImageInfo} The value.
+ */
+sketchology.proto.PageProperties.prototype.getBackgroundImageOrDefault = function() {
+ return /** @type {!sketchology.proto.BackgroundImageInfo} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the background_image field.
+ * @param {!sketchology.proto.BackgroundImageInfo} value The value.
+ */
+sketchology.proto.PageProperties.prototype.setBackgroundImage = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the background_image field has a value.
+ */
+sketchology.proto.PageProperties.prototype.hasBackgroundImage = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the background_image field.
+ */
+sketchology.proto.PageProperties.prototype.backgroundImageCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the background_image field.
+ */
+sketchology.proto.PageProperties.prototype.clearBackgroundImage = function() {
+ this.clear$Field(2);
+};
+
+
+/**
+ * Gets the value of the bounds field.
+ * @return {?sketchology.proto.Rect} The value.
+ */
+sketchology.proto.PageProperties.prototype.getBounds = function() {
+ return /** @type {?sketchology.proto.Rect} */ (this.get$Value(3));
+};
+
+
+/**
+ * Gets the value of the bounds field or the default value if not set.
+ * @return {!sketchology.proto.Rect} The value.
+ */
+sketchology.proto.PageProperties.prototype.getBoundsOrDefault = function() {
+ return /** @type {!sketchology.proto.Rect} */ (this.get$ValueOrDefault(3));
+};
+
+
+/**
+ * Sets the value of the bounds field.
+ * @param {!sketchology.proto.Rect} value The value.
+ */
+sketchology.proto.PageProperties.prototype.setBounds = function(value) {
+ this.set$Value(3, value);
+};
+
+
+/**
+ * @return {boolean} Whether the bounds field has a value.
+ */
+sketchology.proto.PageProperties.prototype.hasBounds = function() {
+ return this.has$Value(3);
+};
+
+
+/**
+ * @return {number} The number of values in the bounds field.
+ */
+sketchology.proto.PageProperties.prototype.boundsCount = function() {
+ return this.count$Values(3);
+};
+
+
+/**
+ * Clears the values in the bounds field.
+ */
+sketchology.proto.PageProperties.prototype.clearBounds = function() {
+ this.clear$Field(3);
+};
+
+
+/**
+ * Gets the value of the border field.
+ * @return {?sketchology.proto.Border} The value.
+ */
+sketchology.proto.PageProperties.prototype.getBorder = function() {
+ return /** @type {?sketchology.proto.Border} */ (this.get$Value(4));
+};
+
+
+/**
+ * Gets the value of the border field or the default value if not set.
+ * @return {!sketchology.proto.Border} The value.
+ */
+sketchology.proto.PageProperties.prototype.getBorderOrDefault = function() {
+ return /** @type {!sketchology.proto.Border} */ (this.get$ValueOrDefault(4));
+};
+
+
+/**
+ * Sets the value of the border field.
+ * @param {!sketchology.proto.Border} value The value.
+ */
+sketchology.proto.PageProperties.prototype.setBorder = function(value) {
+ this.set$Value(4, value);
+};
+
+
+/**
+ * @return {boolean} Whether the border field has a value.
+ */
+sketchology.proto.PageProperties.prototype.hasBorder = function() {
+ return this.has$Value(4);
+};
+
+
+/**
+ * @return {number} The number of values in the border field.
+ */
+sketchology.proto.PageProperties.prototype.borderCount = function() {
+ return this.count$Values(4);
+};
+
+
+/**
+ * Clears the values in the border field.
+ */
+sketchology.proto.PageProperties.prototype.clearBorder = function() {
+ this.clear$Field(4);
+};
+
+
+
+/**
+ * Message AddAction.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.AddAction = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.AddAction, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.AddAction.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.AddAction} The cloned message.
+ * @override
+ */
+sketchology.proto.AddAction.prototype.clone;
+
+
+/**
+ * Gets the value of the uuid field.
+ * @return {?string} The value.
+ */
+sketchology.proto.AddAction.prototype.getUuid = function() {
+ return /** @type {?string} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the uuid field or the default value if not set.
+ * @return {string} The value.
+ */
+sketchology.proto.AddAction.prototype.getUuidOrDefault = function() {
+ return /** @type {string} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the uuid field.
+ * @param {string} value The value.
+ */
+sketchology.proto.AddAction.prototype.setUuid = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the uuid field has a value.
+ */
+sketchology.proto.AddAction.prototype.hasUuid = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the uuid field.
+ */
+sketchology.proto.AddAction.prototype.uuidCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the uuid field.
+ */
+sketchology.proto.AddAction.prototype.clearUuid = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the below_element_with_uuid field.
+ * @return {?string} The value.
+ */
+sketchology.proto.AddAction.prototype.getBelowElementWithUuid = function() {
+ return /** @type {?string} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the below_element_with_uuid field or the default value if not set.
+ * @return {string} The value.
+ */
+sketchology.proto.AddAction.prototype.getBelowElementWithUuidOrDefault = function() {
+ return /** @type {string} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the below_element_with_uuid field.
+ * @param {string} value The value.
+ */
+sketchology.proto.AddAction.prototype.setBelowElementWithUuid = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the below_element_with_uuid field has a value.
+ */
+sketchology.proto.AddAction.prototype.hasBelowElementWithUuid = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the below_element_with_uuid field.
+ */
+sketchology.proto.AddAction.prototype.belowElementWithUuidCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the below_element_with_uuid field.
+ */
+sketchology.proto.AddAction.prototype.clearBelowElementWithUuid = function() {
+ this.clear$Field(2);
+};
+
+
+
+/**
+ * Message RemoveAction.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.RemoveAction = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.RemoveAction, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.RemoveAction.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.RemoveAction} The cloned message.
+ * @override
+ */
+sketchology.proto.RemoveAction.prototype.clone;
+
+
+/**
+ * Gets the value of the uuid field at the index given.
+ * @param {number} index The index to lookup.
+ * @return {?string} The value.
+ */
+sketchology.proto.RemoveAction.prototype.getUuid = function(index) {
+ return /** @type {?string} */ (this.get$Value(1, index));
+};
+
+
+/**
+ * Gets the value of the uuid field at the index given or the default value if not set.
+ * @param {number} index The index to lookup.
+ * @return {string} The value.
+ */
+sketchology.proto.RemoveAction.prototype.getUuidOrDefault = function(index) {
+ return /** @type {string} */ (this.get$ValueOrDefault(1, index));
+};
+
+
+/**
+ * Adds a value to the uuid field.
+ * @param {string} value The value to add.
+ */
+sketchology.proto.RemoveAction.prototype.addUuid = function(value) {
+ this.add$Value(1, value);
+};
+
+
+/**
+ * Returns the array of values in the uuid field.
+ * @return {!Array<string>} The values in the field.
+ */
+sketchology.proto.RemoveAction.prototype.uuidArray = function() {
+ return /** @type {!Array<string>} */ (this.array$Values(1));
+};
+
+
+/**
+ * @return {boolean} Whether the uuid field has a value.
+ */
+sketchology.proto.RemoveAction.prototype.hasUuid = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the uuid field.
+ */
+sketchology.proto.RemoveAction.prototype.uuidCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the uuid field.
+ */
+sketchology.proto.RemoveAction.prototype.clearUuid = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the was_below_uuid field at the index given.
+ * @param {number} index The index to lookup.
+ * @return {?string} The value.
+ */
+sketchology.proto.RemoveAction.prototype.getWasBelowUuid = function(index) {
+ return /** @type {?string} */ (this.get$Value(2, index));
+};
+
+
+/**
+ * Gets the value of the was_below_uuid field at the index given or the default value if not set.
+ * @param {number} index The index to lookup.
+ * @return {string} The value.
+ */
+sketchology.proto.RemoveAction.prototype.getWasBelowUuidOrDefault = function(index) {
+ return /** @type {string} */ (this.get$ValueOrDefault(2, index));
+};
+
+
+/**
+ * Adds a value to the was_below_uuid field.
+ * @param {string} value The value to add.
+ */
+sketchology.proto.RemoveAction.prototype.addWasBelowUuid = function(value) {
+ this.add$Value(2, value);
+};
+
+
+/**
+ * Returns the array of values in the was_below_uuid field.
+ * @return {!Array<string>} The values in the field.
+ */
+sketchology.proto.RemoveAction.prototype.wasBelowUuidArray = function() {
+ return /** @type {!Array<string>} */ (this.array$Values(2));
+};
+
+
+/**
+ * @return {boolean} Whether the was_below_uuid field has a value.
+ */
+sketchology.proto.RemoveAction.prototype.hasWasBelowUuid = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the was_below_uuid field.
+ */
+sketchology.proto.RemoveAction.prototype.wasBelowUuidCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the was_below_uuid field.
+ */
+sketchology.proto.RemoveAction.prototype.clearWasBelowUuid = function() {
+ this.clear$Field(2);
+};
+
+
+
+/**
+ * Message ClearAction.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.ClearAction = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.ClearAction, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.ClearAction.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.ClearAction} The cloned message.
+ * @override
+ */
+sketchology.proto.ClearAction.prototype.clone;
+
+
+/**
+ * Gets the value of the uuid field at the index given.
+ * @param {number} index The index to lookup.
+ * @return {?string} The value.
+ */
+sketchology.proto.ClearAction.prototype.getUuid = function(index) {
+ return /** @type {?string} */ (this.get$Value(1, index));
+};
+
+
+/**
+ * Gets the value of the uuid field at the index given or the default value if not set.
+ * @param {number} index The index to lookup.
+ * @return {string} The value.
+ */
+sketchology.proto.ClearAction.prototype.getUuidOrDefault = function(index) {
+ return /** @type {string} */ (this.get$ValueOrDefault(1, index));
+};
+
+
+/**
+ * Adds a value to the uuid field.
+ * @param {string} value The value to add.
+ */
+sketchology.proto.ClearAction.prototype.addUuid = function(value) {
+ this.add$Value(1, value);
+};
+
+
+/**
+ * Returns the array of values in the uuid field.
+ * @return {!Array<string>} The values in the field.
+ */
+sketchology.proto.ClearAction.prototype.uuidArray = function() {
+ return /** @type {!Array<string>} */ (this.array$Values(1));
+};
+
+
+/**
+ * @return {boolean} Whether the uuid field has a value.
+ */
+sketchology.proto.ClearAction.prototype.hasUuid = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the uuid field.
+ */
+sketchology.proto.ClearAction.prototype.uuidCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the uuid field.
+ */
+sketchology.proto.ClearAction.prototype.clearUuid = function() {
+ this.clear$Field(1);
+};
+
+
+
+/**
+ * Message ReplaceAction.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.ReplaceAction = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.ReplaceAction, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.ReplaceAction.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.ReplaceAction} The cloned message.
+ * @override
+ */
+sketchology.proto.ReplaceAction.prototype.clone;
+
+
+/**
+ * Gets the value of the uuid_add field at the index given.
+ * @param {number} index The index to lookup.
+ * @return {?string} The value.
+ */
+sketchology.proto.ReplaceAction.prototype.getUuidAdd = function(index) {
+ return /** @type {?string} */ (this.get$Value(1, index));
+};
+
+
+/**
+ * Gets the value of the uuid_add field at the index given or the default value if not set.
+ * @param {number} index The index to lookup.
+ * @return {string} The value.
+ */
+sketchology.proto.ReplaceAction.prototype.getUuidAddOrDefault = function(index) {
+ return /** @type {string} */ (this.get$ValueOrDefault(1, index));
+};
+
+
+/**
+ * Adds a value to the uuid_add field.
+ * @param {string} value The value to add.
+ */
+sketchology.proto.ReplaceAction.prototype.addUuidAdd = function(value) {
+ this.add$Value(1, value);
+};
+
+
+/**
+ * Returns the array of values in the uuid_add field.
+ * @return {!Array<string>} The values in the field.
+ */
+sketchology.proto.ReplaceAction.prototype.uuidAddArray = function() {
+ return /** @type {!Array<string>} */ (this.array$Values(1));
+};
+
+
+/**
+ * @return {boolean} Whether the uuid_add field has a value.
+ */
+sketchology.proto.ReplaceAction.prototype.hasUuidAdd = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the uuid_add field.
+ */
+sketchology.proto.ReplaceAction.prototype.uuidAddCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the uuid_add field.
+ */
+sketchology.proto.ReplaceAction.prototype.clearUuidAdd = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the below_element_with_uuid field.
+ * @return {?string} The value.
+ */
+sketchology.proto.ReplaceAction.prototype.getBelowElementWithUuid = function() {
+ return /** @type {?string} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the below_element_with_uuid field or the default value if not set.
+ * @return {string} The value.
+ */
+sketchology.proto.ReplaceAction.prototype.getBelowElementWithUuidOrDefault = function() {
+ return /** @type {string} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the below_element_with_uuid field.
+ * @param {string} value The value.
+ */
+sketchology.proto.ReplaceAction.prototype.setBelowElementWithUuid = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the below_element_with_uuid field has a value.
+ */
+sketchology.proto.ReplaceAction.prototype.hasBelowElementWithUuid = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the below_element_with_uuid field.
+ */
+sketchology.proto.ReplaceAction.prototype.belowElementWithUuidCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the below_element_with_uuid field.
+ */
+sketchology.proto.ReplaceAction.prototype.clearBelowElementWithUuid = function() {
+ this.clear$Field(2);
+};
+
+
+/**
+ * Gets the value of the uuid_remove field at the index given.
+ * @param {number} index The index to lookup.
+ * @return {?string} The value.
+ */
+sketchology.proto.ReplaceAction.prototype.getUuidRemove = function(index) {
+ return /** @type {?string} */ (this.get$Value(3, index));
+};
+
+
+/**
+ * Gets the value of the uuid_remove field at the index given or the default value if not set.
+ * @param {number} index The index to lookup.
+ * @return {string} The value.
+ */
+sketchology.proto.ReplaceAction.prototype.getUuidRemoveOrDefault = function(index) {
+ return /** @type {string} */ (this.get$ValueOrDefault(3, index));
+};
+
+
+/**
+ * Adds a value to the uuid_remove field.
+ * @param {string} value The value to add.
+ */
+sketchology.proto.ReplaceAction.prototype.addUuidRemove = function(value) {
+ this.add$Value(3, value);
+};
+
+
+/**
+ * Returns the array of values in the uuid_remove field.
+ * @return {!Array<string>} The values in the field.
+ */
+sketchology.proto.ReplaceAction.prototype.uuidRemoveArray = function() {
+ return /** @type {!Array<string>} */ (this.array$Values(3));
+};
+
+
+/**
+ * @return {boolean} Whether the uuid_remove field has a value.
+ */
+sketchology.proto.ReplaceAction.prototype.hasUuidRemove = function() {
+ return this.has$Value(3);
+};
+
+
+/**
+ * @return {number} The number of values in the uuid_remove field.
+ */
+sketchology.proto.ReplaceAction.prototype.uuidRemoveCount = function() {
+ return this.count$Values(3);
+};
+
+
+/**
+ * Clears the values in the uuid_remove field.
+ */
+sketchology.proto.ReplaceAction.prototype.clearUuidRemove = function() {
+ this.clear$Field(3);
+};
+
+
+/**
+ * Gets the value of the was_below_uuid field at the index given.
+ * @param {number} index The index to lookup.
+ * @return {?string} The value.
+ */
+sketchology.proto.ReplaceAction.prototype.getWasBelowUuid = function(index) {
+ return /** @type {?string} */ (this.get$Value(4, index));
+};
+
+
+/**
+ * Gets the value of the was_below_uuid field at the index given or the default value if not set.
+ * @param {number} index The index to lookup.
+ * @return {string} The value.
+ */
+sketchology.proto.ReplaceAction.prototype.getWasBelowUuidOrDefault = function(index) {
+ return /** @type {string} */ (this.get$ValueOrDefault(4, index));
+};
+
+
+/**
+ * Adds a value to the was_below_uuid field.
+ * @param {string} value The value to add.
+ */
+sketchology.proto.ReplaceAction.prototype.addWasBelowUuid = function(value) {
+ this.add$Value(4, value);
+};
+
+
+/**
+ * Returns the array of values in the was_below_uuid field.
+ * @return {!Array<string>} The values in the field.
+ */
+sketchology.proto.ReplaceAction.prototype.wasBelowUuidArray = function() {
+ return /** @type {!Array<string>} */ (this.array$Values(4));
+};
+
+
+/**
+ * @return {boolean} Whether the was_below_uuid field has a value.
+ */
+sketchology.proto.ReplaceAction.prototype.hasWasBelowUuid = function() {
+ return this.has$Value(4);
+};
+
+
+/**
+ * @return {number} The number of values in the was_below_uuid field.
+ */
+sketchology.proto.ReplaceAction.prototype.wasBelowUuidCount = function() {
+ return this.count$Values(4);
+};
+
+
+/**
+ * Clears the values in the was_below_uuid field.
+ */
+sketchology.proto.ReplaceAction.prototype.clearWasBelowUuid = function() {
+ this.clear$Field(4);
+};
+
+
+
+/**
+ * Message SetTransformAction.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.SetTransformAction = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.SetTransformAction, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.SetTransformAction.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.SetTransformAction} The cloned message.
+ * @override
+ */
+sketchology.proto.SetTransformAction.prototype.clone;
+
+
+/**
+ * Gets the value of the uuid field at the index given.
+ * @param {number} index The index to lookup.
+ * @return {?string} The value.
+ */
+sketchology.proto.SetTransformAction.prototype.getUuid = function(index) {
+ return /** @type {?string} */ (this.get$Value(1, index));
+};
+
+
+/**
+ * Gets the value of the uuid field at the index given or the default value if not set.
+ * @param {number} index The index to lookup.
+ * @return {string} The value.
+ */
+sketchology.proto.SetTransformAction.prototype.getUuidOrDefault = function(index) {
+ return /** @type {string} */ (this.get$ValueOrDefault(1, index));
+};
+
+
+/**
+ * Adds a value to the uuid field.
+ * @param {string} value The value to add.
+ */
+sketchology.proto.SetTransformAction.prototype.addUuid = function(value) {
+ this.add$Value(1, value);
+};
+
+
+/**
+ * Returns the array of values in the uuid field.
+ * @return {!Array<string>} The values in the field.
+ */
+sketchology.proto.SetTransformAction.prototype.uuidArray = function() {
+ return /** @type {!Array<string>} */ (this.array$Values(1));
+};
+
+
+/**
+ * @return {boolean} Whether the uuid field has a value.
+ */
+sketchology.proto.SetTransformAction.prototype.hasUuid = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the uuid field.
+ */
+sketchology.proto.SetTransformAction.prototype.uuidCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the uuid field.
+ */
+sketchology.proto.SetTransformAction.prototype.clearUuid = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the from_transform field at the index given.
+ * @param {number} index The index to lookup.
+ * @return {?sketchology.proto.AffineTransform} The value.
+ */
+sketchology.proto.SetTransformAction.prototype.getFromTransform = function(index) {
+ return /** @type {?sketchology.proto.AffineTransform} */ (this.get$Value(2, index));
+};
+
+
+/**
+ * Gets the value of the from_transform field at the index given or the default value if not set.
+ * @param {number} index The index to lookup.
+ * @return {!sketchology.proto.AffineTransform} The value.
+ */
+sketchology.proto.SetTransformAction.prototype.getFromTransformOrDefault = function(index) {
+ return /** @type {!sketchology.proto.AffineTransform} */ (this.get$ValueOrDefault(2, index));
+};
+
+
+/**
+ * Adds a value to the from_transform field.
+ * @param {!sketchology.proto.AffineTransform} value The value to add.
+ */
+sketchology.proto.SetTransformAction.prototype.addFromTransform = function(value) {
+ this.add$Value(2, value);
+};
+
+
+/**
+ * Returns the array of values in the from_transform field.
+ * @return {!Array<!sketchology.proto.AffineTransform>} The values in the field.
+ */
+sketchology.proto.SetTransformAction.prototype.fromTransformArray = function() {
+ return /** @type {!Array<!sketchology.proto.AffineTransform>} */ (this.array$Values(2));
+};
+
+
+/**
+ * @return {boolean} Whether the from_transform field has a value.
+ */
+sketchology.proto.SetTransformAction.prototype.hasFromTransform = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the from_transform field.
+ */
+sketchology.proto.SetTransformAction.prototype.fromTransformCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the from_transform field.
+ */
+sketchology.proto.SetTransformAction.prototype.clearFromTransform = function() {
+ this.clear$Field(2);
+};
+
+
+/**
+ * Gets the value of the to_transform field at the index given.
+ * @param {number} index The index to lookup.
+ * @return {?sketchology.proto.AffineTransform} The value.
+ */
+sketchology.proto.SetTransformAction.prototype.getToTransform = function(index) {
+ return /** @type {?sketchology.proto.AffineTransform} */ (this.get$Value(3, index));
+};
+
+
+/**
+ * Gets the value of the to_transform field at the index given or the default value if not set.
+ * @param {number} index The index to lookup.
+ * @return {!sketchology.proto.AffineTransform} The value.
+ */
+sketchology.proto.SetTransformAction.prototype.getToTransformOrDefault = function(index) {
+ return /** @type {!sketchology.proto.AffineTransform} */ (this.get$ValueOrDefault(3, index));
+};
+
+
+/**
+ * Adds a value to the to_transform field.
+ * @param {!sketchology.proto.AffineTransform} value The value to add.
+ */
+sketchology.proto.SetTransformAction.prototype.addToTransform = function(value) {
+ this.add$Value(3, value);
+};
+
+
+/**
+ * Returns the array of values in the to_transform field.
+ * @return {!Array<!sketchology.proto.AffineTransform>} The values in the field.
+ */
+sketchology.proto.SetTransformAction.prototype.toTransformArray = function() {
+ return /** @type {!Array<!sketchology.proto.AffineTransform>} */ (this.array$Values(3));
+};
+
+
+/**
+ * @return {boolean} Whether the to_transform field has a value.
+ */
+sketchology.proto.SetTransformAction.prototype.hasToTransform = function() {
+ return this.has$Value(3);
+};
+
+
+/**
+ * @return {number} The number of values in the to_transform field.
+ */
+sketchology.proto.SetTransformAction.prototype.toTransformCount = function() {
+ return this.count$Values(3);
+};
+
+
+/**
+ * Clears the values in the to_transform field.
+ */
+sketchology.proto.SetTransformAction.prototype.clearToTransform = function() {
+ this.clear$Field(3);
+};
+
+
+
+/**
+ * Message SetPageBoundsAction.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.SetPageBoundsAction = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.SetPageBoundsAction, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.SetPageBoundsAction.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.SetPageBoundsAction} The cloned message.
+ * @override
+ */
+sketchology.proto.SetPageBoundsAction.prototype.clone;
+
+
+/**
+ * Gets the value of the old_bounds field.
+ * @return {?sketchology.proto.Rect} The value.
+ */
+sketchology.proto.SetPageBoundsAction.prototype.getOldBounds = function() {
+ return /** @type {?sketchology.proto.Rect} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the old_bounds field or the default value if not set.
+ * @return {!sketchology.proto.Rect} The value.
+ */
+sketchology.proto.SetPageBoundsAction.prototype.getOldBoundsOrDefault = function() {
+ return /** @type {!sketchology.proto.Rect} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the old_bounds field.
+ * @param {!sketchology.proto.Rect} value The value.
+ */
+sketchology.proto.SetPageBoundsAction.prototype.setOldBounds = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the old_bounds field has a value.
+ */
+sketchology.proto.SetPageBoundsAction.prototype.hasOldBounds = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the old_bounds field.
+ */
+sketchology.proto.SetPageBoundsAction.prototype.oldBoundsCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the old_bounds field.
+ */
+sketchology.proto.SetPageBoundsAction.prototype.clearOldBounds = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the new_bounds field.
+ * @return {?sketchology.proto.Rect} The value.
+ */
+sketchology.proto.SetPageBoundsAction.prototype.getNewBounds = function() {
+ return /** @type {?sketchology.proto.Rect} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the new_bounds field or the default value if not set.
+ * @return {!sketchology.proto.Rect} The value.
+ */
+sketchology.proto.SetPageBoundsAction.prototype.getNewBoundsOrDefault = function() {
+ return /** @type {!sketchology.proto.Rect} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the new_bounds field.
+ * @param {!sketchology.proto.Rect} value The value.
+ */
+sketchology.proto.SetPageBoundsAction.prototype.setNewBounds = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the new_bounds field has a value.
+ */
+sketchology.proto.SetPageBoundsAction.prototype.hasNewBounds = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the new_bounds field.
+ */
+sketchology.proto.SetPageBoundsAction.prototype.newBoundsCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the new_bounds field.
+ */
+sketchology.proto.SetPageBoundsAction.prototype.clearNewBounds = function() {
+ this.clear$Field(2);
+};
+
+
+
+/**
+ * Message StorageAction.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.StorageAction = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.StorageAction, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.StorageAction.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.StorageAction} The cloned message.
+ * @override
+ */
+sketchology.proto.StorageAction.prototype.clone;
+
+
+/**
+ * Gets the value of the add_action field.
+ * @return {?sketchology.proto.AddAction} The value.
+ */
+sketchology.proto.StorageAction.prototype.getAddAction = function() {
+ return /** @type {?sketchology.proto.AddAction} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the add_action field or the default value if not set.
+ * @return {!sketchology.proto.AddAction} The value.
+ */
+sketchology.proto.StorageAction.prototype.getAddActionOrDefault = function() {
+ return /** @type {!sketchology.proto.AddAction} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the add_action field.
+ * @param {!sketchology.proto.AddAction} value The value.
+ */
+sketchology.proto.StorageAction.prototype.setAddAction = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the add_action field has a value.
+ */
+sketchology.proto.StorageAction.prototype.hasAddAction = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the add_action field.
+ */
+sketchology.proto.StorageAction.prototype.addActionCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the add_action field.
+ */
+sketchology.proto.StorageAction.prototype.clearAddAction = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the remove_action field.
+ * @return {?sketchology.proto.RemoveAction} The value.
+ */
+sketchology.proto.StorageAction.prototype.getRemoveAction = function() {
+ return /** @type {?sketchology.proto.RemoveAction} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the remove_action field or the default value if not set.
+ * @return {!sketchology.proto.RemoveAction} The value.
+ */
+sketchology.proto.StorageAction.prototype.getRemoveActionOrDefault = function() {
+ return /** @type {!sketchology.proto.RemoveAction} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the remove_action field.
+ * @param {!sketchology.proto.RemoveAction} value The value.
+ */
+sketchology.proto.StorageAction.prototype.setRemoveAction = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the remove_action field has a value.
+ */
+sketchology.proto.StorageAction.prototype.hasRemoveAction = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the remove_action field.
+ */
+sketchology.proto.StorageAction.prototype.removeActionCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the remove_action field.
+ */
+sketchology.proto.StorageAction.prototype.clearRemoveAction = function() {
+ this.clear$Field(2);
+};
+
+
+/**
+ * Gets the value of the clear_action field.
+ * @return {?sketchology.proto.ClearAction} The value.
+ */
+sketchology.proto.StorageAction.prototype.getClearAction = function() {
+ return /** @type {?sketchology.proto.ClearAction} */ (this.get$Value(3));
+};
+
+
+/**
+ * Gets the value of the clear_action field or the default value if not set.
+ * @return {!sketchology.proto.ClearAction} The value.
+ */
+sketchology.proto.StorageAction.prototype.getClearActionOrDefault = function() {
+ return /** @type {!sketchology.proto.ClearAction} */ (this.get$ValueOrDefault(3));
+};
+
+
+/**
+ * Sets the value of the clear_action field.
+ * @param {!sketchology.proto.ClearAction} value The value.
+ */
+sketchology.proto.StorageAction.prototype.setClearAction = function(value) {
+ this.set$Value(3, value);
+};
+
+
+/**
+ * @return {boolean} Whether the clear_action field has a value.
+ */
+sketchology.proto.StorageAction.prototype.hasClearAction = function() {
+ return this.has$Value(3);
+};
+
+
+/**
+ * @return {number} The number of values in the clear_action field.
+ */
+sketchology.proto.StorageAction.prototype.clearActionCount = function() {
+ return this.count$Values(3);
+};
+
+
+/**
+ * Clears the values in the clear_action field.
+ */
+sketchology.proto.StorageAction.prototype.clearClearAction = function() {
+ this.clear$Field(3);
+};
+
+
+/**
+ * Gets the value of the replace_action field.
+ * @return {?sketchology.proto.ReplaceAction} The value.
+ */
+sketchology.proto.StorageAction.prototype.getReplaceAction = function() {
+ return /** @type {?sketchology.proto.ReplaceAction} */ (this.get$Value(4));
+};
+
+
+/**
+ * Gets the value of the replace_action field or the default value if not set.
+ * @return {!sketchology.proto.ReplaceAction} The value.
+ */
+sketchology.proto.StorageAction.prototype.getReplaceActionOrDefault = function() {
+ return /** @type {!sketchology.proto.ReplaceAction} */ (this.get$ValueOrDefault(4));
+};
+
+
+/**
+ * Sets the value of the replace_action field.
+ * @param {!sketchology.proto.ReplaceAction} value The value.
+ */
+sketchology.proto.StorageAction.prototype.setReplaceAction = function(value) {
+ this.set$Value(4, value);
+};
+
+
+/**
+ * @return {boolean} Whether the replace_action field has a value.
+ */
+sketchology.proto.StorageAction.prototype.hasReplaceAction = function() {
+ return this.has$Value(4);
+};
+
+
+/**
+ * @return {number} The number of values in the replace_action field.
+ */
+sketchology.proto.StorageAction.prototype.replaceActionCount = function() {
+ return this.count$Values(4);
+};
+
+
+/**
+ * Clears the values in the replace_action field.
+ */
+sketchology.proto.StorageAction.prototype.clearReplaceAction = function() {
+ this.clear$Field(4);
+};
+
+
+/**
+ * Gets the value of the set_transform_action field.
+ * @return {?sketchology.proto.SetTransformAction} The value.
+ */
+sketchology.proto.StorageAction.prototype.getSetTransformAction = function() {
+ return /** @type {?sketchology.proto.SetTransformAction} */ (this.get$Value(5));
+};
+
+
+/**
+ * Gets the value of the set_transform_action field or the default value if not set.
+ * @return {!sketchology.proto.SetTransformAction} The value.
+ */
+sketchology.proto.StorageAction.prototype.getSetTransformActionOrDefault = function() {
+ return /** @type {!sketchology.proto.SetTransformAction} */ (this.get$ValueOrDefault(5));
+};
+
+
+/**
+ * Sets the value of the set_transform_action field.
+ * @param {!sketchology.proto.SetTransformAction} value The value.
+ */
+sketchology.proto.StorageAction.prototype.setSetTransformAction = function(value) {
+ this.set$Value(5, value);
+};
+
+
+/**
+ * @return {boolean} Whether the set_transform_action field has a value.
+ */
+sketchology.proto.StorageAction.prototype.hasSetTransformAction = function() {
+ return this.has$Value(5);
+};
+
+
+/**
+ * @return {number} The number of values in the set_transform_action field.
+ */
+sketchology.proto.StorageAction.prototype.setTransformActionCount = function() {
+ return this.count$Values(5);
+};
+
+
+/**
+ * Clears the values in the set_transform_action field.
+ */
+sketchology.proto.StorageAction.prototype.clearSetTransformAction = function() {
+ this.clear$Field(5);
+};
+
+
+/**
+ * Gets the value of the set_page_bounds_action field.
+ * @return {?sketchology.proto.SetPageBoundsAction} The value.
+ */
+sketchology.proto.StorageAction.prototype.getSetPageBoundsAction = function() {
+ return /** @type {?sketchology.proto.SetPageBoundsAction} */ (this.get$Value(6));
+};
+
+
+/**
+ * Gets the value of the set_page_bounds_action field or the default value if not set.
+ * @return {!sketchology.proto.SetPageBoundsAction} The value.
+ */
+sketchology.proto.StorageAction.prototype.getSetPageBoundsActionOrDefault = function() {
+ return /** @type {!sketchology.proto.SetPageBoundsAction} */ (this.get$ValueOrDefault(6));
+};
+
+
+/**
+ * Sets the value of the set_page_bounds_action field.
+ * @param {!sketchology.proto.SetPageBoundsAction} value The value.
+ */
+sketchology.proto.StorageAction.prototype.setSetPageBoundsAction = function(value) {
+ this.set$Value(6, value);
+};
+
+
+/**
+ * @return {boolean} Whether the set_page_bounds_action field has a value.
+ */
+sketchology.proto.StorageAction.prototype.hasSetPageBoundsAction = function() {
+ return this.has$Value(6);
+};
+
+
+/**
+ * @return {number} The number of values in the set_page_bounds_action field.
+ */
+sketchology.proto.StorageAction.prototype.setPageBoundsActionCount = function() {
+ return this.count$Values(6);
+};
+
+
+/**
+ * Clears the values in the set_page_bounds_action field.
+ */
+sketchology.proto.StorageAction.prototype.clearSetPageBoundsAction = function() {
+ this.clear$Field(6);
+};
+
+
+
+/**
+ * Message Snapshot.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.Snapshot = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.Snapshot, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.Snapshot.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.Snapshot} The cloned message.
+ * @override
+ */
+sketchology.proto.Snapshot.prototype.clone;
+
+
+/**
+ * Gets the value of the page_properties field.
+ * @return {?sketchology.proto.PageProperties} The value.
+ */
+sketchology.proto.Snapshot.prototype.getPageProperties = function() {
+ return /** @type {?sketchology.proto.PageProperties} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the page_properties field or the default value if not set.
+ * @return {!sketchology.proto.PageProperties} The value.
+ */
+sketchology.proto.Snapshot.prototype.getPagePropertiesOrDefault = function() {
+ return /** @type {!sketchology.proto.PageProperties} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the page_properties field.
+ * @param {!sketchology.proto.PageProperties} value The value.
+ */
+sketchology.proto.Snapshot.prototype.setPageProperties = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the page_properties field has a value.
+ */
+sketchology.proto.Snapshot.prototype.hasPageProperties = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the page_properties field.
+ */
+sketchology.proto.Snapshot.prototype.pagePropertiesCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the page_properties field.
+ */
+sketchology.proto.Snapshot.prototype.clearPageProperties = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the element field at the index given.
+ * @param {number} index The index to lookup.
+ * @return {?sketchology.proto.ElementBundle} The value.
+ */
+sketchology.proto.Snapshot.prototype.getElement = function(index) {
+ return /** @type {?sketchology.proto.ElementBundle} */ (this.get$Value(2, index));
+};
+
+
+/**
+ * Gets the value of the element field at the index given or the default value if not set.
+ * @param {number} index The index to lookup.
+ * @return {!sketchology.proto.ElementBundle} The value.
+ */
+sketchology.proto.Snapshot.prototype.getElementOrDefault = function(index) {
+ return /** @type {!sketchology.proto.ElementBundle} */ (this.get$ValueOrDefault(2, index));
+};
+
+
+/**
+ * Adds a value to the element field.
+ * @param {!sketchology.proto.ElementBundle} value The value to add.
+ */
+sketchology.proto.Snapshot.prototype.addElement = function(value) {
+ this.add$Value(2, value);
+};
+
+
+/**
+ * Returns the array of values in the element field.
+ * @return {!Array<!sketchology.proto.ElementBundle>} The values in the field.
+ */
+sketchology.proto.Snapshot.prototype.elementArray = function() {
+ return /** @type {!Array<!sketchology.proto.ElementBundle>} */ (this.array$Values(2));
+};
+
+
+/**
+ * @return {boolean} Whether the element field has a value.
+ */
+sketchology.proto.Snapshot.prototype.hasElement = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the element field.
+ */
+sketchology.proto.Snapshot.prototype.elementCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the element field.
+ */
+sketchology.proto.Snapshot.prototype.clearElement = function() {
+ this.clear$Field(2);
+};
+
+
+/**
+ * Gets the value of the dead_element field at the index given.
+ * @param {number} index The index to lookup.
+ * @return {?sketchology.proto.ElementBundle} The value.
+ */
+sketchology.proto.Snapshot.prototype.getDeadElement = function(index) {
+ return /** @type {?sketchology.proto.ElementBundle} */ (this.get$Value(3, index));
+};
+
+
+/**
+ * Gets the value of the dead_element field at the index given or the default value if not set.
+ * @param {number} index The index to lookup.
+ * @return {!sketchology.proto.ElementBundle} The value.
+ */
+sketchology.proto.Snapshot.prototype.getDeadElementOrDefault = function(index) {
+ return /** @type {!sketchology.proto.ElementBundle} */ (this.get$ValueOrDefault(3, index));
+};
+
+
+/**
+ * Adds a value to the dead_element field.
+ * @param {!sketchology.proto.ElementBundle} value The value to add.
+ */
+sketchology.proto.Snapshot.prototype.addDeadElement = function(value) {
+ this.add$Value(3, value);
+};
+
+
+/**
+ * Returns the array of values in the dead_element field.
+ * @return {!Array<!sketchology.proto.ElementBundle>} The values in the field.
+ */
+sketchology.proto.Snapshot.prototype.deadElementArray = function() {
+ return /** @type {!Array<!sketchology.proto.ElementBundle>} */ (this.array$Values(3));
+};
+
+
+/**
+ * @return {boolean} Whether the dead_element field has a value.
+ */
+sketchology.proto.Snapshot.prototype.hasDeadElement = function() {
+ return this.has$Value(3);
+};
+
+
+/**
+ * @return {number} The number of values in the dead_element field.
+ */
+sketchology.proto.Snapshot.prototype.deadElementCount = function() {
+ return this.count$Values(3);
+};
+
+
+/**
+ * Clears the values in the dead_element field.
+ */
+sketchology.proto.Snapshot.prototype.clearDeadElement = function() {
+ this.clear$Field(3);
+};
+
+
+/**
+ * Gets the value of the undo_action field at the index given.
+ * @param {number} index The index to lookup.
+ * @return {?sketchology.proto.StorageAction} The value.
+ */
+sketchology.proto.Snapshot.prototype.getUndoAction = function(index) {
+ return /** @type {?sketchology.proto.StorageAction} */ (this.get$Value(4, index));
+};
+
+
+/**
+ * Gets the value of the undo_action field at the index given or the default value if not set.
+ * @param {number} index The index to lookup.
+ * @return {!sketchology.proto.StorageAction} The value.
+ */
+sketchology.proto.Snapshot.prototype.getUndoActionOrDefault = function(index) {
+ return /** @type {!sketchology.proto.StorageAction} */ (this.get$ValueOrDefault(4, index));
+};
+
+
+/**
+ * Adds a value to the undo_action field.
+ * @param {!sketchology.proto.StorageAction} value The value to add.
+ */
+sketchology.proto.Snapshot.prototype.addUndoAction = function(value) {
+ this.add$Value(4, value);
+};
+
+
+/**
+ * Returns the array of values in the undo_action field.
+ * @return {!Array<!sketchology.proto.StorageAction>} The values in the field.
+ */
+sketchology.proto.Snapshot.prototype.undoActionArray = function() {
+ return /** @type {!Array<!sketchology.proto.StorageAction>} */ (this.array$Values(4));
+};
+
+
+/**
+ * @return {boolean} Whether the undo_action field has a value.
+ */
+sketchology.proto.Snapshot.prototype.hasUndoAction = function() {
+ return this.has$Value(4);
+};
+
+
+/**
+ * @return {number} The number of values in the undo_action field.
+ */
+sketchology.proto.Snapshot.prototype.undoActionCount = function() {
+ return this.count$Values(4);
+};
+
+
+/**
+ * Clears the values in the undo_action field.
+ */
+sketchology.proto.Snapshot.prototype.clearUndoAction = function() {
+ this.clear$Field(4);
+};
+
+
+/**
+ * Gets the value of the redo_action field at the index given.
+ * @param {number} index The index to lookup.
+ * @return {?sketchology.proto.StorageAction} The value.
+ */
+sketchology.proto.Snapshot.prototype.getRedoAction = function(index) {
+ return /** @type {?sketchology.proto.StorageAction} */ (this.get$Value(5, index));
+};
+
+
+/**
+ * Gets the value of the redo_action field at the index given or the default value if not set.
+ * @param {number} index The index to lookup.
+ * @return {!sketchology.proto.StorageAction} The value.
+ */
+sketchology.proto.Snapshot.prototype.getRedoActionOrDefault = function(index) {
+ return /** @type {!sketchology.proto.StorageAction} */ (this.get$ValueOrDefault(5, index));
+};
+
+
+/**
+ * Adds a value to the redo_action field.
+ * @param {!sketchology.proto.StorageAction} value The value to add.
+ */
+sketchology.proto.Snapshot.prototype.addRedoAction = function(value) {
+ this.add$Value(5, value);
+};
+
+
+/**
+ * Returns the array of values in the redo_action field.
+ * @return {!Array<!sketchology.proto.StorageAction>} The values in the field.
+ */
+sketchology.proto.Snapshot.prototype.redoActionArray = function() {
+ return /** @type {!Array<!sketchology.proto.StorageAction>} */ (this.array$Values(5));
+};
+
+
+/**
+ * @return {boolean} Whether the redo_action field has a value.
+ */
+sketchology.proto.Snapshot.prototype.hasRedoAction = function() {
+ return this.has$Value(5);
+};
+
+
+/**
+ * @return {number} The number of values in the redo_action field.
+ */
+sketchology.proto.Snapshot.prototype.redoActionCount = function() {
+ return this.count$Values(5);
+};
+
+
+/**
+ * Clears the values in the redo_action field.
+ */
+sketchology.proto.Snapshot.prototype.clearRedoAction = function() {
+ this.clear$Field(5);
+};
+
+
+/**
+ * Gets the value of the element_state_index field at the index given.
+ * @param {number} index The index to lookup.
+ * @return {?sketchology.proto.ElementState} The value.
+ */
+sketchology.proto.Snapshot.prototype.getElementStateIndex = function(index) {
+ return /** @type {?sketchology.proto.ElementState} */ (this.get$Value(6, index));
+};
+
+
+/**
+ * Gets the value of the element_state_index field at the index given or the default value if not set.
+ * @param {number} index The index to lookup.
+ * @return {!sketchology.proto.ElementState} The value.
+ */
+sketchology.proto.Snapshot.prototype.getElementStateIndexOrDefault = function(index) {
+ return /** @type {!sketchology.proto.ElementState} */ (this.get$ValueOrDefault(6, index));
+};
+
+
+/**
+ * Adds a value to the element_state_index field.
+ * @param {!sketchology.proto.ElementState} value The value to add.
+ */
+sketchology.proto.Snapshot.prototype.addElementStateIndex = function(value) {
+ this.add$Value(6, value);
+};
+
+
+/**
+ * Returns the array of values in the element_state_index field.
+ * @return {!Array<!sketchology.proto.ElementState>} The values in the field.
+ */
+sketchology.proto.Snapshot.prototype.elementStateIndexArray = function() {
+ return /** @type {!Array<!sketchology.proto.ElementState>} */ (this.array$Values(6));
+};
+
+
+/**
+ * @return {boolean} Whether the element_state_index field has a value.
+ */
+sketchology.proto.Snapshot.prototype.hasElementStateIndex = function() {
+ return this.has$Value(6);
+};
+
+
+/**
+ * @return {number} The number of values in the element_state_index field.
+ */
+sketchology.proto.Snapshot.prototype.elementStateIndexCount = function() {
+ return this.count$Values(6);
+};
+
+
+/**
+ * Clears the values in the element_state_index field.
+ */
+sketchology.proto.Snapshot.prototype.clearElementStateIndex = function() {
+ this.clear$Field(6);
+};
+
+
+/**
+ * Gets the value of the fingerprint field.
+ * @return {?string} The value.
+ */
+sketchology.proto.Snapshot.prototype.getFingerprint = function() {
+ return /** @type {?string} */ (this.get$Value(7));
+};
+
+
+/**
+ * Gets the value of the fingerprint field or the default value if not set.
+ * @return {string} The value.
+ */
+sketchology.proto.Snapshot.prototype.getFingerprintOrDefault = function() {
+ return /** @type {string} */ (this.get$ValueOrDefault(7));
+};
+
+
+/**
+ * Sets the value of the fingerprint field.
+ * @param {string} value The value.
+ */
+sketchology.proto.Snapshot.prototype.setFingerprint = function(value) {
+ this.set$Value(7, value);
+};
+
+
+/**
+ * @return {boolean} Whether the fingerprint field has a value.
+ */
+sketchology.proto.Snapshot.prototype.hasFingerprint = function() {
+ return this.has$Value(7);
+};
+
+
+/**
+ * @return {number} The number of values in the fingerprint field.
+ */
+sketchology.proto.Snapshot.prototype.fingerprintCount = function() {
+ return this.count$Values(7);
+};
+
+
+/**
+ * Clears the values in the fingerprint field.
+ */
+sketchology.proto.Snapshot.prototype.clearFingerprint = function() {
+ this.clear$Field(7);
+};
+
+
+
+/**
+ * Message MutationPacket.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.MutationPacket = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.MutationPacket, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.MutationPacket.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.MutationPacket} The cloned message.
+ * @override
+ */
+sketchology.proto.MutationPacket.prototype.clone;
+
+
+/**
+ * Gets the value of the mutation field at the index given.
+ * @param {number} index The index to lookup.
+ * @return {?sketchology.proto.StorageAction} The value.
+ */
+sketchology.proto.MutationPacket.prototype.getMutation = function(index) {
+ return /** @type {?sketchology.proto.StorageAction} */ (this.get$Value(1, index));
+};
+
+
+/**
+ * Gets the value of the mutation field at the index given or the default value if not set.
+ * @param {number} index The index to lookup.
+ * @return {!sketchology.proto.StorageAction} The value.
+ */
+sketchology.proto.MutationPacket.prototype.getMutationOrDefault = function(index) {
+ return /** @type {!sketchology.proto.StorageAction} */ (this.get$ValueOrDefault(1, index));
+};
+
+
+/**
+ * Adds a value to the mutation field.
+ * @param {!sketchology.proto.StorageAction} value The value to add.
+ */
+sketchology.proto.MutationPacket.prototype.addMutation = function(value) {
+ this.add$Value(1, value);
+};
+
+
+/**
+ * Returns the array of values in the mutation field.
+ * @return {!Array<!sketchology.proto.StorageAction>} The values in the field.
+ */
+sketchology.proto.MutationPacket.prototype.mutationArray = function() {
+ return /** @type {!Array<!sketchology.proto.StorageAction>} */ (this.array$Values(1));
+};
+
+
+/**
+ * @return {boolean} Whether the mutation field has a value.
+ */
+sketchology.proto.MutationPacket.prototype.hasMutation = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the mutation field.
+ */
+sketchology.proto.MutationPacket.prototype.mutationCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the mutation field.
+ */
+sketchology.proto.MutationPacket.prototype.clearMutation = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the element field at the index given.
+ * @param {number} index The index to lookup.
+ * @return {?sketchology.proto.ElementBundle} The value.
+ */
+sketchology.proto.MutationPacket.prototype.getElement = function(index) {
+ return /** @type {?sketchology.proto.ElementBundle} */ (this.get$Value(2, index));
+};
+
+
+/**
+ * Gets the value of the element field at the index given or the default value if not set.
+ * @param {number} index The index to lookup.
+ * @return {!sketchology.proto.ElementBundle} The value.
+ */
+sketchology.proto.MutationPacket.prototype.getElementOrDefault = function(index) {
+ return /** @type {!sketchology.proto.ElementBundle} */ (this.get$ValueOrDefault(2, index));
+};
+
+
+/**
+ * Adds a value to the element field.
+ * @param {!sketchology.proto.ElementBundle} value The value to add.
+ */
+sketchology.proto.MutationPacket.prototype.addElement = function(value) {
+ this.add$Value(2, value);
+};
+
+
+/**
+ * Returns the array of values in the element field.
+ * @return {!Array<!sketchology.proto.ElementBundle>} The values in the field.
+ */
+sketchology.proto.MutationPacket.prototype.elementArray = function() {
+ return /** @type {!Array<!sketchology.proto.ElementBundle>} */ (this.array$Values(2));
+};
+
+
+/**
+ * @return {boolean} Whether the element field has a value.
+ */
+sketchology.proto.MutationPacket.prototype.hasElement = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the element field.
+ */
+sketchology.proto.MutationPacket.prototype.elementCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the element field.
+ */
+sketchology.proto.MutationPacket.prototype.clearElement = function() {
+ this.clear$Field(2);
+};
+
+
+/** @override */
+sketchology.proto.Color.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.Color.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'Color',
+ fullName: 'sketchology.proto.Color'
+ },
+ 1: {
+ name: 'argb',
+ fieldType: goog.proto2.Message.FieldType.UINT32,
+ type: Number
+ }
+ };
+ sketchology.proto.Color.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.Color, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.Color.getDescriptor =
+ sketchology.proto.Color.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.BackgroundColor.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.BackgroundColor.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'BackgroundColor',
+ fullName: 'sketchology.proto.BackgroundColor'
+ },
+ 1: {
+ name: 'rgba',
+ fieldType: goog.proto2.Message.FieldType.UINT32,
+ type: Number
+ }
+ };
+ sketchology.proto.BackgroundColor.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.BackgroundColor, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.BackgroundColor.getDescriptor =
+ sketchology.proto.BackgroundColor.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.PageProperties.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.PageProperties.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'PageProperties',
+ fullName: 'sketchology.proto.PageProperties'
+ },
+ 1: {
+ name: 'background_color',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.Color
+ },
+ 2: {
+ name: 'background_image',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.BackgroundImageInfo
+ },
+ 3: {
+ name: 'bounds',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.Rect
+ },
+ 4: {
+ name: 'border',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.Border
+ }
+ };
+ sketchology.proto.PageProperties.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.PageProperties, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.PageProperties.getDescriptor =
+ sketchology.proto.PageProperties.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.AddAction.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.AddAction.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'AddAction',
+ fullName: 'sketchology.proto.AddAction'
+ },
+ 1: {
+ name: 'uuid',
+ fieldType: goog.proto2.Message.FieldType.STRING,
+ type: String
+ },
+ 2: {
+ name: 'below_element_with_uuid',
+ fieldType: goog.proto2.Message.FieldType.STRING,
+ type: String
+ }
+ };
+ sketchology.proto.AddAction.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.AddAction, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.AddAction.getDescriptor =
+ sketchology.proto.AddAction.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.RemoveAction.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.RemoveAction.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'RemoveAction',
+ fullName: 'sketchology.proto.RemoveAction'
+ },
+ 1: {
+ name: 'uuid',
+ repeated: true,
+ fieldType: goog.proto2.Message.FieldType.STRING,
+ type: String
+ },
+ 2: {
+ name: 'was_below_uuid',
+ repeated: true,
+ fieldType: goog.proto2.Message.FieldType.STRING,
+ type: String
+ }
+ };
+ sketchology.proto.RemoveAction.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.RemoveAction, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.RemoveAction.getDescriptor =
+ sketchology.proto.RemoveAction.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.ClearAction.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.ClearAction.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'ClearAction',
+ fullName: 'sketchology.proto.ClearAction'
+ },
+ 1: {
+ name: 'uuid',
+ repeated: true,
+ fieldType: goog.proto2.Message.FieldType.STRING,
+ type: String
+ }
+ };
+ sketchology.proto.ClearAction.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.ClearAction, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.ClearAction.getDescriptor =
+ sketchology.proto.ClearAction.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.ReplaceAction.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.ReplaceAction.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'ReplaceAction',
+ fullName: 'sketchology.proto.ReplaceAction'
+ },
+ 1: {
+ name: 'uuid_add',
+ repeated: true,
+ fieldType: goog.proto2.Message.FieldType.STRING,
+ type: String
+ },
+ 2: {
+ name: 'below_element_with_uuid',
+ fieldType: goog.proto2.Message.FieldType.STRING,
+ type: String
+ },
+ 3: {
+ name: 'uuid_remove',
+ repeated: true,
+ fieldType: goog.proto2.Message.FieldType.STRING,
+ type: String
+ },
+ 4: {
+ name: 'was_below_uuid',
+ repeated: true,
+ fieldType: goog.proto2.Message.FieldType.STRING,
+ type: String
+ }
+ };
+ sketchology.proto.ReplaceAction.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.ReplaceAction, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.ReplaceAction.getDescriptor =
+ sketchology.proto.ReplaceAction.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.SetTransformAction.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.SetTransformAction.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'SetTransformAction',
+ fullName: 'sketchology.proto.SetTransformAction'
+ },
+ 1: {
+ name: 'uuid',
+ repeated: true,
+ fieldType: goog.proto2.Message.FieldType.STRING,
+ type: String
+ },
+ 2: {
+ name: 'from_transform',
+ repeated: true,
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.AffineTransform
+ },
+ 3: {
+ name: 'to_transform',
+ repeated: true,
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.AffineTransform
+ }
+ };
+ sketchology.proto.SetTransformAction.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.SetTransformAction, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.SetTransformAction.getDescriptor =
+ sketchology.proto.SetTransformAction.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.SetPageBoundsAction.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.SetPageBoundsAction.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'SetPageBoundsAction',
+ fullName: 'sketchology.proto.SetPageBoundsAction'
+ },
+ 1: {
+ name: 'old_bounds',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.Rect
+ },
+ 2: {
+ name: 'new_bounds',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.Rect
+ }
+ };
+ sketchology.proto.SetPageBoundsAction.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.SetPageBoundsAction, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.SetPageBoundsAction.getDescriptor =
+ sketchology.proto.SetPageBoundsAction.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.StorageAction.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.StorageAction.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'StorageAction',
+ fullName: 'sketchology.proto.StorageAction'
+ },
+ 1: {
+ name: 'add_action',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.AddAction
+ },
+ 2: {
+ name: 'remove_action',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.RemoveAction
+ },
+ 3: {
+ name: 'clear_action',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.ClearAction
+ },
+ 4: {
+ name: 'replace_action',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.ReplaceAction
+ },
+ 5: {
+ name: 'set_transform_action',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.SetTransformAction
+ },
+ 6: {
+ name: 'set_page_bounds_action',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.SetPageBoundsAction
+ }
+ };
+ sketchology.proto.StorageAction.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.StorageAction, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.StorageAction.getDescriptor =
+ sketchology.proto.StorageAction.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.Snapshot.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.Snapshot.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'Snapshot',
+ fullName: 'sketchology.proto.Snapshot'
+ },
+ 1: {
+ name: 'page_properties',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.PageProperties
+ },
+ 2: {
+ name: 'element',
+ repeated: true,
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.ElementBundle
+ },
+ 3: {
+ name: 'dead_element',
+ repeated: true,
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.ElementBundle
+ },
+ 4: {
+ name: 'undo_action',
+ repeated: true,
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.StorageAction
+ },
+ 5: {
+ name: 'redo_action',
+ repeated: true,
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.StorageAction
+ },
+ 6: {
+ name: 'element_state_index',
+ repeated: true,
+ fieldType: goog.proto2.Message.FieldType.ENUM,
+ defaultValue: sketchology.proto.ElementState.ALIVE,
+ type: sketchology.proto.ElementState
+ },
+ 7: {
+ name: 'fingerprint',
+ fieldType: goog.proto2.Message.FieldType.UINT64,
+ type: String
+ }
+ };
+ sketchology.proto.Snapshot.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.Snapshot, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.Snapshot.getDescriptor =
+ sketchology.proto.Snapshot.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.MutationPacket.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.MutationPacket.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'MutationPacket',
+ fullName: 'sketchology.proto.MutationPacket'
+ },
+ 1: {
+ name: 'mutation',
+ repeated: true,
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.StorageAction
+ },
+ 2: {
+ name: 'element',
+ repeated: true,
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.ElementBundle
+ }
+ };
+ sketchology.proto.MutationPacket.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.MutationPacket, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.MutationPacket.getDescriptor =
+ sketchology.proto.MutationPacket.prototype.getDescriptor;
diff --git a/chromium/third_party/ink/sketchology/proto/elements.pb.js b/chromium/third_party/ink/sketchology/proto/elements.pb.js
new file mode 100644
index 00000000000..4c0de6a6513
--- /dev/null
+++ b/chromium/third_party/ink/sketchology/proto/elements.pb.js
@@ -0,0 +1,3976 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+// Protocol Buffer 2 Copyright 2008 Google Inc.
+// All other code copyright its respective owners.
+
+/**
+ * @fileoverview Generated Protocol Buffer code for file
+ * third_party/sketchology/proto/elements.proto.
+ * Generated by //net/proto2/compiler/public:protocol_compiler.
+ * @suppress {messageConventions}
+ */
+
+goog.provide('sketchology.proto.CallbackFlags');
+goog.provide('sketchology.proto.SourceDetails');
+goog.provide('sketchology.proto.SourceDetails.Origin');
+goog.provide('sketchology.proto.BackgroundImageInfo');
+goog.provide('sketchology.proto.Border');
+goog.provide('sketchology.proto.LOD');
+goog.provide('sketchology.proto.Stroke');
+goog.provide('sketchology.proto.UncompressedStroke');
+goog.provide('sketchology.proto.AffineTransform');
+goog.provide('sketchology.proto.Element');
+goog.provide('sketchology.proto.ElementAttributes');
+goog.provide('sketchology.proto.UncompressedElement');
+goog.provide('sketchology.proto.ElementMutation');
+goog.provide('sketchology.proto.ElementIdList');
+goog.provide('sketchology.proto.Point');
+goog.provide('sketchology.proto.ElementBundle');
+goog.provide('sketchology.proto.Path');
+goog.provide('sketchology.proto.Path.SegmentType');
+goog.provide('sketchology.proto.Path.EndCapType');
+goog.provide('sketchology.proto.ShaderType');
+
+goog.require('goog.proto2.Message');
+goog.require('sketchology.proto.Rect');
+
+
+/**
+ * Enumeration ShaderType.
+ * @enum {number}
+ */
+sketchology.proto.ShaderType = {
+ NONE: 0,
+ VERTEX_COLORED: 1,
+ SOLID_COLORED: 2,
+ ERASE: 3,
+ VERTEX_TEXTURED: 4
+};
+
+
+
+/**
+ * Message CallbackFlags.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.CallbackFlags = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.CallbackFlags, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.CallbackFlags.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.CallbackFlags} The cloned message.
+ * @override
+ */
+sketchology.proto.CallbackFlags.prototype.clone;
+
+
+/**
+ * Gets the value of the mesh_data_ctm field.
+ * @return {?boolean} The value.
+ */
+sketchology.proto.CallbackFlags.prototype.getMeshDataCtm = function() {
+ return /** @type {?boolean} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the mesh_data_ctm field or the default value if not set.
+ * @return {boolean} The value.
+ */
+sketchology.proto.CallbackFlags.prototype.getMeshDataCtmOrDefault = function() {
+ return /** @type {boolean} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the mesh_data_ctm field.
+ * @param {boolean} value The value.
+ */
+sketchology.proto.CallbackFlags.prototype.setMeshDataCtm = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the mesh_data_ctm field has a value.
+ */
+sketchology.proto.CallbackFlags.prototype.hasMeshDataCtm = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the mesh_data_ctm field.
+ */
+sketchology.proto.CallbackFlags.prototype.meshDataCtmCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the mesh_data_ctm field.
+ */
+sketchology.proto.CallbackFlags.prototype.clearMeshDataCtm = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the uncompressed_outline field.
+ * @return {?boolean} The value.
+ */
+sketchology.proto.CallbackFlags.prototype.getUncompressedOutline = function() {
+ return /** @type {?boolean} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the uncompressed_outline field or the default value if not set.
+ * @return {boolean} The value.
+ */
+sketchology.proto.CallbackFlags.prototype.getUncompressedOutlineOrDefault = function() {
+ return /** @type {boolean} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the uncompressed_outline field.
+ * @param {boolean} value The value.
+ */
+sketchology.proto.CallbackFlags.prototype.setUncompressedOutline = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the uncompressed_outline field has a value.
+ */
+sketchology.proto.CallbackFlags.prototype.hasUncompressedOutline = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the uncompressed_outline field.
+ */
+sketchology.proto.CallbackFlags.prototype.uncompressedOutlineCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the uncompressed_outline field.
+ */
+sketchology.proto.CallbackFlags.prototype.clearUncompressedOutline = function() {
+ this.clear$Field(2);
+};
+
+
+/**
+ * Gets the value of the compressed_input_points field.
+ * @return {?boolean} The value.
+ */
+sketchology.proto.CallbackFlags.prototype.getCompressedInputPoints = function() {
+ return /** @type {?boolean} */ (this.get$Value(3));
+};
+
+
+/**
+ * Gets the value of the compressed_input_points field or the default value if not set.
+ * @return {boolean} The value.
+ */
+sketchology.proto.CallbackFlags.prototype.getCompressedInputPointsOrDefault = function() {
+ return /** @type {boolean} */ (this.get$ValueOrDefault(3));
+};
+
+
+/**
+ * Sets the value of the compressed_input_points field.
+ * @param {boolean} value The value.
+ */
+sketchology.proto.CallbackFlags.prototype.setCompressedInputPoints = function(value) {
+ this.set$Value(3, value);
+};
+
+
+/**
+ * @return {boolean} Whether the compressed_input_points field has a value.
+ */
+sketchology.proto.CallbackFlags.prototype.hasCompressedInputPoints = function() {
+ return this.has$Value(3);
+};
+
+
+/**
+ * @return {number} The number of values in the compressed_input_points field.
+ */
+sketchology.proto.CallbackFlags.prototype.compressedInputPointsCount = function() {
+ return this.count$Values(3);
+};
+
+
+/**
+ * Clears the values in the compressed_input_points field.
+ */
+sketchology.proto.CallbackFlags.prototype.clearCompressedInputPoints = function() {
+ this.clear$Field(3);
+};
+
+
+
+/**
+ * Message SourceDetails.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.SourceDetails = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.SourceDetails, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.SourceDetails.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.SourceDetails} The cloned message.
+ * @override
+ */
+sketchology.proto.SourceDetails.prototype.clone;
+
+
+/**
+ * Gets the value of the origin field.
+ * @return {?sketchology.proto.SourceDetails.Origin} The value.
+ */
+sketchology.proto.SourceDetails.prototype.getOrigin = function() {
+ return /** @type {?sketchology.proto.SourceDetails.Origin} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the origin field or the default value if not set.
+ * @return {!sketchology.proto.SourceDetails.Origin} The value.
+ */
+sketchology.proto.SourceDetails.prototype.getOriginOrDefault = function() {
+ return /** @type {!sketchology.proto.SourceDetails.Origin} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the origin field.
+ * @param {!sketchology.proto.SourceDetails.Origin} value The value.
+ */
+sketchology.proto.SourceDetails.prototype.setOrigin = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the origin field has a value.
+ */
+sketchology.proto.SourceDetails.prototype.hasOrigin = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the origin field.
+ */
+sketchology.proto.SourceDetails.prototype.originCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the origin field.
+ */
+sketchology.proto.SourceDetails.prototype.clearOrigin = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the host_source_details field.
+ * @return {?number} The value.
+ */
+sketchology.proto.SourceDetails.prototype.getHostSourceDetails = function() {
+ return /** @type {?number} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the host_source_details field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.SourceDetails.prototype.getHostSourceDetailsOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the host_source_details field.
+ * @param {number} value The value.
+ */
+sketchology.proto.SourceDetails.prototype.setHostSourceDetails = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the host_source_details field has a value.
+ */
+sketchology.proto.SourceDetails.prototype.hasHostSourceDetails = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the host_source_details field.
+ */
+sketchology.proto.SourceDetails.prototype.hostSourceDetailsCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the host_source_details field.
+ */
+sketchology.proto.SourceDetails.prototype.clearHostSourceDetails = function() {
+ this.clear$Field(2);
+};
+
+
+/**
+ * Enumeration Origin.
+ * @enum {number}
+ */
+sketchology.proto.SourceDetails.Origin = {
+ UNKNOWN: 0,
+ ENGINE: 1,
+ HOST: 2
+};
+
+
+
+/**
+ * Message BackgroundImageInfo.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.BackgroundImageInfo = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.BackgroundImageInfo, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.BackgroundImageInfo.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.BackgroundImageInfo} The cloned message.
+ * @override
+ */
+sketchology.proto.BackgroundImageInfo.prototype.clone;
+
+
+/**
+ * Gets the value of the uri field.
+ * @return {?string} The value.
+ */
+sketchology.proto.BackgroundImageInfo.prototype.getUri = function() {
+ return /** @type {?string} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the uri field or the default value if not set.
+ * @return {string} The value.
+ */
+sketchology.proto.BackgroundImageInfo.prototype.getUriOrDefault = function() {
+ return /** @type {string} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the uri field.
+ * @param {string} value The value.
+ */
+sketchology.proto.BackgroundImageInfo.prototype.setUri = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the uri field has a value.
+ */
+sketchology.proto.BackgroundImageInfo.prototype.hasUri = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the uri field.
+ */
+sketchology.proto.BackgroundImageInfo.prototype.uriCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the uri field.
+ */
+sketchology.proto.BackgroundImageInfo.prototype.clearUri = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the bounds field.
+ * @return {?sketchology.proto.Rect} The value.
+ */
+sketchology.proto.BackgroundImageInfo.prototype.getBounds = function() {
+ return /** @type {?sketchology.proto.Rect} */ (this.get$Value(3));
+};
+
+
+/**
+ * Gets the value of the bounds field or the default value if not set.
+ * @return {!sketchology.proto.Rect} The value.
+ */
+sketchology.proto.BackgroundImageInfo.prototype.getBoundsOrDefault = function() {
+ return /** @type {!sketchology.proto.Rect} */ (this.get$ValueOrDefault(3));
+};
+
+
+/**
+ * Sets the value of the bounds field.
+ * @param {!sketchology.proto.Rect} value The value.
+ */
+sketchology.proto.BackgroundImageInfo.prototype.setBounds = function(value) {
+ this.set$Value(3, value);
+};
+
+
+/**
+ * @return {boolean} Whether the bounds field has a value.
+ */
+sketchology.proto.BackgroundImageInfo.prototype.hasBounds = function() {
+ return this.has$Value(3);
+};
+
+
+/**
+ * @return {number} The number of values in the bounds field.
+ */
+sketchology.proto.BackgroundImageInfo.prototype.boundsCount = function() {
+ return this.count$Values(3);
+};
+
+
+/**
+ * Clears the values in the bounds field.
+ */
+sketchology.proto.BackgroundImageInfo.prototype.clearBounds = function() {
+ this.clear$Field(3);
+};
+
+
+
+/**
+ * Message Border.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.Border = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.Border, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.Border.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.Border} The cloned message.
+ * @override
+ */
+sketchology.proto.Border.prototype.clone;
+
+
+/**
+ * Gets the value of the uri field.
+ * @return {?string} The value.
+ */
+sketchology.proto.Border.prototype.getUri = function() {
+ return /** @type {?string} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the uri field or the default value if not set.
+ * @return {string} The value.
+ */
+sketchology.proto.Border.prototype.getUriOrDefault = function() {
+ return /** @type {string} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the uri field.
+ * @param {string} value The value.
+ */
+sketchology.proto.Border.prototype.setUri = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the uri field has a value.
+ */
+sketchology.proto.Border.prototype.hasUri = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the uri field.
+ */
+sketchology.proto.Border.prototype.uriCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the uri field.
+ */
+sketchology.proto.Border.prototype.clearUri = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the scale field.
+ * @return {?number} The value.
+ */
+sketchology.proto.Border.prototype.getScale = function() {
+ return /** @type {?number} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the scale field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.Border.prototype.getScaleOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the scale field.
+ * @param {number} value The value.
+ */
+sketchology.proto.Border.prototype.setScale = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the scale field has a value.
+ */
+sketchology.proto.Border.prototype.hasScale = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the scale field.
+ */
+sketchology.proto.Border.prototype.scaleCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the scale field.
+ */
+sketchology.proto.Border.prototype.clearScale = function() {
+ this.clear$Field(2);
+};
+
+
+
+/**
+ * Message LOD.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.LOD = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.LOD, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.LOD.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.LOD} The cloned message.
+ * @override
+ */
+sketchology.proto.LOD.prototype.clone;
+
+
+/**
+ * Gets the value of the max_coverage field.
+ * @return {?number} The value.
+ */
+sketchology.proto.LOD.prototype.getMaxCoverage = function() {
+ return /** @type {?number} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the max_coverage field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.LOD.prototype.getMaxCoverageOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the max_coverage field.
+ * @param {number} value The value.
+ */
+sketchology.proto.LOD.prototype.setMaxCoverage = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the max_coverage field has a value.
+ */
+sketchology.proto.LOD.prototype.hasMaxCoverage = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the max_coverage field.
+ */
+sketchology.proto.LOD.prototype.maxCoverageCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the max_coverage field.
+ */
+sketchology.proto.LOD.prototype.clearMaxCoverage = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the ctm_blob field.
+ * @return {?string} The value.
+ */
+sketchology.proto.LOD.prototype.getCtmBlob = function() {
+ return /** @type {?string} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the ctm_blob field or the default value if not set.
+ * @return {string} The value.
+ */
+sketchology.proto.LOD.prototype.getCtmBlobOrDefault = function() {
+ return /** @type {string} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the ctm_blob field.
+ * @param {string} value The value.
+ */
+sketchology.proto.LOD.prototype.setCtmBlob = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the ctm_blob field has a value.
+ */
+sketchology.proto.LOD.prototype.hasCtmBlob = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the ctm_blob field.
+ */
+sketchology.proto.LOD.prototype.ctmBlobCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the ctm_blob field.
+ */
+sketchology.proto.LOD.prototype.clearCtmBlob = function() {
+ this.clear$Field(2);
+};
+
+
+
+/**
+ * Message Stroke.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.Stroke = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.Stroke, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.Stroke.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.Stroke} The cloned message.
+ * @override
+ */
+sketchology.proto.Stroke.prototype.clone;
+
+
+/**
+ * Gets the value of the shader_type field.
+ * @return {?sketchology.proto.ShaderType} The value.
+ */
+sketchology.proto.Stroke.prototype.getShaderType = function() {
+ return /** @type {?sketchology.proto.ShaderType} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the shader_type field or the default value if not set.
+ * @return {!sketchology.proto.ShaderType} The value.
+ */
+sketchology.proto.Stroke.prototype.getShaderTypeOrDefault = function() {
+ return /** @type {!sketchology.proto.ShaderType} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the shader_type field.
+ * @param {!sketchology.proto.ShaderType} value The value.
+ */
+sketchology.proto.Stroke.prototype.setShaderType = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the shader_type field has a value.
+ */
+sketchology.proto.Stroke.prototype.hasShaderType = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the shader_type field.
+ */
+sketchology.proto.Stroke.prototype.shaderTypeCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the shader_type field.
+ */
+sketchology.proto.Stroke.prototype.clearShaderType = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the lod field at the index given.
+ * @param {number} index The index to lookup.
+ * @return {?sketchology.proto.LOD} The value.
+ */
+sketchology.proto.Stroke.prototype.getLod = function(index) {
+ return /** @type {?sketchology.proto.LOD} */ (this.get$Value(3, index));
+};
+
+
+/**
+ * Gets the value of the lod field at the index given or the default value if not set.
+ * @param {number} index The index to lookup.
+ * @return {!sketchology.proto.LOD} The value.
+ */
+sketchology.proto.Stroke.prototype.getLodOrDefault = function(index) {
+ return /** @type {!sketchology.proto.LOD} */ (this.get$ValueOrDefault(3, index));
+};
+
+
+/**
+ * Adds a value to the lod field.
+ * @param {!sketchology.proto.LOD} value The value to add.
+ */
+sketchology.proto.Stroke.prototype.addLod = function(value) {
+ this.add$Value(3, value);
+};
+
+
+/**
+ * Returns the array of values in the lod field.
+ * @return {!Array<!sketchology.proto.LOD>} The values in the field.
+ */
+sketchology.proto.Stroke.prototype.lodArray = function() {
+ return /** @type {!Array<!sketchology.proto.LOD>} */ (this.array$Values(3));
+};
+
+
+/**
+ * @return {boolean} Whether the lod field has a value.
+ */
+sketchology.proto.Stroke.prototype.hasLod = function() {
+ return this.has$Value(3);
+};
+
+
+/**
+ * @return {number} The number of values in the lod field.
+ */
+sketchology.proto.Stroke.prototype.lodCount = function() {
+ return this.count$Values(3);
+};
+
+
+/**
+ * Clears the values in the lod field.
+ */
+sketchology.proto.Stroke.prototype.clearLod = function() {
+ this.clear$Field(3);
+};
+
+
+/**
+ * Gets the value of the abgr field.
+ * @return {?number} The value.
+ */
+sketchology.proto.Stroke.prototype.getAbgr = function() {
+ return /** @type {?number} */ (this.get$Value(4));
+};
+
+
+/**
+ * Gets the value of the abgr field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.Stroke.prototype.getAbgrOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(4));
+};
+
+
+/**
+ * Sets the value of the abgr field.
+ * @param {number} value The value.
+ */
+sketchology.proto.Stroke.prototype.setAbgr = function(value) {
+ this.set$Value(4, value);
+};
+
+
+/**
+ * @return {boolean} Whether the abgr field has a value.
+ */
+sketchology.proto.Stroke.prototype.hasAbgr = function() {
+ return this.has$Value(4);
+};
+
+
+/**
+ * @return {number} The number of values in the abgr field.
+ */
+sketchology.proto.Stroke.prototype.abgrCount = function() {
+ return this.count$Values(4);
+};
+
+
+/**
+ * Clears the values in the abgr field.
+ */
+sketchology.proto.Stroke.prototype.clearAbgr = function() {
+ this.clear$Field(4);
+};
+
+
+/**
+ * Gets the value of the point_x field at the index given.
+ * @param {number} index The index to lookup.
+ * @return {?number} The value.
+ */
+sketchology.proto.Stroke.prototype.getPointX = function(index) {
+ return /** @type {?number} */ (this.get$Value(5, index));
+};
+
+
+/**
+ * Gets the value of the point_x field at the index given or the default value if not set.
+ * @param {number} index The index to lookup.
+ * @return {number} The value.
+ */
+sketchology.proto.Stroke.prototype.getPointXOrDefault = function(index) {
+ return /** @type {number} */ (this.get$ValueOrDefault(5, index));
+};
+
+
+/**
+ * Adds a value to the point_x field.
+ * @param {number} value The value to add.
+ */
+sketchology.proto.Stroke.prototype.addPointX = function(value) {
+ this.add$Value(5, value);
+};
+
+
+/**
+ * Returns the array of values in the point_x field.
+ * @return {!Array<number>} The values in the field.
+ */
+sketchology.proto.Stroke.prototype.pointXArray = function() {
+ return /** @type {!Array<number>} */ (this.array$Values(5));
+};
+
+
+/**
+ * @return {boolean} Whether the point_x field has a value.
+ */
+sketchology.proto.Stroke.prototype.hasPointX = function() {
+ return this.has$Value(5);
+};
+
+
+/**
+ * @return {number} The number of values in the point_x field.
+ */
+sketchology.proto.Stroke.prototype.pointXCount = function() {
+ return this.count$Values(5);
+};
+
+
+/**
+ * Clears the values in the point_x field.
+ */
+sketchology.proto.Stroke.prototype.clearPointX = function() {
+ this.clear$Field(5);
+};
+
+
+/**
+ * Gets the value of the point_y field at the index given.
+ * @param {number} index The index to lookup.
+ * @return {?number} The value.
+ */
+sketchology.proto.Stroke.prototype.getPointY = function(index) {
+ return /** @type {?number} */ (this.get$Value(6, index));
+};
+
+
+/**
+ * Gets the value of the point_y field at the index given or the default value if not set.
+ * @param {number} index The index to lookup.
+ * @return {number} The value.
+ */
+sketchology.proto.Stroke.prototype.getPointYOrDefault = function(index) {
+ return /** @type {number} */ (this.get$ValueOrDefault(6, index));
+};
+
+
+/**
+ * Adds a value to the point_y field.
+ * @param {number} value The value to add.
+ */
+sketchology.proto.Stroke.prototype.addPointY = function(value) {
+ this.add$Value(6, value);
+};
+
+
+/**
+ * Returns the array of values in the point_y field.
+ * @return {!Array<number>} The values in the field.
+ */
+sketchology.proto.Stroke.prototype.pointYArray = function() {
+ return /** @type {!Array<number>} */ (this.array$Values(6));
+};
+
+
+/**
+ * @return {boolean} Whether the point_y field has a value.
+ */
+sketchology.proto.Stroke.prototype.hasPointY = function() {
+ return this.has$Value(6);
+};
+
+
+/**
+ * @return {number} The number of values in the point_y field.
+ */
+sketchology.proto.Stroke.prototype.pointYCount = function() {
+ return this.count$Values(6);
+};
+
+
+/**
+ * Clears the values in the point_y field.
+ */
+sketchology.proto.Stroke.prototype.clearPointY = function() {
+ this.clear$Field(6);
+};
+
+
+/**
+ * Gets the value of the point_t_ms field at the index given.
+ * @param {number} index The index to lookup.
+ * @return {?number} The value.
+ */
+sketchology.proto.Stroke.prototype.getPointTMs = function(index) {
+ return /** @type {?number} */ (this.get$Value(7, index));
+};
+
+
+/**
+ * Gets the value of the point_t_ms field at the index given or the default value if not set.
+ * @param {number} index The index to lookup.
+ * @return {number} The value.
+ */
+sketchology.proto.Stroke.prototype.getPointTMsOrDefault = function(index) {
+ return /** @type {number} */ (this.get$ValueOrDefault(7, index));
+};
+
+
+/**
+ * Adds a value to the point_t_ms field.
+ * @param {number} value The value to add.
+ */
+sketchology.proto.Stroke.prototype.addPointTMs = function(value) {
+ this.add$Value(7, value);
+};
+
+
+/**
+ * Returns the array of values in the point_t_ms field.
+ * @return {!Array<number>} The values in the field.
+ */
+sketchology.proto.Stroke.prototype.pointTMsArray = function() {
+ return /** @type {!Array<number>} */ (this.array$Values(7));
+};
+
+
+/**
+ * @return {boolean} Whether the point_t_ms field has a value.
+ */
+sketchology.proto.Stroke.prototype.hasPointTMs = function() {
+ return this.has$Value(7);
+};
+
+
+/**
+ * @return {number} The number of values in the point_t_ms field.
+ */
+sketchology.proto.Stroke.prototype.pointTMsCount = function() {
+ return this.count$Values(7);
+};
+
+
+/**
+ * Clears the values in the point_t_ms field.
+ */
+sketchology.proto.Stroke.prototype.clearPointTMs = function() {
+ this.clear$Field(7);
+};
+
+
+/**
+ * Gets the value of the deprecated_transform field.
+ * @return {?sketchology.proto.AffineTransform} The value.
+ */
+sketchology.proto.Stroke.prototype.getDeprecatedTransform = function() {
+ return /** @type {?sketchology.proto.AffineTransform} */ (this.get$Value(8));
+};
+
+
+/**
+ * Gets the value of the deprecated_transform field or the default value if not set.
+ * @return {!sketchology.proto.AffineTransform} The value.
+ */
+sketchology.proto.Stroke.prototype.getDeprecatedTransformOrDefault = function() {
+ return /** @type {!sketchology.proto.AffineTransform} */ (this.get$ValueOrDefault(8));
+};
+
+
+/**
+ * Sets the value of the deprecated_transform field.
+ * @param {!sketchology.proto.AffineTransform} value The value.
+ */
+sketchology.proto.Stroke.prototype.setDeprecatedTransform = function(value) {
+ this.set$Value(8, value);
+};
+
+
+/**
+ * @return {boolean} Whether the deprecated_transform field has a value.
+ */
+sketchology.proto.Stroke.prototype.hasDeprecatedTransform = function() {
+ return this.has$Value(8);
+};
+
+
+/**
+ * @return {number} The number of values in the deprecated_transform field.
+ */
+sketchology.proto.Stroke.prototype.deprecatedTransformCount = function() {
+ return this.count$Values(8);
+};
+
+
+/**
+ * Clears the values in the deprecated_transform field.
+ */
+sketchology.proto.Stroke.prototype.clearDeprecatedTransform = function() {
+ this.clear$Field(8);
+};
+
+
+/**
+ * Gets the value of the start_time_ms field.
+ * @return {?string} The value.
+ */
+sketchology.proto.Stroke.prototype.getStartTimeMs = function() {
+ return /** @type {?string} */ (this.get$Value(9));
+};
+
+
+/**
+ * Gets the value of the start_time_ms field or the default value if not set.
+ * @return {string} The value.
+ */
+sketchology.proto.Stroke.prototype.getStartTimeMsOrDefault = function() {
+ return /** @type {string} */ (this.get$ValueOrDefault(9));
+};
+
+
+/**
+ * Sets the value of the start_time_ms field.
+ * @param {string} value The value.
+ */
+sketchology.proto.Stroke.prototype.setStartTimeMs = function(value) {
+ this.set$Value(9, value);
+};
+
+
+/**
+ * @return {boolean} Whether the start_time_ms field has a value.
+ */
+sketchology.proto.Stroke.prototype.hasStartTimeMs = function() {
+ return this.has$Value(9);
+};
+
+
+/**
+ * @return {number} The number of values in the start_time_ms field.
+ */
+sketchology.proto.Stroke.prototype.startTimeMsCount = function() {
+ return this.count$Values(9);
+};
+
+
+/**
+ * Clears the values in the start_time_ms field.
+ */
+sketchology.proto.Stroke.prototype.clearStartTimeMs = function() {
+ this.clear$Field(9);
+};
+
+
+
+/**
+ * Message UncompressedStroke.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.UncompressedStroke = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.UncompressedStroke, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.UncompressedStroke.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.UncompressedStroke} The cloned message.
+ * @override
+ */
+sketchology.proto.UncompressedStroke.prototype.clone;
+
+
+/**
+ * Gets the value of the outline field at the index given.
+ * @param {number} index The index to lookup.
+ * @return {?sketchology.proto.Point} The value.
+ */
+sketchology.proto.UncompressedStroke.prototype.getOutline = function(index) {
+ return /** @type {?sketchology.proto.Point} */ (this.get$Value(1, index));
+};
+
+
+/**
+ * Gets the value of the outline field at the index given or the default value if not set.
+ * @param {number} index The index to lookup.
+ * @return {!sketchology.proto.Point} The value.
+ */
+sketchology.proto.UncompressedStroke.prototype.getOutlineOrDefault = function(index) {
+ return /** @type {!sketchology.proto.Point} */ (this.get$ValueOrDefault(1, index));
+};
+
+
+/**
+ * Adds a value to the outline field.
+ * @param {!sketchology.proto.Point} value The value to add.
+ */
+sketchology.proto.UncompressedStroke.prototype.addOutline = function(value) {
+ this.add$Value(1, value);
+};
+
+
+/**
+ * Returns the array of values in the outline field.
+ * @return {!Array<!sketchology.proto.Point>} The values in the field.
+ */
+sketchology.proto.UncompressedStroke.prototype.outlineArray = function() {
+ return /** @type {!Array<!sketchology.proto.Point>} */ (this.array$Values(1));
+};
+
+
+/**
+ * @return {boolean} Whether the outline field has a value.
+ */
+sketchology.proto.UncompressedStroke.prototype.hasOutline = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the outline field.
+ */
+sketchology.proto.UncompressedStroke.prototype.outlineCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the outline field.
+ */
+sketchology.proto.UncompressedStroke.prototype.clearOutline = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the rgba field.
+ * @return {?number} The value.
+ */
+sketchology.proto.UncompressedStroke.prototype.getRgba = function() {
+ return /** @type {?number} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the rgba field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.UncompressedStroke.prototype.getRgbaOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the rgba field.
+ * @param {number} value The value.
+ */
+sketchology.proto.UncompressedStroke.prototype.setRgba = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the rgba field has a value.
+ */
+sketchology.proto.UncompressedStroke.prototype.hasRgba = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the rgba field.
+ */
+sketchology.proto.UncompressedStroke.prototype.rgbaCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the rgba field.
+ */
+sketchology.proto.UncompressedStroke.prototype.clearRgba = function() {
+ this.clear$Field(2);
+};
+
+
+
+/**
+ * Message AffineTransform.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.AffineTransform = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.AffineTransform, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.AffineTransform.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.AffineTransform} The cloned message.
+ * @override
+ */
+sketchology.proto.AffineTransform.prototype.clone;
+
+
+/**
+ * Gets the value of the tx field.
+ * @return {?number} The value.
+ */
+sketchology.proto.AffineTransform.prototype.getTx = function() {
+ return /** @type {?number} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the tx field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.AffineTransform.prototype.getTxOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the tx field.
+ * @param {number} value The value.
+ */
+sketchology.proto.AffineTransform.prototype.setTx = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the tx field has a value.
+ */
+sketchology.proto.AffineTransform.prototype.hasTx = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the tx field.
+ */
+sketchology.proto.AffineTransform.prototype.txCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the tx field.
+ */
+sketchology.proto.AffineTransform.prototype.clearTx = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the ty field.
+ * @return {?number} The value.
+ */
+sketchology.proto.AffineTransform.prototype.getTy = function() {
+ return /** @type {?number} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the ty field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.AffineTransform.prototype.getTyOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the ty field.
+ * @param {number} value The value.
+ */
+sketchology.proto.AffineTransform.prototype.setTy = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the ty field has a value.
+ */
+sketchology.proto.AffineTransform.prototype.hasTy = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the ty field.
+ */
+sketchology.proto.AffineTransform.prototype.tyCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the ty field.
+ */
+sketchology.proto.AffineTransform.prototype.clearTy = function() {
+ this.clear$Field(2);
+};
+
+
+/**
+ * Gets the value of the scale_x field.
+ * @return {?number} The value.
+ */
+sketchology.proto.AffineTransform.prototype.getScaleX = function() {
+ return /** @type {?number} */ (this.get$Value(3));
+};
+
+
+/**
+ * Gets the value of the scale_x field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.AffineTransform.prototype.getScaleXOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(3));
+};
+
+
+/**
+ * Sets the value of the scale_x field.
+ * @param {number} value The value.
+ */
+sketchology.proto.AffineTransform.prototype.setScaleX = function(value) {
+ this.set$Value(3, value);
+};
+
+
+/**
+ * @return {boolean} Whether the scale_x field has a value.
+ */
+sketchology.proto.AffineTransform.prototype.hasScaleX = function() {
+ return this.has$Value(3);
+};
+
+
+/**
+ * @return {number} The number of values in the scale_x field.
+ */
+sketchology.proto.AffineTransform.prototype.scaleXCount = function() {
+ return this.count$Values(3);
+};
+
+
+/**
+ * Clears the values in the scale_x field.
+ */
+sketchology.proto.AffineTransform.prototype.clearScaleX = function() {
+ this.clear$Field(3);
+};
+
+
+/**
+ * Gets the value of the scale_y field.
+ * @return {?number} The value.
+ */
+sketchology.proto.AffineTransform.prototype.getScaleY = function() {
+ return /** @type {?number} */ (this.get$Value(4));
+};
+
+
+/**
+ * Gets the value of the scale_y field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.AffineTransform.prototype.getScaleYOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(4));
+};
+
+
+/**
+ * Sets the value of the scale_y field.
+ * @param {number} value The value.
+ */
+sketchology.proto.AffineTransform.prototype.setScaleY = function(value) {
+ this.set$Value(4, value);
+};
+
+
+/**
+ * @return {boolean} Whether the scale_y field has a value.
+ */
+sketchology.proto.AffineTransform.prototype.hasScaleY = function() {
+ return this.has$Value(4);
+};
+
+
+/**
+ * @return {number} The number of values in the scale_y field.
+ */
+sketchology.proto.AffineTransform.prototype.scaleYCount = function() {
+ return this.count$Values(4);
+};
+
+
+/**
+ * Clears the values in the scale_y field.
+ */
+sketchology.proto.AffineTransform.prototype.clearScaleY = function() {
+ this.clear$Field(4);
+};
+
+
+/**
+ * Gets the value of the rotation_radians field.
+ * @return {?number} The value.
+ */
+sketchology.proto.AffineTransform.prototype.getRotationRadians = function() {
+ return /** @type {?number} */ (this.get$Value(5));
+};
+
+
+/**
+ * Gets the value of the rotation_radians field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.AffineTransform.prototype.getRotationRadiansOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(5));
+};
+
+
+/**
+ * Sets the value of the rotation_radians field.
+ * @param {number} value The value.
+ */
+sketchology.proto.AffineTransform.prototype.setRotationRadians = function(value) {
+ this.set$Value(5, value);
+};
+
+
+/**
+ * @return {boolean} Whether the rotation_radians field has a value.
+ */
+sketchology.proto.AffineTransform.prototype.hasRotationRadians = function() {
+ return this.has$Value(5);
+};
+
+
+/**
+ * @return {number} The number of values in the rotation_radians field.
+ */
+sketchology.proto.AffineTransform.prototype.rotationRadiansCount = function() {
+ return this.count$Values(5);
+};
+
+
+/**
+ * Clears the values in the rotation_radians field.
+ */
+sketchology.proto.AffineTransform.prototype.clearRotationRadians = function() {
+ this.clear$Field(5);
+};
+
+
+
+/**
+ * Message Element.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.Element = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.Element, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.Element.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.Element} The cloned message.
+ * @override
+ */
+sketchology.proto.Element.prototype.clone;
+
+
+/**
+ * Gets the value of the deprecated_uuid field.
+ * @return {?string} The value.
+ */
+sketchology.proto.Element.prototype.getDeprecatedUuid = function() {
+ return /** @type {?string} */ (this.get$Value(4));
+};
+
+
+/**
+ * Gets the value of the deprecated_uuid field or the default value if not set.
+ * @return {string} The value.
+ */
+sketchology.proto.Element.prototype.getDeprecatedUuidOrDefault = function() {
+ return /** @type {string} */ (this.get$ValueOrDefault(4));
+};
+
+
+/**
+ * Sets the value of the deprecated_uuid field.
+ * @param {string} value The value.
+ */
+sketchology.proto.Element.prototype.setDeprecatedUuid = function(value) {
+ this.set$Value(4, value);
+};
+
+
+/**
+ * @return {boolean} Whether the deprecated_uuid field has a value.
+ */
+sketchology.proto.Element.prototype.hasDeprecatedUuid = function() {
+ return this.has$Value(4);
+};
+
+
+/**
+ * @return {number} The number of values in the deprecated_uuid field.
+ */
+sketchology.proto.Element.prototype.deprecatedUuidCount = function() {
+ return this.count$Values(4);
+};
+
+
+/**
+ * Clears the values in the deprecated_uuid field.
+ */
+sketchology.proto.Element.prototype.clearDeprecatedUuid = function() {
+ this.clear$Field(4);
+};
+
+
+/**
+ * Gets the value of the minimum_serializer_version field.
+ * @return {?number} The value.
+ */
+sketchology.proto.Element.prototype.getMinimumSerializerVersion = function() {
+ return /** @type {?number} */ (this.get$Value(5));
+};
+
+
+/**
+ * Gets the value of the minimum_serializer_version field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.Element.prototype.getMinimumSerializerVersionOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(5));
+};
+
+
+/**
+ * Sets the value of the minimum_serializer_version field.
+ * @param {number} value The value.
+ */
+sketchology.proto.Element.prototype.setMinimumSerializerVersion = function(value) {
+ this.set$Value(5, value);
+};
+
+
+/**
+ * @return {boolean} Whether the minimum_serializer_version field has a value.
+ */
+sketchology.proto.Element.prototype.hasMinimumSerializerVersion = function() {
+ return this.has$Value(5);
+};
+
+
+/**
+ * @return {number} The number of values in the minimum_serializer_version field.
+ */
+sketchology.proto.Element.prototype.minimumSerializerVersionCount = function() {
+ return this.count$Values(5);
+};
+
+
+/**
+ * Clears the values in the minimum_serializer_version field.
+ */
+sketchology.proto.Element.prototype.clearMinimumSerializerVersion = function() {
+ this.clear$Field(5);
+};
+
+
+/**
+ * Gets the value of the stroke field.
+ * @return {?sketchology.proto.Stroke} The value.
+ */
+sketchology.proto.Element.prototype.getStroke = function() {
+ return /** @type {?sketchology.proto.Stroke} */ (this.get$Value(6));
+};
+
+
+/**
+ * Gets the value of the stroke field or the default value if not set.
+ * @return {!sketchology.proto.Stroke} The value.
+ */
+sketchology.proto.Element.prototype.getStrokeOrDefault = function() {
+ return /** @type {!sketchology.proto.Stroke} */ (this.get$ValueOrDefault(6));
+};
+
+
+/**
+ * Sets the value of the stroke field.
+ * @param {!sketchology.proto.Stroke} value The value.
+ */
+sketchology.proto.Element.prototype.setStroke = function(value) {
+ this.set$Value(6, value);
+};
+
+
+/**
+ * @return {boolean} Whether the stroke field has a value.
+ */
+sketchology.proto.Element.prototype.hasStroke = function() {
+ return this.has$Value(6);
+};
+
+
+/**
+ * @return {number} The number of values in the stroke field.
+ */
+sketchology.proto.Element.prototype.strokeCount = function() {
+ return this.count$Values(6);
+};
+
+
+/**
+ * Clears the values in the stroke field.
+ */
+sketchology.proto.Element.prototype.clearStroke = function() {
+ this.clear$Field(6);
+};
+
+
+/**
+ * Gets the value of the path field.
+ * @return {?sketchology.proto.Path} The value.
+ */
+sketchology.proto.Element.prototype.getPath = function() {
+ return /** @type {?sketchology.proto.Path} */ (this.get$Value(9));
+};
+
+
+/**
+ * Gets the value of the path field or the default value if not set.
+ * @return {!sketchology.proto.Path} The value.
+ */
+sketchology.proto.Element.prototype.getPathOrDefault = function() {
+ return /** @type {!sketchology.proto.Path} */ (this.get$ValueOrDefault(9));
+};
+
+
+/**
+ * Sets the value of the path field.
+ * @param {!sketchology.proto.Path} value The value.
+ */
+sketchology.proto.Element.prototype.setPath = function(value) {
+ this.set$Value(9, value);
+};
+
+
+/**
+ * @return {boolean} Whether the path field has a value.
+ */
+sketchology.proto.Element.prototype.hasPath = function() {
+ return this.has$Value(9);
+};
+
+
+/**
+ * @return {number} The number of values in the path field.
+ */
+sketchology.proto.Element.prototype.pathCount = function() {
+ return this.count$Values(9);
+};
+
+
+/**
+ * Clears the values in the path field.
+ */
+sketchology.proto.Element.prototype.clearPath = function() {
+ this.clear$Field(9);
+};
+
+
+/**
+ * Gets the value of the attributes field.
+ * @return {?sketchology.proto.ElementAttributes} The value.
+ */
+sketchology.proto.Element.prototype.getAttributes = function() {
+ return /** @type {?sketchology.proto.ElementAttributes} */ (this.get$Value(10));
+};
+
+
+/**
+ * Gets the value of the attributes field or the default value if not set.
+ * @return {!sketchology.proto.ElementAttributes} The value.
+ */
+sketchology.proto.Element.prototype.getAttributesOrDefault = function() {
+ return /** @type {!sketchology.proto.ElementAttributes} */ (this.get$ValueOrDefault(10));
+};
+
+
+/**
+ * Sets the value of the attributes field.
+ * @param {!sketchology.proto.ElementAttributes} value The value.
+ */
+sketchology.proto.Element.prototype.setAttributes = function(value) {
+ this.set$Value(10, value);
+};
+
+
+/**
+ * @return {boolean} Whether the attributes field has a value.
+ */
+sketchology.proto.Element.prototype.hasAttributes = function() {
+ return this.has$Value(10);
+};
+
+
+/**
+ * @return {number} The number of values in the attributes field.
+ */
+sketchology.proto.Element.prototype.attributesCount = function() {
+ return this.count$Values(10);
+};
+
+
+/**
+ * Clears the values in the attributes field.
+ */
+sketchology.proto.Element.prototype.clearAttributes = function() {
+ this.clear$Field(10);
+};
+
+
+
+/**
+ * Message ElementAttributes.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.ElementAttributes = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.ElementAttributes, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.ElementAttributes.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.ElementAttributes} The cloned message.
+ * @override
+ */
+sketchology.proto.ElementAttributes.prototype.clone;
+
+
+/**
+ * Gets the value of the selectable field.
+ * @return {?boolean} The value.
+ */
+sketchology.proto.ElementAttributes.prototype.getSelectable = function() {
+ return /** @type {?boolean} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the selectable field or the default value if not set.
+ * @return {boolean} The value.
+ */
+sketchology.proto.ElementAttributes.prototype.getSelectableOrDefault = function() {
+ return /** @type {boolean} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the selectable field.
+ * @param {boolean} value The value.
+ */
+sketchology.proto.ElementAttributes.prototype.setSelectable = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the selectable field has a value.
+ */
+sketchology.proto.ElementAttributes.prototype.hasSelectable = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the selectable field.
+ */
+sketchology.proto.ElementAttributes.prototype.selectableCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the selectable field.
+ */
+sketchology.proto.ElementAttributes.prototype.clearSelectable = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the magic_erasable field.
+ * @return {?boolean} The value.
+ */
+sketchology.proto.ElementAttributes.prototype.getMagicErasable = function() {
+ return /** @type {?boolean} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the magic_erasable field or the default value if not set.
+ * @return {boolean} The value.
+ */
+sketchology.proto.ElementAttributes.prototype.getMagicErasableOrDefault = function() {
+ return /** @type {boolean} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the magic_erasable field.
+ * @param {boolean} value The value.
+ */
+sketchology.proto.ElementAttributes.prototype.setMagicErasable = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the magic_erasable field has a value.
+ */
+sketchology.proto.ElementAttributes.prototype.hasMagicErasable = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the magic_erasable field.
+ */
+sketchology.proto.ElementAttributes.prototype.magicErasableCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the magic_erasable field.
+ */
+sketchology.proto.ElementAttributes.prototype.clearMagicErasable = function() {
+ this.clear$Field(2);
+};
+
+
+/**
+ * Gets the value of the is_sticker field.
+ * @return {?boolean} The value.
+ */
+sketchology.proto.ElementAttributes.prototype.getIsSticker = function() {
+ return /** @type {?boolean} */ (this.get$Value(3));
+};
+
+
+/**
+ * Gets the value of the is_sticker field or the default value if not set.
+ * @return {boolean} The value.
+ */
+sketchology.proto.ElementAttributes.prototype.getIsStickerOrDefault = function() {
+ return /** @type {boolean} */ (this.get$ValueOrDefault(3));
+};
+
+
+/**
+ * Sets the value of the is_sticker field.
+ * @param {boolean} value The value.
+ */
+sketchology.proto.ElementAttributes.prototype.setIsSticker = function(value) {
+ this.set$Value(3, value);
+};
+
+
+/**
+ * @return {boolean} Whether the is_sticker field has a value.
+ */
+sketchology.proto.ElementAttributes.prototype.hasIsSticker = function() {
+ return this.has$Value(3);
+};
+
+
+/**
+ * @return {number} The number of values in the is_sticker field.
+ */
+sketchology.proto.ElementAttributes.prototype.isStickerCount = function() {
+ return this.count$Values(3);
+};
+
+
+/**
+ * Clears the values in the is_sticker field.
+ */
+sketchology.proto.ElementAttributes.prototype.clearIsSticker = function() {
+ this.clear$Field(3);
+};
+
+
+/**
+ * Gets the value of the is_text field.
+ * @return {?boolean} The value.
+ */
+sketchology.proto.ElementAttributes.prototype.getIsText = function() {
+ return /** @type {?boolean} */ (this.get$Value(4));
+};
+
+
+/**
+ * Gets the value of the is_text field or the default value if not set.
+ * @return {boolean} The value.
+ */
+sketchology.proto.ElementAttributes.prototype.getIsTextOrDefault = function() {
+ return /** @type {boolean} */ (this.get$ValueOrDefault(4));
+};
+
+
+/**
+ * Sets the value of the is_text field.
+ * @param {boolean} value The value.
+ */
+sketchology.proto.ElementAttributes.prototype.setIsText = function(value) {
+ this.set$Value(4, value);
+};
+
+
+/**
+ * @return {boolean} Whether the is_text field has a value.
+ */
+sketchology.proto.ElementAttributes.prototype.hasIsText = function() {
+ return this.has$Value(4);
+};
+
+
+/**
+ * @return {number} The number of values in the is_text field.
+ */
+sketchology.proto.ElementAttributes.prototype.isTextCount = function() {
+ return this.count$Values(4);
+};
+
+
+/**
+ * Clears the values in the is_text field.
+ */
+sketchology.proto.ElementAttributes.prototype.clearIsText = function() {
+ this.clear$Field(4);
+};
+
+
+
+/**
+ * Message UncompressedElement.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.UncompressedElement = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.UncompressedElement, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.UncompressedElement.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.UncompressedElement} The cloned message.
+ * @override
+ */
+sketchology.proto.UncompressedElement.prototype.clone;
+
+
+/**
+ * Gets the value of the uncompressed_stroke field.
+ * @return {?sketchology.proto.UncompressedStroke} The value.
+ */
+sketchology.proto.UncompressedElement.prototype.getUncompressedStroke = function() {
+ return /** @type {?sketchology.proto.UncompressedStroke} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the uncompressed_stroke field or the default value if not set.
+ * @return {!sketchology.proto.UncompressedStroke} The value.
+ */
+sketchology.proto.UncompressedElement.prototype.getUncompressedStrokeOrDefault = function() {
+ return /** @type {!sketchology.proto.UncompressedStroke} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the uncompressed_stroke field.
+ * @param {!sketchology.proto.UncompressedStroke} value The value.
+ */
+sketchology.proto.UncompressedElement.prototype.setUncompressedStroke = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the uncompressed_stroke field has a value.
+ */
+sketchology.proto.UncompressedElement.prototype.hasUncompressedStroke = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the uncompressed_stroke field.
+ */
+sketchology.proto.UncompressedElement.prototype.uncompressedStrokeCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the uncompressed_stroke field.
+ */
+sketchology.proto.UncompressedElement.prototype.clearUncompressedStroke = function() {
+ this.clear$Field(1);
+};
+
+
+
+/**
+ * Message ElementMutation.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.ElementMutation = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.ElementMutation, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.ElementMutation.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.ElementMutation} The cloned message.
+ * @override
+ */
+sketchology.proto.ElementMutation.prototype.clone;
+
+
+/**
+ * Gets the value of the uuid field at the index given.
+ * @param {number} index The index to lookup.
+ * @return {?string} The value.
+ */
+sketchology.proto.ElementMutation.prototype.getUuid = function(index) {
+ return /** @type {?string} */ (this.get$Value(1, index));
+};
+
+
+/**
+ * Gets the value of the uuid field at the index given or the default value if not set.
+ * @param {number} index The index to lookup.
+ * @return {string} The value.
+ */
+sketchology.proto.ElementMutation.prototype.getUuidOrDefault = function(index) {
+ return /** @type {string} */ (this.get$ValueOrDefault(1, index));
+};
+
+
+/**
+ * Adds a value to the uuid field.
+ * @param {string} value The value to add.
+ */
+sketchology.proto.ElementMutation.prototype.addUuid = function(value) {
+ this.add$Value(1, value);
+};
+
+
+/**
+ * Returns the array of values in the uuid field.
+ * @return {!Array<string>} The values in the field.
+ */
+sketchology.proto.ElementMutation.prototype.uuidArray = function() {
+ return /** @type {!Array<string>} */ (this.array$Values(1));
+};
+
+
+/**
+ * @return {boolean} Whether the uuid field has a value.
+ */
+sketchology.proto.ElementMutation.prototype.hasUuid = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the uuid field.
+ */
+sketchology.proto.ElementMutation.prototype.uuidCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the uuid field.
+ */
+sketchology.proto.ElementMutation.prototype.clearUuid = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the transform field at the index given.
+ * @param {number} index The index to lookup.
+ * @return {?sketchology.proto.AffineTransform} The value.
+ */
+sketchology.proto.ElementMutation.prototype.getTransform = function(index) {
+ return /** @type {?sketchology.proto.AffineTransform} */ (this.get$Value(2, index));
+};
+
+
+/**
+ * Gets the value of the transform field at the index given or the default value if not set.
+ * @param {number} index The index to lookup.
+ * @return {!sketchology.proto.AffineTransform} The value.
+ */
+sketchology.proto.ElementMutation.prototype.getTransformOrDefault = function(index) {
+ return /** @type {!sketchology.proto.AffineTransform} */ (this.get$ValueOrDefault(2, index));
+};
+
+
+/**
+ * Adds a value to the transform field.
+ * @param {!sketchology.proto.AffineTransform} value The value to add.
+ */
+sketchology.proto.ElementMutation.prototype.addTransform = function(value) {
+ this.add$Value(2, value);
+};
+
+
+/**
+ * Returns the array of values in the transform field.
+ * @return {!Array<!sketchology.proto.AffineTransform>} The values in the field.
+ */
+sketchology.proto.ElementMutation.prototype.transformArray = function() {
+ return /** @type {!Array<!sketchology.proto.AffineTransform>} */ (this.array$Values(2));
+};
+
+
+/**
+ * @return {boolean} Whether the transform field has a value.
+ */
+sketchology.proto.ElementMutation.prototype.hasTransform = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the transform field.
+ */
+sketchology.proto.ElementMutation.prototype.transformCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the transform field.
+ */
+sketchology.proto.ElementMutation.prototype.clearTransform = function() {
+ this.clear$Field(2);
+};
+
+
+
+/**
+ * Message ElementIdList.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.ElementIdList = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.ElementIdList, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.ElementIdList.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.ElementIdList} The cloned message.
+ * @override
+ */
+sketchology.proto.ElementIdList.prototype.clone;
+
+
+/**
+ * Gets the value of the uuid field at the index given.
+ * @param {number} index The index to lookup.
+ * @return {?string} The value.
+ */
+sketchology.proto.ElementIdList.prototype.getUuid = function(index) {
+ return /** @type {?string} */ (this.get$Value(1, index));
+};
+
+
+/**
+ * Gets the value of the uuid field at the index given or the default value if not set.
+ * @param {number} index The index to lookup.
+ * @return {string} The value.
+ */
+sketchology.proto.ElementIdList.prototype.getUuidOrDefault = function(index) {
+ return /** @type {string} */ (this.get$ValueOrDefault(1, index));
+};
+
+
+/**
+ * Adds a value to the uuid field.
+ * @param {string} value The value to add.
+ */
+sketchology.proto.ElementIdList.prototype.addUuid = function(value) {
+ this.add$Value(1, value);
+};
+
+
+/**
+ * Returns the array of values in the uuid field.
+ * @return {!Array<string>} The values in the field.
+ */
+sketchology.proto.ElementIdList.prototype.uuidArray = function() {
+ return /** @type {!Array<string>} */ (this.array$Values(1));
+};
+
+
+/**
+ * @return {boolean} Whether the uuid field has a value.
+ */
+sketchology.proto.ElementIdList.prototype.hasUuid = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the uuid field.
+ */
+sketchology.proto.ElementIdList.prototype.uuidCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the uuid field.
+ */
+sketchology.proto.ElementIdList.prototype.clearUuid = function() {
+ this.clear$Field(1);
+};
+
+
+
+/**
+ * Message Point.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.Point = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.Point, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.Point.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.Point} The cloned message.
+ * @override
+ */
+sketchology.proto.Point.prototype.clone;
+
+
+/**
+ * Gets the value of the x field.
+ * @return {?number} The value.
+ */
+sketchology.proto.Point.prototype.getX = function() {
+ return /** @type {?number} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the x field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.Point.prototype.getXOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the x field.
+ * @param {number} value The value.
+ */
+sketchology.proto.Point.prototype.setX = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the x field has a value.
+ */
+sketchology.proto.Point.prototype.hasX = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the x field.
+ */
+sketchology.proto.Point.prototype.xCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the x field.
+ */
+sketchology.proto.Point.prototype.clearX = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the y field.
+ * @return {?number} The value.
+ */
+sketchology.proto.Point.prototype.getY = function() {
+ return /** @type {?number} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the y field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.Point.prototype.getYOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the y field.
+ * @param {number} value The value.
+ */
+sketchology.proto.Point.prototype.setY = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the y field has a value.
+ */
+sketchology.proto.Point.prototype.hasY = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the y field.
+ */
+sketchology.proto.Point.prototype.yCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the y field.
+ */
+sketchology.proto.Point.prototype.clearY = function() {
+ this.clear$Field(2);
+};
+
+
+
+/**
+ * Message ElementBundle.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.ElementBundle = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.ElementBundle, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.ElementBundle.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.ElementBundle} The cloned message.
+ * @override
+ */
+sketchology.proto.ElementBundle.prototype.clone;
+
+
+/**
+ * Gets the value of the uuid field.
+ * @return {?string} The value.
+ */
+sketchology.proto.ElementBundle.prototype.getUuid = function() {
+ return /** @type {?string} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the uuid field or the default value if not set.
+ * @return {string} The value.
+ */
+sketchology.proto.ElementBundle.prototype.getUuidOrDefault = function() {
+ return /** @type {string} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the uuid field.
+ * @param {string} value The value.
+ */
+sketchology.proto.ElementBundle.prototype.setUuid = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the uuid field has a value.
+ */
+sketchology.proto.ElementBundle.prototype.hasUuid = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the uuid field.
+ */
+sketchology.proto.ElementBundle.prototype.uuidCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the uuid field.
+ */
+sketchology.proto.ElementBundle.prototype.clearUuid = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the element field.
+ * @return {?sketchology.proto.Element} The value.
+ */
+sketchology.proto.ElementBundle.prototype.getElement = function() {
+ return /** @type {?sketchology.proto.Element} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the element field or the default value if not set.
+ * @return {!sketchology.proto.Element} The value.
+ */
+sketchology.proto.ElementBundle.prototype.getElementOrDefault = function() {
+ return /** @type {!sketchology.proto.Element} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the element field.
+ * @param {!sketchology.proto.Element} value The value.
+ */
+sketchology.proto.ElementBundle.prototype.setElement = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the element field has a value.
+ */
+sketchology.proto.ElementBundle.prototype.hasElement = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the element field.
+ */
+sketchology.proto.ElementBundle.prototype.elementCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the element field.
+ */
+sketchology.proto.ElementBundle.prototype.clearElement = function() {
+ this.clear$Field(2);
+};
+
+
+/**
+ * Gets the value of the transform field.
+ * @return {?sketchology.proto.AffineTransform} The value.
+ */
+sketchology.proto.ElementBundle.prototype.getTransform = function() {
+ return /** @type {?sketchology.proto.AffineTransform} */ (this.get$Value(3));
+};
+
+
+/**
+ * Gets the value of the transform field or the default value if not set.
+ * @return {!sketchology.proto.AffineTransform} The value.
+ */
+sketchology.proto.ElementBundle.prototype.getTransformOrDefault = function() {
+ return /** @type {!sketchology.proto.AffineTransform} */ (this.get$ValueOrDefault(3));
+};
+
+
+/**
+ * Sets the value of the transform field.
+ * @param {!sketchology.proto.AffineTransform} value The value.
+ */
+sketchology.proto.ElementBundle.prototype.setTransform = function(value) {
+ this.set$Value(3, value);
+};
+
+
+/**
+ * @return {boolean} Whether the transform field has a value.
+ */
+sketchology.proto.ElementBundle.prototype.hasTransform = function() {
+ return this.has$Value(3);
+};
+
+
+/**
+ * @return {number} The number of values in the transform field.
+ */
+sketchology.proto.ElementBundle.prototype.transformCount = function() {
+ return this.count$Values(3);
+};
+
+
+/**
+ * Clears the values in the transform field.
+ */
+sketchology.proto.ElementBundle.prototype.clearTransform = function() {
+ this.clear$Field(3);
+};
+
+
+/**
+ * Gets the value of the uncompressed_element field.
+ * @return {?sketchology.proto.UncompressedElement} The value.
+ */
+sketchology.proto.ElementBundle.prototype.getUncompressedElement = function() {
+ return /** @type {?sketchology.proto.UncompressedElement} */ (this.get$Value(4));
+};
+
+
+/**
+ * Gets the value of the uncompressed_element field or the default value if not set.
+ * @return {!sketchology.proto.UncompressedElement} The value.
+ */
+sketchology.proto.ElementBundle.prototype.getUncompressedElementOrDefault = function() {
+ return /** @type {!sketchology.proto.UncompressedElement} */ (this.get$ValueOrDefault(4));
+};
+
+
+/**
+ * Sets the value of the uncompressed_element field.
+ * @param {!sketchology.proto.UncompressedElement} value The value.
+ */
+sketchology.proto.ElementBundle.prototype.setUncompressedElement = function(value) {
+ this.set$Value(4, value);
+};
+
+
+/**
+ * @return {boolean} Whether the uncompressed_element field has a value.
+ */
+sketchology.proto.ElementBundle.prototype.hasUncompressedElement = function() {
+ return this.has$Value(4);
+};
+
+
+/**
+ * @return {number} The number of values in the uncompressed_element field.
+ */
+sketchology.proto.ElementBundle.prototype.uncompressedElementCount = function() {
+ return this.count$Values(4);
+};
+
+
+/**
+ * Clears the values in the uncompressed_element field.
+ */
+sketchology.proto.ElementBundle.prototype.clearUncompressedElement = function() {
+ this.clear$Field(4);
+};
+
+
+
+/**
+ * Message Path.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.Path = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.Path, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.Path.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.Path} The cloned message.
+ * @override
+ */
+sketchology.proto.Path.prototype.clone;
+
+
+/**
+ * Gets the value of the segment_types field at the index given.
+ * @param {number} index The index to lookup.
+ * @return {?sketchology.proto.Path.SegmentType} The value.
+ */
+sketchology.proto.Path.prototype.getSegmentTypes = function(index) {
+ return /** @type {?sketchology.proto.Path.SegmentType} */ (this.get$Value(1, index));
+};
+
+
+/**
+ * Gets the value of the segment_types field at the index given or the default value if not set.
+ * @param {number} index The index to lookup.
+ * @return {!sketchology.proto.Path.SegmentType} The value.
+ */
+sketchology.proto.Path.prototype.getSegmentTypesOrDefault = function(index) {
+ return /** @type {!sketchology.proto.Path.SegmentType} */ (this.get$ValueOrDefault(1, index));
+};
+
+
+/**
+ * Adds a value to the segment_types field.
+ * @param {!sketchology.proto.Path.SegmentType} value The value to add.
+ */
+sketchology.proto.Path.prototype.addSegmentTypes = function(value) {
+ this.add$Value(1, value);
+};
+
+
+/**
+ * Returns the array of values in the segment_types field.
+ * @return {!Array<!sketchology.proto.Path.SegmentType>} The values in the field.
+ */
+sketchology.proto.Path.prototype.segmentTypesArray = function() {
+ return /** @type {!Array<!sketchology.proto.Path.SegmentType>} */ (this.array$Values(1));
+};
+
+
+/**
+ * @return {boolean} Whether the segment_types field has a value.
+ */
+sketchology.proto.Path.prototype.hasSegmentTypes = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the segment_types field.
+ */
+sketchology.proto.Path.prototype.segmentTypesCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the segment_types field.
+ */
+sketchology.proto.Path.prototype.clearSegmentTypes = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the segment_counts field at the index given.
+ * @param {number} index The index to lookup.
+ * @return {?number} The value.
+ */
+sketchology.proto.Path.prototype.getSegmentCounts = function(index) {
+ return /** @type {?number} */ (this.get$Value(2, index));
+};
+
+
+/**
+ * Gets the value of the segment_counts field at the index given or the default value if not set.
+ * @param {number} index The index to lookup.
+ * @return {number} The value.
+ */
+sketchology.proto.Path.prototype.getSegmentCountsOrDefault = function(index) {
+ return /** @type {number} */ (this.get$ValueOrDefault(2, index));
+};
+
+
+/**
+ * Adds a value to the segment_counts field.
+ * @param {number} value The value to add.
+ */
+sketchology.proto.Path.prototype.addSegmentCounts = function(value) {
+ this.add$Value(2, value);
+};
+
+
+/**
+ * Returns the array of values in the segment_counts field.
+ * @return {!Array<number>} The values in the field.
+ */
+sketchology.proto.Path.prototype.segmentCountsArray = function() {
+ return /** @type {!Array<number>} */ (this.array$Values(2));
+};
+
+
+/**
+ * @return {boolean} Whether the segment_counts field has a value.
+ */
+sketchology.proto.Path.prototype.hasSegmentCounts = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the segment_counts field.
+ */
+sketchology.proto.Path.prototype.segmentCountsCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the segment_counts field.
+ */
+sketchology.proto.Path.prototype.clearSegmentCounts = function() {
+ this.clear$Field(2);
+};
+
+
+/**
+ * Gets the value of the segment_args field at the index given.
+ * @param {number} index The index to lookup.
+ * @return {?number} The value.
+ */
+sketchology.proto.Path.prototype.getSegmentArgs = function(index) {
+ return /** @type {?number} */ (this.get$Value(3, index));
+};
+
+
+/**
+ * Gets the value of the segment_args field at the index given or the default value if not set.
+ * @param {number} index The index to lookup.
+ * @return {number} The value.
+ */
+sketchology.proto.Path.prototype.getSegmentArgsOrDefault = function(index) {
+ return /** @type {number} */ (this.get$ValueOrDefault(3, index));
+};
+
+
+/**
+ * Adds a value to the segment_args field.
+ * @param {number} value The value to add.
+ */
+sketchology.proto.Path.prototype.addSegmentArgs = function(value) {
+ this.add$Value(3, value);
+};
+
+
+/**
+ * Returns the array of values in the segment_args field.
+ * @return {!Array<number>} The values in the field.
+ */
+sketchology.proto.Path.prototype.segmentArgsArray = function() {
+ return /** @type {!Array<number>} */ (this.array$Values(3));
+};
+
+
+/**
+ * @return {boolean} Whether the segment_args field has a value.
+ */
+sketchology.proto.Path.prototype.hasSegmentArgs = function() {
+ return this.has$Value(3);
+};
+
+
+/**
+ * @return {number} The number of values in the segment_args field.
+ */
+sketchology.proto.Path.prototype.segmentArgsCount = function() {
+ return this.count$Values(3);
+};
+
+
+/**
+ * Clears the values in the segment_args field.
+ */
+sketchology.proto.Path.prototype.clearSegmentArgs = function() {
+ this.clear$Field(3);
+};
+
+
+/**
+ * Gets the value of the radius field.
+ * @return {?number} The value.
+ */
+sketchology.proto.Path.prototype.getRadius = function() {
+ return /** @type {?number} */ (this.get$Value(4));
+};
+
+
+/**
+ * Gets the value of the radius field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.Path.prototype.getRadiusOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(4));
+};
+
+
+/**
+ * Sets the value of the radius field.
+ * @param {number} value The value.
+ */
+sketchology.proto.Path.prototype.setRadius = function(value) {
+ this.set$Value(4, value);
+};
+
+
+/**
+ * @return {boolean} Whether the radius field has a value.
+ */
+sketchology.proto.Path.prototype.hasRadius = function() {
+ return this.has$Value(4);
+};
+
+
+/**
+ * @return {number} The number of values in the radius field.
+ */
+sketchology.proto.Path.prototype.radiusCount = function() {
+ return this.count$Values(4);
+};
+
+
+/**
+ * Clears the values in the radius field.
+ */
+sketchology.proto.Path.prototype.clearRadius = function() {
+ this.clear$Field(4);
+};
+
+
+/**
+ * Gets the value of the rgba field.
+ * @return {?number} The value.
+ */
+sketchology.proto.Path.prototype.getRgba = function() {
+ return /** @type {?number} */ (this.get$Value(5));
+};
+
+
+/**
+ * Gets the value of the rgba field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.Path.prototype.getRgbaOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(5));
+};
+
+
+/**
+ * Sets the value of the rgba field.
+ * @param {number} value The value.
+ */
+sketchology.proto.Path.prototype.setRgba = function(value) {
+ this.set$Value(5, value);
+};
+
+
+/**
+ * @return {boolean} Whether the rgba field has a value.
+ */
+sketchology.proto.Path.prototype.hasRgba = function() {
+ return this.has$Value(5);
+};
+
+
+/**
+ * @return {number} The number of values in the rgba field.
+ */
+sketchology.proto.Path.prototype.rgbaCount = function() {
+ return this.count$Values(5);
+};
+
+
+/**
+ * Clears the values in the rgba field.
+ */
+sketchology.proto.Path.prototype.clearRgba = function() {
+ this.clear$Field(5);
+};
+
+
+/**
+ * Gets the value of the end_cap field.
+ * @return {?sketchology.proto.Path.EndCapType} The value.
+ */
+sketchology.proto.Path.prototype.getEndCap = function() {
+ return /** @type {?sketchology.proto.Path.EndCapType} */ (this.get$Value(6));
+};
+
+
+/**
+ * Gets the value of the end_cap field or the default value if not set.
+ * @return {!sketchology.proto.Path.EndCapType} The value.
+ */
+sketchology.proto.Path.prototype.getEndCapOrDefault = function() {
+ return /** @type {!sketchology.proto.Path.EndCapType} */ (this.get$ValueOrDefault(6));
+};
+
+
+/**
+ * Sets the value of the end_cap field.
+ * @param {!sketchology.proto.Path.EndCapType} value The value.
+ */
+sketchology.proto.Path.prototype.setEndCap = function(value) {
+ this.set$Value(6, value);
+};
+
+
+/**
+ * @return {boolean} Whether the end_cap field has a value.
+ */
+sketchology.proto.Path.prototype.hasEndCap = function() {
+ return this.has$Value(6);
+};
+
+
+/**
+ * @return {number} The number of values in the end_cap field.
+ */
+sketchology.proto.Path.prototype.endCapCount = function() {
+ return this.count$Values(6);
+};
+
+
+/**
+ * Clears the values in the end_cap field.
+ */
+sketchology.proto.Path.prototype.clearEndCap = function() {
+ this.clear$Field(6);
+};
+
+
+/**
+ * Gets the value of the fill_rgba field.
+ * @return {?number} The value.
+ */
+sketchology.proto.Path.prototype.getFillRgba = function() {
+ return /** @type {?number} */ (this.get$Value(7));
+};
+
+
+/**
+ * Gets the value of the fill_rgba field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.Path.prototype.getFillRgbaOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(7));
+};
+
+
+/**
+ * Sets the value of the fill_rgba field.
+ * @param {number} value The value.
+ */
+sketchology.proto.Path.prototype.setFillRgba = function(value) {
+ this.set$Value(7, value);
+};
+
+
+/**
+ * @return {boolean} Whether the fill_rgba field has a value.
+ */
+sketchology.proto.Path.prototype.hasFillRgba = function() {
+ return this.has$Value(7);
+};
+
+
+/**
+ * @return {number} The number of values in the fill_rgba field.
+ */
+sketchology.proto.Path.prototype.fillRgbaCount = function() {
+ return this.count$Values(7);
+};
+
+
+/**
+ * Clears the values in the fill_rgba field.
+ */
+sketchology.proto.Path.prototype.clearFillRgba = function() {
+ this.clear$Field(7);
+};
+
+
+/**
+ * Enumeration SegmentType.
+ * @enum {number}
+ */
+sketchology.proto.Path.SegmentType = {
+ UNKNOWN: 0,
+ MOVE_TO: 1,
+ LINE_TO: 2,
+ CURVE_TO: 3,
+ QUAD_TO: 4,
+ CLOSE: 5
+};
+
+
+/**
+ * Enumeration EndCapType.
+ * @enum {number}
+ */
+sketchology.proto.Path.EndCapType = {
+ BUTT: 1,
+ ROUND: 2,
+ SQUARE: 3
+};
+
+
+/** @override */
+sketchology.proto.CallbackFlags.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.CallbackFlags.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'CallbackFlags',
+ fullName: 'sketchology.proto.CallbackFlags'
+ },
+ 1: {
+ name: 'mesh_data_ctm',
+ fieldType: goog.proto2.Message.FieldType.BOOL,
+ type: Boolean
+ },
+ 2: {
+ name: 'uncompressed_outline',
+ fieldType: goog.proto2.Message.FieldType.BOOL,
+ type: Boolean
+ },
+ 3: {
+ name: 'compressed_input_points',
+ fieldType: goog.proto2.Message.FieldType.BOOL,
+ type: Boolean
+ }
+ };
+ sketchology.proto.CallbackFlags.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.CallbackFlags, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.CallbackFlags.getDescriptor =
+ sketchology.proto.CallbackFlags.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.SourceDetails.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.SourceDetails.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'SourceDetails',
+ fullName: 'sketchology.proto.SourceDetails'
+ },
+ 1: {
+ name: 'origin',
+ fieldType: goog.proto2.Message.FieldType.ENUM,
+ defaultValue: sketchology.proto.SourceDetails.Origin.UNKNOWN,
+ type: sketchology.proto.SourceDetails.Origin
+ },
+ 2: {
+ name: 'host_source_details',
+ fieldType: goog.proto2.Message.FieldType.UINT32,
+ type: Number
+ }
+ };
+ sketchology.proto.SourceDetails.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.SourceDetails, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.SourceDetails.getDescriptor =
+ sketchology.proto.SourceDetails.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.BackgroundImageInfo.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.BackgroundImageInfo.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'BackgroundImageInfo',
+ fullName: 'sketchology.proto.BackgroundImageInfo'
+ },
+ 1: {
+ name: 'uri',
+ fieldType: goog.proto2.Message.FieldType.STRING,
+ type: String
+ },
+ 3: {
+ name: 'bounds',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.Rect
+ }
+ };
+ sketchology.proto.BackgroundImageInfo.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.BackgroundImageInfo, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.BackgroundImageInfo.getDescriptor =
+ sketchology.proto.BackgroundImageInfo.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.Border.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.Border.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'Border',
+ fullName: 'sketchology.proto.Border'
+ },
+ 1: {
+ name: 'uri',
+ fieldType: goog.proto2.Message.FieldType.STRING,
+ type: String
+ },
+ 2: {
+ name: 'scale',
+ fieldType: goog.proto2.Message.FieldType.FLOAT,
+ defaultValue: 1,
+ type: Number
+ }
+ };
+ sketchology.proto.Border.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.Border, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.Border.getDescriptor =
+ sketchology.proto.Border.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.LOD.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.LOD.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'LOD',
+ fullName: 'sketchology.proto.LOD'
+ },
+ 1: {
+ name: 'max_coverage',
+ fieldType: goog.proto2.Message.FieldType.FLOAT,
+ type: Number
+ },
+ 2: {
+ name: 'ctm_blob',
+ fieldType: goog.proto2.Message.FieldType.BYTES,
+ type: String
+ }
+ };
+ sketchology.proto.LOD.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.LOD, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.LOD.getDescriptor =
+ sketchology.proto.LOD.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.Stroke.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.Stroke.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'Stroke',
+ fullName: 'sketchology.proto.Stroke'
+ },
+ 1: {
+ name: 'shader_type',
+ fieldType: goog.proto2.Message.FieldType.ENUM,
+ defaultValue: sketchology.proto.ShaderType.NONE,
+ type: sketchology.proto.ShaderType
+ },
+ 3: {
+ name: 'lod',
+ repeated: true,
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.LOD
+ },
+ 4: {
+ name: 'abgr',
+ fieldType: goog.proto2.Message.FieldType.UINT32,
+ type: Number
+ },
+ 5: {
+ name: 'point_x',
+ repeated: true,
+ packed: true,
+ fieldType: goog.proto2.Message.FieldType.SINT32,
+ type: Number
+ },
+ 6: {
+ name: 'point_y',
+ repeated: true,
+ packed: true,
+ fieldType: goog.proto2.Message.FieldType.SINT32,
+ type: Number
+ },
+ 7: {
+ name: 'point_t_ms',
+ repeated: true,
+ packed: true,
+ fieldType: goog.proto2.Message.FieldType.UINT32,
+ type: Number
+ },
+ 8: {
+ name: 'deprecated_transform',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.AffineTransform
+ },
+ 9: {
+ name: 'start_time_ms',
+ fieldType: goog.proto2.Message.FieldType.UINT64,
+ type: String
+ }
+ };
+ sketchology.proto.Stroke.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.Stroke, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.Stroke.getDescriptor =
+ sketchology.proto.Stroke.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.UncompressedStroke.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.UncompressedStroke.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'UncompressedStroke',
+ fullName: 'sketchology.proto.UncompressedStroke'
+ },
+ 1: {
+ name: 'outline',
+ repeated: true,
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.Point
+ },
+ 2: {
+ name: 'rgba',
+ fieldType: goog.proto2.Message.FieldType.UINT32,
+ type: Number
+ }
+ };
+ sketchology.proto.UncompressedStroke.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.UncompressedStroke, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.UncompressedStroke.getDescriptor =
+ sketchology.proto.UncompressedStroke.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.AffineTransform.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.AffineTransform.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'AffineTransform',
+ fullName: 'sketchology.proto.AffineTransform'
+ },
+ 1: {
+ name: 'tx',
+ fieldType: goog.proto2.Message.FieldType.FLOAT,
+ type: Number
+ },
+ 2: {
+ name: 'ty',
+ fieldType: goog.proto2.Message.FieldType.FLOAT,
+ type: Number
+ },
+ 3: {
+ name: 'scale_x',
+ fieldType: goog.proto2.Message.FieldType.FLOAT,
+ defaultValue: 1,
+ type: Number
+ },
+ 4: {
+ name: 'scale_y',
+ fieldType: goog.proto2.Message.FieldType.FLOAT,
+ defaultValue: 1,
+ type: Number
+ },
+ 5: {
+ name: 'rotation_radians',
+ fieldType: goog.proto2.Message.FieldType.FLOAT,
+ type: Number
+ }
+ };
+ sketchology.proto.AffineTransform.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.AffineTransform, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.AffineTransform.getDescriptor =
+ sketchology.proto.AffineTransform.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.Element.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.Element.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'Element',
+ fullName: 'sketchology.proto.Element'
+ },
+ 4: {
+ name: 'deprecated_uuid',
+ fieldType: goog.proto2.Message.FieldType.STRING,
+ type: String
+ },
+ 5: {
+ name: 'minimum_serializer_version',
+ fieldType: goog.proto2.Message.FieldType.UINT32,
+ type: Number
+ },
+ 6: {
+ name: 'stroke',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.Stroke
+ },
+ 9: {
+ name: 'path',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.Path
+ },
+ 10: {
+ name: 'attributes',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.ElementAttributes
+ }
+ };
+ sketchology.proto.Element.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.Element, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.Element.getDescriptor =
+ sketchology.proto.Element.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.ElementAttributes.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.ElementAttributes.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'ElementAttributes',
+ fullName: 'sketchology.proto.ElementAttributes'
+ },
+ 1: {
+ name: 'selectable',
+ fieldType: goog.proto2.Message.FieldType.BOOL,
+ defaultValue: true,
+ type: Boolean
+ },
+ 2: {
+ name: 'magic_erasable',
+ fieldType: goog.proto2.Message.FieldType.BOOL,
+ defaultValue: true,
+ type: Boolean
+ },
+ 3: {
+ name: 'is_sticker',
+ fieldType: goog.proto2.Message.FieldType.BOOL,
+ defaultValue: false,
+ type: Boolean
+ },
+ 4: {
+ name: 'is_text',
+ fieldType: goog.proto2.Message.FieldType.BOOL,
+ defaultValue: false,
+ type: Boolean
+ }
+ };
+ sketchology.proto.ElementAttributes.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.ElementAttributes, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.ElementAttributes.getDescriptor =
+ sketchology.proto.ElementAttributes.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.UncompressedElement.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.UncompressedElement.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'UncompressedElement',
+ fullName: 'sketchology.proto.UncompressedElement'
+ },
+ 1: {
+ name: 'uncompressed_stroke',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.UncompressedStroke
+ }
+ };
+ sketchology.proto.UncompressedElement.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.UncompressedElement, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.UncompressedElement.getDescriptor =
+ sketchology.proto.UncompressedElement.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.ElementMutation.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.ElementMutation.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'ElementMutation',
+ fullName: 'sketchology.proto.ElementMutation'
+ },
+ 1: {
+ name: 'uuid',
+ repeated: true,
+ fieldType: goog.proto2.Message.FieldType.STRING,
+ type: String
+ },
+ 2: {
+ name: 'transform',
+ repeated: true,
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.AffineTransform
+ }
+ };
+ sketchology.proto.ElementMutation.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.ElementMutation, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.ElementMutation.getDescriptor =
+ sketchology.proto.ElementMutation.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.ElementIdList.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.ElementIdList.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'ElementIdList',
+ fullName: 'sketchology.proto.ElementIdList'
+ },
+ 1: {
+ name: 'uuid',
+ repeated: true,
+ fieldType: goog.proto2.Message.FieldType.STRING,
+ type: String
+ }
+ };
+ sketchology.proto.ElementIdList.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.ElementIdList, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.ElementIdList.getDescriptor =
+ sketchology.proto.ElementIdList.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.Point.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.Point.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'Point',
+ fullName: 'sketchology.proto.Point'
+ },
+ 1: {
+ name: 'x',
+ fieldType: goog.proto2.Message.FieldType.FLOAT,
+ type: Number
+ },
+ 2: {
+ name: 'y',
+ fieldType: goog.proto2.Message.FieldType.FLOAT,
+ type: Number
+ }
+ };
+ sketchology.proto.Point.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.Point, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.Point.getDescriptor =
+ sketchology.proto.Point.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.ElementBundle.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.ElementBundle.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'ElementBundle',
+ fullName: 'sketchology.proto.ElementBundle'
+ },
+ 1: {
+ name: 'uuid',
+ fieldType: goog.proto2.Message.FieldType.STRING,
+ type: String
+ },
+ 2: {
+ name: 'element',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.Element
+ },
+ 3: {
+ name: 'transform',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.AffineTransform
+ },
+ 4: {
+ name: 'uncompressed_element',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.UncompressedElement
+ }
+ };
+ sketchology.proto.ElementBundle.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.ElementBundle, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.ElementBundle.getDescriptor =
+ sketchology.proto.ElementBundle.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.Path.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.Path.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'Path',
+ fullName: 'sketchology.proto.Path'
+ },
+ 1: {
+ name: 'segment_types',
+ repeated: true,
+ fieldType: goog.proto2.Message.FieldType.ENUM,
+ defaultValue: sketchology.proto.Path.SegmentType.UNKNOWN,
+ type: sketchology.proto.Path.SegmentType
+ },
+ 2: {
+ name: 'segment_counts',
+ repeated: true,
+ fieldType: goog.proto2.Message.FieldType.UINT32,
+ type: Number
+ },
+ 3: {
+ name: 'segment_args',
+ repeated: true,
+ fieldType: goog.proto2.Message.FieldType.DOUBLE,
+ type: Number
+ },
+ 4: {
+ name: 'radius',
+ fieldType: goog.proto2.Message.FieldType.DOUBLE,
+ defaultValue: 1,
+ type: Number
+ },
+ 5: {
+ name: 'rgba',
+ fieldType: goog.proto2.Message.FieldType.UINT32,
+ type: Number
+ },
+ 6: {
+ name: 'end_cap',
+ fieldType: goog.proto2.Message.FieldType.ENUM,
+ defaultValue: sketchology.proto.Path.EndCapType.ROUND,
+ type: sketchology.proto.Path.EndCapType
+ },
+ 7: {
+ name: 'fill_rgba',
+ fieldType: goog.proto2.Message.FieldType.UINT32,
+ type: Number
+ }
+ };
+ sketchology.proto.Path.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.Path, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.Path.getDescriptor =
+ sketchology.proto.Path.prototype.getDescriptor;
diff --git a/chromium/third_party/ink/sketchology/proto/rect_bounds.pb.js b/chromium/third_party/ink/sketchology/proto/rect_bounds.pb.js
new file mode 100644
index 00000000000..ef41a1d1df6
--- /dev/null
+++ b/chromium/third_party/ink/sketchology/proto/rect_bounds.pb.js
@@ -0,0 +1,525 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+// Protocol Buffer 2 Copyright 2008 Google Inc.
+// All other code copyright its respective owners.
+
+/**
+ * @fileoverview Generated Protocol Buffer code for file
+ * third_party/sketchology/proto/rect_bounds.proto.
+ * Generated by //net/proto2/compiler/public:protocol_compiler.
+ * @suppress {messageConventions}
+ */
+
+goog.provide('sketchology.proto.Rect');
+goog.provide('sketchology.proto.RectBounds');
+
+goog.require('goog.proto2.Message');
+
+
+
+/**
+ * Message Rect.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.Rect = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.Rect, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.Rect.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.Rect} The cloned message.
+ * @override
+ */
+sketchology.proto.Rect.prototype.clone;
+
+
+/**
+ * Gets the value of the xlow field.
+ * @return {?number} The value.
+ */
+sketchology.proto.Rect.prototype.getXlow = function() {
+ return /** @type {?number} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the xlow field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.Rect.prototype.getXlowOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the xlow field.
+ * @param {number} value The value.
+ */
+sketchology.proto.Rect.prototype.setXlow = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the xlow field has a value.
+ */
+sketchology.proto.Rect.prototype.hasXlow = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the xlow field.
+ */
+sketchology.proto.Rect.prototype.xlowCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the xlow field.
+ */
+sketchology.proto.Rect.prototype.clearXlow = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the xhigh field.
+ * @return {?number} The value.
+ */
+sketchology.proto.Rect.prototype.getXhigh = function() {
+ return /** @type {?number} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the xhigh field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.Rect.prototype.getXhighOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the xhigh field.
+ * @param {number} value The value.
+ */
+sketchology.proto.Rect.prototype.setXhigh = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the xhigh field has a value.
+ */
+sketchology.proto.Rect.prototype.hasXhigh = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the xhigh field.
+ */
+sketchology.proto.Rect.prototype.xhighCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the xhigh field.
+ */
+sketchology.proto.Rect.prototype.clearXhigh = function() {
+ this.clear$Field(2);
+};
+
+
+/**
+ * Gets the value of the ylow field.
+ * @return {?number} The value.
+ */
+sketchology.proto.Rect.prototype.getYlow = function() {
+ return /** @type {?number} */ (this.get$Value(3));
+};
+
+
+/**
+ * Gets the value of the ylow field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.Rect.prototype.getYlowOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(3));
+};
+
+
+/**
+ * Sets the value of the ylow field.
+ * @param {number} value The value.
+ */
+sketchology.proto.Rect.prototype.setYlow = function(value) {
+ this.set$Value(3, value);
+};
+
+
+/**
+ * @return {boolean} Whether the ylow field has a value.
+ */
+sketchology.proto.Rect.prototype.hasYlow = function() {
+ return this.has$Value(3);
+};
+
+
+/**
+ * @return {number} The number of values in the ylow field.
+ */
+sketchology.proto.Rect.prototype.ylowCount = function() {
+ return this.count$Values(3);
+};
+
+
+/**
+ * Clears the values in the ylow field.
+ */
+sketchology.proto.Rect.prototype.clearYlow = function() {
+ this.clear$Field(3);
+};
+
+
+/**
+ * Gets the value of the yhigh field.
+ * @return {?number} The value.
+ */
+sketchology.proto.Rect.prototype.getYhigh = function() {
+ return /** @type {?number} */ (this.get$Value(4));
+};
+
+
+/**
+ * Gets the value of the yhigh field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.Rect.prototype.getYhighOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(4));
+};
+
+
+/**
+ * Sets the value of the yhigh field.
+ * @param {number} value The value.
+ */
+sketchology.proto.Rect.prototype.setYhigh = function(value) {
+ this.set$Value(4, value);
+};
+
+
+/**
+ * @return {boolean} Whether the yhigh field has a value.
+ */
+sketchology.proto.Rect.prototype.hasYhigh = function() {
+ return this.has$Value(4);
+};
+
+
+/**
+ * @return {number} The number of values in the yhigh field.
+ */
+sketchology.proto.Rect.prototype.yhighCount = function() {
+ return this.count$Values(4);
+};
+
+
+/**
+ * Clears the values in the yhigh field.
+ */
+sketchology.proto.Rect.prototype.clearYhigh = function() {
+ this.clear$Field(4);
+};
+
+
+
+/**
+ * Message RectBounds.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.RectBounds = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.RectBounds, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.RectBounds.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.RectBounds} The cloned message.
+ * @override
+ */
+sketchology.proto.RectBounds.prototype.clone;
+
+
+/**
+ * Gets the value of the mbr field.
+ * @return {?sketchology.proto.Rect} The value.
+ */
+sketchology.proto.RectBounds.prototype.getMbr = function() {
+ return /** @type {?sketchology.proto.Rect} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the mbr field or the default value if not set.
+ * @return {!sketchology.proto.Rect} The value.
+ */
+sketchology.proto.RectBounds.prototype.getMbrOrDefault = function() {
+ return /** @type {!sketchology.proto.Rect} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the mbr field.
+ * @param {!sketchology.proto.Rect} value The value.
+ */
+sketchology.proto.RectBounds.prototype.setMbr = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the mbr field has a value.
+ */
+sketchology.proto.RectBounds.prototype.hasMbr = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the mbr field.
+ */
+sketchology.proto.RectBounds.prototype.mbrCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the mbr field.
+ */
+sketchology.proto.RectBounds.prototype.clearMbr = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the fit_rects field at the index given.
+ * @param {number} index The index to lookup.
+ * @return {?sketchology.proto.Rect} The value.
+ */
+sketchology.proto.RectBounds.prototype.getFitRects = function(index) {
+ return /** @type {?sketchology.proto.Rect} */ (this.get$Value(2, index));
+};
+
+
+/**
+ * Gets the value of the fit_rects field at the index given or the default value if not set.
+ * @param {number} index The index to lookup.
+ * @return {!sketchology.proto.Rect} The value.
+ */
+sketchology.proto.RectBounds.prototype.getFitRectsOrDefault = function(index) {
+ return /** @type {!sketchology.proto.Rect} */ (this.get$ValueOrDefault(2, index));
+};
+
+
+/**
+ * Adds a value to the fit_rects field.
+ * @param {!sketchology.proto.Rect} value The value to add.
+ */
+sketchology.proto.RectBounds.prototype.addFitRects = function(value) {
+ this.add$Value(2, value);
+};
+
+
+/**
+ * Returns the array of values in the fit_rects field.
+ * @return {!Array<!sketchology.proto.Rect>} The values in the field.
+ */
+sketchology.proto.RectBounds.prototype.fitRectsArray = function() {
+ return /** @type {!Array<!sketchology.proto.Rect>} */ (this.array$Values(2));
+};
+
+
+/**
+ * @return {boolean} Whether the fit_rects field has a value.
+ */
+sketchology.proto.RectBounds.prototype.hasFitRects = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the fit_rects field.
+ */
+sketchology.proto.RectBounds.prototype.fitRectsCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the fit_rects field.
+ */
+sketchology.proto.RectBounds.prototype.clearFitRects = function() {
+ this.clear$Field(2);
+};
+
+
+/**
+ * Gets the value of the max_dim field.
+ * @return {?number} The value.
+ */
+sketchology.proto.RectBounds.prototype.getMaxDim = function() {
+ return /** @type {?number} */ (this.get$Value(3));
+};
+
+
+/**
+ * Gets the value of the max_dim field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.RectBounds.prototype.getMaxDimOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(3));
+};
+
+
+/**
+ * Sets the value of the max_dim field.
+ * @param {number} value The value.
+ */
+sketchology.proto.RectBounds.prototype.setMaxDim = function(value) {
+ this.set$Value(3, value);
+};
+
+
+/**
+ * @return {boolean} Whether the max_dim field has a value.
+ */
+sketchology.proto.RectBounds.prototype.hasMaxDim = function() {
+ return this.has$Value(3);
+};
+
+
+/**
+ * @return {number} The number of values in the max_dim field.
+ */
+sketchology.proto.RectBounds.prototype.maxDimCount = function() {
+ return this.count$Values(3);
+};
+
+
+/**
+ * Clears the values in the max_dim field.
+ */
+sketchology.proto.RectBounds.prototype.clearMaxDim = function() {
+ this.clear$Field(3);
+};
+
+
+/** @override */
+sketchology.proto.Rect.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.Rect.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'Rect',
+ fullName: 'sketchology.proto.Rect'
+ },
+ 1: {
+ name: 'xlow',
+ fieldType: goog.proto2.Message.FieldType.FLOAT,
+ type: Number
+ },
+ 2: {
+ name: 'xhigh',
+ fieldType: goog.proto2.Message.FieldType.FLOAT,
+ type: Number
+ },
+ 3: {
+ name: 'ylow',
+ fieldType: goog.proto2.Message.FieldType.FLOAT,
+ type: Number
+ },
+ 4: {
+ name: 'yhigh',
+ fieldType: goog.proto2.Message.FieldType.FLOAT,
+ type: Number
+ }
+ };
+ sketchology.proto.Rect.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.Rect, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.Rect.getDescriptor =
+ sketchology.proto.Rect.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.RectBounds.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.RectBounds.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'RectBounds',
+ fullName: 'sketchology.proto.RectBounds'
+ },
+ 1: {
+ name: 'mbr',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.Rect
+ },
+ 2: {
+ name: 'fit_rects',
+ repeated: true,
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.Rect
+ },
+ 3: {
+ name: 'max_dim',
+ fieldType: goog.proto2.Message.FieldType.FLOAT,
+ type: Number
+ }
+ };
+ sketchology.proto.RectBounds.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.RectBounds, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.RectBounds.getDescriptor =
+ sketchology.proto.RectBounds.prototype.getDescriptor;
diff --git a/chromium/third_party/ink/sketchology/proto/sengine.pb.js b/chromium/third_party/ink/sketchology/proto/sengine.pb.js
new file mode 100644
index 00000000000..56bd87d3335
--- /dev/null
+++ b/chromium/third_party/ink/sketchology/proto/sengine.pb.js
@@ -0,0 +1,8196 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+// Protocol Buffer 2 Copyright 2008 Google Inc.
+// All other code copyright its respective owners.
+
+/**
+ * @fileoverview Generated Protocol Buffer code for file
+ * third_party/sketchology/proto/sengine.proto.
+ * Generated by //net/proto2/compiler/public:protocol_compiler.
+ * @suppress {messageConventions}
+ */
+
+goog.provide('sketchology.proto.Command');
+goog.provide('sketchology.proto.CommandList');
+goog.provide('sketchology.proto.NoArgCommand');
+goog.provide('sketchology.proto.ReplaceElementsCommand');
+goog.provide('sketchology.proto.EvictImageData');
+goog.provide('sketchology.proto.Viewport');
+goog.provide('sketchology.proto.ImageExport');
+goog.provide('sketchology.proto.LinearPathAnimation');
+goog.provide('sketchology.proto.LineSize');
+goog.provide('sketchology.proto.LineSize.SizeType');
+goog.provide('sketchology.proto.PusherToolParams');
+goog.provide('sketchology.proto.ToolParams');
+goog.provide('sketchology.proto.ToolParams.ToolType');
+goog.provide('sketchology.proto.FlagAssignment');
+goog.provide('sketchology.proto.AddElement');
+goog.provide('sketchology.proto.OutOfBoundsColor');
+goog.provide('sketchology.proto.SInputStream');
+goog.provide('sketchology.proto.SInput');
+goog.provide('sketchology.proto.SInput.InputType');
+goog.provide('sketchology.proto.SimulatedInput');
+goog.provide('sketchology.proto.SequencePoint');
+goog.provide('sketchology.proto.SetCallbackFlags');
+goog.provide('sketchology.proto.EngineState');
+goog.provide('sketchology.proto.CameraBoundsConfig');
+goog.provide('sketchology.proto.ImageInfo');
+goog.provide('sketchology.proto.ImageInfo.AssetType');
+goog.provide('sketchology.proto.ImageRect');
+goog.provide('sketchology.proto.GridInfo');
+goog.provide('sketchology.proto.CreateDocument');
+goog.provide('sketchology.proto.AddPath');
+goog.provide('sketchology.proto.PusherPositionUpdate');
+goog.provide('sketchology.proto.ElementQueryData');
+goog.provide('sketchology.proto.ElementQueryItem');
+goog.provide('sketchology.proto.SelectionState');
+goog.provide('sketchology.proto.ToolEvent');
+goog.provide('sketchology.proto.RenderingStrategy');
+goog.provide('sketchology.proto.BrushType');
+goog.provide('sketchology.proto.Flag');
+goog.provide('sketchology.proto.DocumentType');
+goog.provide('sketchology.proto.StorageType');
+
+goog.require('goog.proto2.Message');
+goog.require('sketchology.proto.BackgroundColor');
+goog.require('sketchology.proto.BackgroundImageInfo');
+goog.require('sketchology.proto.Border');
+goog.require('sketchology.proto.CallbackFlags');
+goog.require('sketchology.proto.ElementAnimation');
+goog.require('sketchology.proto.ElementAttributes');
+goog.require('sketchology.proto.ElementBundle');
+goog.require('sketchology.proto.ElementMutation');
+goog.require('sketchology.proto.Path');
+goog.require('sketchology.proto.Point');
+goog.require('sketchology.proto.Rect');
+goog.require('sketchology.proto.Snapshot');
+goog.require('sketchology.proto.SourceDetails');
+
+
+/**
+ * Enumeration RenderingStrategy.
+ * @enum {number}
+ */
+sketchology.proto.RenderingStrategy = {
+ UNKNOWN_RENDERER: 0,
+ BUFFERED_RENDERER: 1,
+ DIRECT_RENDERER: 2
+};
+
+
+/**
+ * Enumeration BrushType.
+ * @enum {number}
+ */
+sketchology.proto.BrushType = {
+ UNKNOWN_BRUSH: 0,
+ CALLIGRAPHY: 1,
+ INKPEN: 2,
+ MARKER: 3,
+ BALLPOINT: 4,
+ PENCIL: 5,
+ ERASER: 6,
+ AIRBRUSH: 7,
+ HIGHLIGHTER: 8,
+ GRADIENT: 9,
+ CHISEL: 10,
+ BALLPOINT_IN_PEN_MODE_ELSE_MARKER: 11
+};
+
+
+/**
+ * Enumeration Flag.
+ * @enum {number}
+ */
+sketchology.proto.Flag = {
+ UNKNOWN: 0,
+ READ_ONLY_MODE: 1,
+ ENABLE_PAN_ZOOM: 2,
+ ENABLE_ROTATION: 3,
+ ENABLE_AUTO_PEN_MODE: 4,
+ ENABLE_PEN_MODE: 5,
+ LOW_MEMORY_MODE: 6,
+ OPAQUE_PREDICTED_SEGMENT: 7
+};
+
+
+/**
+ * Enumeration DocumentType.
+ * @enum {number}
+ */
+sketchology.proto.DocumentType = {
+ UNKNOWN_DOCUMENT_TYPE: 0,
+ SINGLE_USER_DOCUMENT: 1,
+ PASSTHROUGH_DOCUMENT: 2
+};
+
+
+/**
+ * Enumeration StorageType.
+ * @enum {number}
+ */
+sketchology.proto.StorageType = {
+ UNKNOWN_STORAGE_TYPE: 0,
+ IN_MEMORY_STORAGE: 1,
+ SQLITE_STORAGE: 2
+};
+
+
+
+/**
+ * Message Command.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.Command = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.Command, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.Command.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.Command} The cloned message.
+ * @override
+ */
+sketchology.proto.Command.prototype.clone;
+
+
+/**
+ * Gets the value of the set_viewport field.
+ * @return {?sketchology.proto.Viewport} The value.
+ */
+sketchology.proto.Command.prototype.getSetViewport = function() {
+ return /** @type {?sketchology.proto.Viewport} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the set_viewport field or the default value if not set.
+ * @return {!sketchology.proto.Viewport} The value.
+ */
+sketchology.proto.Command.prototype.getSetViewportOrDefault = function() {
+ return /** @type {!sketchology.proto.Viewport} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the set_viewport field.
+ * @param {!sketchology.proto.Viewport} value The value.
+ */
+sketchology.proto.Command.prototype.setSetViewport = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the set_viewport field has a value.
+ */
+sketchology.proto.Command.prototype.hasSetViewport = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the set_viewport field.
+ */
+sketchology.proto.Command.prototype.setViewportCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the set_viewport field.
+ */
+sketchology.proto.Command.prototype.clearSetViewport = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the tool_params field.
+ * @return {?sketchology.proto.ToolParams} The value.
+ */
+sketchology.proto.Command.prototype.getToolParams = function() {
+ return /** @type {?sketchology.proto.ToolParams} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the tool_params field or the default value if not set.
+ * @return {!sketchology.proto.ToolParams} The value.
+ */
+sketchology.proto.Command.prototype.getToolParamsOrDefault = function() {
+ return /** @type {!sketchology.proto.ToolParams} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the tool_params field.
+ * @param {!sketchology.proto.ToolParams} value The value.
+ */
+sketchology.proto.Command.prototype.setToolParams = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the tool_params field has a value.
+ */
+sketchology.proto.Command.prototype.hasToolParams = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the tool_params field.
+ */
+sketchology.proto.Command.prototype.toolParamsCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the tool_params field.
+ */
+sketchology.proto.Command.prototype.clearToolParams = function() {
+ this.clear$Field(2);
+};
+
+
+/**
+ * Gets the value of the add_path field.
+ * @return {?sketchology.proto.AddPath} The value.
+ */
+sketchology.proto.Command.prototype.getAddPath = function() {
+ return /** @type {?sketchology.proto.AddPath} */ (this.get$Value(3));
+};
+
+
+/**
+ * Gets the value of the add_path field or the default value if not set.
+ * @return {!sketchology.proto.AddPath} The value.
+ */
+sketchology.proto.Command.prototype.getAddPathOrDefault = function() {
+ return /** @type {!sketchology.proto.AddPath} */ (this.get$ValueOrDefault(3));
+};
+
+
+/**
+ * Sets the value of the add_path field.
+ * @param {!sketchology.proto.AddPath} value The value.
+ */
+sketchology.proto.Command.prototype.setAddPath = function(value) {
+ this.set$Value(3, value);
+};
+
+
+/**
+ * @return {boolean} Whether the add_path field has a value.
+ */
+sketchology.proto.Command.prototype.hasAddPath = function() {
+ return this.has$Value(3);
+};
+
+
+/**
+ * @return {number} The number of values in the add_path field.
+ */
+sketchology.proto.Command.prototype.addPathCount = function() {
+ return this.count$Values(3);
+};
+
+
+/**
+ * Clears the values in the add_path field.
+ */
+sketchology.proto.Command.prototype.clearAddPath = function() {
+ this.clear$Field(3);
+};
+
+
+/**
+ * Gets the value of the camera_position field.
+ * @return {?sketchology.proto.Rect} The value.
+ */
+sketchology.proto.Command.prototype.getCameraPosition = function() {
+ return /** @type {?sketchology.proto.Rect} */ (this.get$Value(4));
+};
+
+
+/**
+ * Gets the value of the camera_position field or the default value if not set.
+ * @return {!sketchology.proto.Rect} The value.
+ */
+sketchology.proto.Command.prototype.getCameraPositionOrDefault = function() {
+ return /** @type {!sketchology.proto.Rect} */ (this.get$ValueOrDefault(4));
+};
+
+
+/**
+ * Sets the value of the camera_position field.
+ * @param {!sketchology.proto.Rect} value The value.
+ */
+sketchology.proto.Command.prototype.setCameraPosition = function(value) {
+ this.set$Value(4, value);
+};
+
+
+/**
+ * @return {boolean} Whether the camera_position field has a value.
+ */
+sketchology.proto.Command.prototype.hasCameraPosition = function() {
+ return this.has$Value(4);
+};
+
+
+/**
+ * @return {number} The number of values in the camera_position field.
+ */
+sketchology.proto.Command.prototype.cameraPositionCount = function() {
+ return this.count$Values(4);
+};
+
+
+/**
+ * Clears the values in the camera_position field.
+ */
+sketchology.proto.Command.prototype.clearCameraPosition = function() {
+ this.clear$Field(4);
+};
+
+
+/**
+ * Gets the value of the page_bounds field.
+ * @return {?sketchology.proto.Rect} The value.
+ */
+sketchology.proto.Command.prototype.getPageBounds = function() {
+ return /** @type {?sketchology.proto.Rect} */ (this.get$Value(5));
+};
+
+
+/**
+ * Gets the value of the page_bounds field or the default value if not set.
+ * @return {!sketchology.proto.Rect} The value.
+ */
+sketchology.proto.Command.prototype.getPageBoundsOrDefault = function() {
+ return /** @type {!sketchology.proto.Rect} */ (this.get$ValueOrDefault(5));
+};
+
+
+/**
+ * Sets the value of the page_bounds field.
+ * @param {!sketchology.proto.Rect} value The value.
+ */
+sketchology.proto.Command.prototype.setPageBounds = function(value) {
+ this.set$Value(5, value);
+};
+
+
+/**
+ * @return {boolean} Whether the page_bounds field has a value.
+ */
+sketchology.proto.Command.prototype.hasPageBounds = function() {
+ return this.has$Value(5);
+};
+
+
+/**
+ * @return {number} The number of values in the page_bounds field.
+ */
+sketchology.proto.Command.prototype.pageBoundsCount = function() {
+ return this.count$Values(5);
+};
+
+
+/**
+ * Clears the values in the page_bounds field.
+ */
+sketchology.proto.Command.prototype.clearPageBounds = function() {
+ this.clear$Field(5);
+};
+
+
+/**
+ * Gets the value of the image_export field.
+ * @return {?sketchology.proto.ImageExport} The value.
+ */
+sketchology.proto.Command.prototype.getImageExport = function() {
+ return /** @type {?sketchology.proto.ImageExport} */ (this.get$Value(6));
+};
+
+
+/**
+ * Gets the value of the image_export field or the default value if not set.
+ * @return {!sketchology.proto.ImageExport} The value.
+ */
+sketchology.proto.Command.prototype.getImageExportOrDefault = function() {
+ return /** @type {!sketchology.proto.ImageExport} */ (this.get$ValueOrDefault(6));
+};
+
+
+/**
+ * Sets the value of the image_export field.
+ * @param {!sketchology.proto.ImageExport} value The value.
+ */
+sketchology.proto.Command.prototype.setImageExport = function(value) {
+ this.set$Value(6, value);
+};
+
+
+/**
+ * @return {boolean} Whether the image_export field has a value.
+ */
+sketchology.proto.Command.prototype.hasImageExport = function() {
+ return this.has$Value(6);
+};
+
+
+/**
+ * @return {number} The number of values in the image_export field.
+ */
+sketchology.proto.Command.prototype.imageExportCount = function() {
+ return this.count$Values(6);
+};
+
+
+/**
+ * Clears the values in the image_export field.
+ */
+sketchology.proto.Command.prototype.clearImageExport = function() {
+ this.clear$Field(6);
+};
+
+
+/**
+ * Gets the value of the flag_assignment field.
+ * @return {?sketchology.proto.FlagAssignment} The value.
+ */
+sketchology.proto.Command.prototype.getFlagAssignment = function() {
+ return /** @type {?sketchology.proto.FlagAssignment} */ (this.get$Value(7));
+};
+
+
+/**
+ * Gets the value of the flag_assignment field or the default value if not set.
+ * @return {!sketchology.proto.FlagAssignment} The value.
+ */
+sketchology.proto.Command.prototype.getFlagAssignmentOrDefault = function() {
+ return /** @type {!sketchology.proto.FlagAssignment} */ (this.get$ValueOrDefault(7));
+};
+
+
+/**
+ * Sets the value of the flag_assignment field.
+ * @param {!sketchology.proto.FlagAssignment} value The value.
+ */
+sketchology.proto.Command.prototype.setFlagAssignment = function(value) {
+ this.set$Value(7, value);
+};
+
+
+/**
+ * @return {boolean} Whether the flag_assignment field has a value.
+ */
+sketchology.proto.Command.prototype.hasFlagAssignment = function() {
+ return this.has$Value(7);
+};
+
+
+/**
+ * @return {number} The number of values in the flag_assignment field.
+ */
+sketchology.proto.Command.prototype.flagAssignmentCount = function() {
+ return this.count$Values(7);
+};
+
+
+/**
+ * Clears the values in the flag_assignment field.
+ */
+sketchology.proto.Command.prototype.clearFlagAssignment = function() {
+ this.clear$Field(7);
+};
+
+
+/**
+ * Gets the value of the set_element_transforms field.
+ * @return {?sketchology.proto.ElementMutation} The value.
+ */
+sketchology.proto.Command.prototype.getSetElementTransforms = function() {
+ return /** @type {?sketchology.proto.ElementMutation} */ (this.get$Value(8));
+};
+
+
+/**
+ * Gets the value of the set_element_transforms field or the default value if not set.
+ * @return {!sketchology.proto.ElementMutation} The value.
+ */
+sketchology.proto.Command.prototype.getSetElementTransformsOrDefault = function() {
+ return /** @type {!sketchology.proto.ElementMutation} */ (this.get$ValueOrDefault(8));
+};
+
+
+/**
+ * Sets the value of the set_element_transforms field.
+ * @param {!sketchology.proto.ElementMutation} value The value.
+ */
+sketchology.proto.Command.prototype.setSetElementTransforms = function(value) {
+ this.set$Value(8, value);
+};
+
+
+/**
+ * @return {boolean} Whether the set_element_transforms field has a value.
+ */
+sketchology.proto.Command.prototype.hasSetElementTransforms = function() {
+ return this.has$Value(8);
+};
+
+
+/**
+ * @return {number} The number of values in the set_element_transforms field.
+ */
+sketchology.proto.Command.prototype.setElementTransformsCount = function() {
+ return this.count$Values(8);
+};
+
+
+/**
+ * Clears the values in the set_element_transforms field.
+ */
+sketchology.proto.Command.prototype.clearSetElementTransforms = function() {
+ this.clear$Field(8);
+};
+
+
+/**
+ * Gets the value of the add_element field.
+ * @return {?sketchology.proto.AddElement} The value.
+ */
+sketchology.proto.Command.prototype.getAddElement = function() {
+ return /** @type {?sketchology.proto.AddElement} */ (this.get$Value(9));
+};
+
+
+/**
+ * Gets the value of the add_element field or the default value if not set.
+ * @return {!sketchology.proto.AddElement} The value.
+ */
+sketchology.proto.Command.prototype.getAddElementOrDefault = function() {
+ return /** @type {!sketchology.proto.AddElement} */ (this.get$ValueOrDefault(9));
+};
+
+
+/**
+ * Sets the value of the add_element field.
+ * @param {!sketchology.proto.AddElement} value The value.
+ */
+sketchology.proto.Command.prototype.setAddElement = function(value) {
+ this.set$Value(9, value);
+};
+
+
+/**
+ * @return {boolean} Whether the add_element field has a value.
+ */
+sketchology.proto.Command.prototype.hasAddElement = function() {
+ return this.has$Value(9);
+};
+
+
+/**
+ * @return {number} The number of values in the add_element field.
+ */
+sketchology.proto.Command.prototype.addElementCount = function() {
+ return this.count$Values(9);
+};
+
+
+/**
+ * Clears the values in the add_element field.
+ */
+sketchology.proto.Command.prototype.clearAddElement = function() {
+ this.clear$Field(9);
+};
+
+
+/**
+ * Gets the value of the background_image field.
+ * @return {?sketchology.proto.BackgroundImageInfo} The value.
+ */
+sketchology.proto.Command.prototype.getBackgroundImage = function() {
+ return /** @type {?sketchology.proto.BackgroundImageInfo} */ (this.get$Value(10));
+};
+
+
+/**
+ * Gets the value of the background_image field or the default value if not set.
+ * @return {!sketchology.proto.BackgroundImageInfo} The value.
+ */
+sketchology.proto.Command.prototype.getBackgroundImageOrDefault = function() {
+ return /** @type {!sketchology.proto.BackgroundImageInfo} */ (this.get$ValueOrDefault(10));
+};
+
+
+/**
+ * Sets the value of the background_image field.
+ * @param {!sketchology.proto.BackgroundImageInfo} value The value.
+ */
+sketchology.proto.Command.prototype.setBackgroundImage = function(value) {
+ this.set$Value(10, value);
+};
+
+
+/**
+ * @return {boolean} Whether the background_image field has a value.
+ */
+sketchology.proto.Command.prototype.hasBackgroundImage = function() {
+ return this.has$Value(10);
+};
+
+
+/**
+ * @return {number} The number of values in the background_image field.
+ */
+sketchology.proto.Command.prototype.backgroundImageCount = function() {
+ return this.count$Values(10);
+};
+
+
+/**
+ * Clears the values in the background_image field.
+ */
+sketchology.proto.Command.prototype.clearBackgroundImage = function() {
+ this.clear$Field(10);
+};
+
+
+/**
+ * Gets the value of the background_color field.
+ * @return {?sketchology.proto.BackgroundColor} The value.
+ */
+sketchology.proto.Command.prototype.getBackgroundColor = function() {
+ return /** @type {?sketchology.proto.BackgroundColor} */ (this.get$Value(11));
+};
+
+
+/**
+ * Gets the value of the background_color field or the default value if not set.
+ * @return {!sketchology.proto.BackgroundColor} The value.
+ */
+sketchology.proto.Command.prototype.getBackgroundColorOrDefault = function() {
+ return /** @type {!sketchology.proto.BackgroundColor} */ (this.get$ValueOrDefault(11));
+};
+
+
+/**
+ * Sets the value of the background_color field.
+ * @param {!sketchology.proto.BackgroundColor} value The value.
+ */
+sketchology.proto.Command.prototype.setBackgroundColor = function(value) {
+ this.set$Value(11, value);
+};
+
+
+/**
+ * @return {boolean} Whether the background_color field has a value.
+ */
+sketchology.proto.Command.prototype.hasBackgroundColor = function() {
+ return this.has$Value(11);
+};
+
+
+/**
+ * @return {number} The number of values in the background_color field.
+ */
+sketchology.proto.Command.prototype.backgroundColorCount = function() {
+ return this.count$Values(11);
+};
+
+
+/**
+ * Clears the values in the background_color field.
+ */
+sketchology.proto.Command.prototype.clearBackgroundColor = function() {
+ this.clear$Field(11);
+};
+
+
+/**
+ * Gets the value of the set_out_of_bounds_color field.
+ * @return {?sketchology.proto.OutOfBoundsColor} The value.
+ */
+sketchology.proto.Command.prototype.getSetOutOfBoundsColor = function() {
+ return /** @type {?sketchology.proto.OutOfBoundsColor} */ (this.get$Value(12));
+};
+
+
+/**
+ * Gets the value of the set_out_of_bounds_color field or the default value if not set.
+ * @return {!sketchology.proto.OutOfBoundsColor} The value.
+ */
+sketchology.proto.Command.prototype.getSetOutOfBoundsColorOrDefault = function() {
+ return /** @type {!sketchology.proto.OutOfBoundsColor} */ (this.get$ValueOrDefault(12));
+};
+
+
+/**
+ * Sets the value of the set_out_of_bounds_color field.
+ * @param {!sketchology.proto.OutOfBoundsColor} value The value.
+ */
+sketchology.proto.Command.prototype.setSetOutOfBoundsColor = function(value) {
+ this.set$Value(12, value);
+};
+
+
+/**
+ * @return {boolean} Whether the set_out_of_bounds_color field has a value.
+ */
+sketchology.proto.Command.prototype.hasSetOutOfBoundsColor = function() {
+ return this.has$Value(12);
+};
+
+
+/**
+ * @return {number} The number of values in the set_out_of_bounds_color field.
+ */
+sketchology.proto.Command.prototype.setOutOfBoundsColorCount = function() {
+ return this.count$Values(12);
+};
+
+
+/**
+ * Clears the values in the set_out_of_bounds_color field.
+ */
+sketchology.proto.Command.prototype.clearSetOutOfBoundsColor = function() {
+ this.clear$Field(12);
+};
+
+
+/**
+ * Gets the value of the set_page_border field.
+ * @return {?sketchology.proto.Border} The value.
+ */
+sketchology.proto.Command.prototype.getSetPageBorder = function() {
+ return /** @type {?sketchology.proto.Border} */ (this.get$Value(13));
+};
+
+
+/**
+ * Gets the value of the set_page_border field or the default value if not set.
+ * @return {!sketchology.proto.Border} The value.
+ */
+sketchology.proto.Command.prototype.getSetPageBorderOrDefault = function() {
+ return /** @type {!sketchology.proto.Border} */ (this.get$ValueOrDefault(13));
+};
+
+
+/**
+ * Sets the value of the set_page_border field.
+ * @param {!sketchology.proto.Border} value The value.
+ */
+sketchology.proto.Command.prototype.setSetPageBorder = function(value) {
+ this.set$Value(13, value);
+};
+
+
+/**
+ * @return {boolean} Whether the set_page_border field has a value.
+ */
+sketchology.proto.Command.prototype.hasSetPageBorder = function() {
+ return this.has$Value(13);
+};
+
+
+/**
+ * @return {number} The number of values in the set_page_border field.
+ */
+sketchology.proto.Command.prototype.setPageBorderCount = function() {
+ return this.count$Values(13);
+};
+
+
+/**
+ * Clears the values in the set_page_border field.
+ */
+sketchology.proto.Command.prototype.clearSetPageBorder = function() {
+ this.clear$Field(13);
+};
+
+
+/**
+ * Gets the value of the send_input_stream field.
+ * @return {?sketchology.proto.SInputStream} The value.
+ */
+sketchology.proto.Command.prototype.getSendInputStream = function() {
+ return /** @type {?sketchology.proto.SInputStream} */ (this.get$Value(14));
+};
+
+
+/**
+ * Gets the value of the send_input_stream field or the default value if not set.
+ * @return {!sketchology.proto.SInputStream} The value.
+ */
+sketchology.proto.Command.prototype.getSendInputStreamOrDefault = function() {
+ return /** @type {!sketchology.proto.SInputStream} */ (this.get$ValueOrDefault(14));
+};
+
+
+/**
+ * Sets the value of the send_input_stream field.
+ * @param {!sketchology.proto.SInputStream} value The value.
+ */
+sketchology.proto.Command.prototype.setSendInputStream = function(value) {
+ this.set$Value(14, value);
+};
+
+
+/**
+ * @return {boolean} Whether the send_input_stream field has a value.
+ */
+sketchology.proto.Command.prototype.hasSendInputStream = function() {
+ return this.has$Value(14);
+};
+
+
+/**
+ * @return {number} The number of values in the send_input_stream field.
+ */
+sketchology.proto.Command.prototype.sendInputStreamCount = function() {
+ return this.count$Values(14);
+};
+
+
+/**
+ * Clears the values in the send_input_stream field.
+ */
+sketchology.proto.Command.prototype.clearSendInputStream = function() {
+ this.clear$Field(14);
+};
+
+
+/**
+ * Gets the value of the sequence_point field.
+ * @return {?sketchology.proto.SequencePoint} The value.
+ */
+sketchology.proto.Command.prototype.getSequencePoint = function() {
+ return /** @type {?sketchology.proto.SequencePoint} */ (this.get$Value(15));
+};
+
+
+/**
+ * Gets the value of the sequence_point field or the default value if not set.
+ * @return {!sketchology.proto.SequencePoint} The value.
+ */
+sketchology.proto.Command.prototype.getSequencePointOrDefault = function() {
+ return /** @type {!sketchology.proto.SequencePoint} */ (this.get$ValueOrDefault(15));
+};
+
+
+/**
+ * Sets the value of the sequence_point field.
+ * @param {!sketchology.proto.SequencePoint} value The value.
+ */
+sketchology.proto.Command.prototype.setSequencePoint = function(value) {
+ this.set$Value(15, value);
+};
+
+
+/**
+ * @return {boolean} Whether the sequence_point field has a value.
+ */
+sketchology.proto.Command.prototype.hasSequencePoint = function() {
+ return this.has$Value(15);
+};
+
+
+/**
+ * @return {number} The number of values in the sequence_point field.
+ */
+sketchology.proto.Command.prototype.sequencePointCount = function() {
+ return this.count$Values(15);
+};
+
+
+/**
+ * Clears the values in the sequence_point field.
+ */
+sketchology.proto.Command.prototype.clearSequencePoint = function() {
+ this.clear$Field(15);
+};
+
+
+/**
+ * Gets the value of the set_callback_flags field.
+ * @return {?sketchology.proto.SetCallbackFlags} The value.
+ */
+sketchology.proto.Command.prototype.getSetCallbackFlags = function() {
+ return /** @type {?sketchology.proto.SetCallbackFlags} */ (this.get$Value(16));
+};
+
+
+/**
+ * Gets the value of the set_callback_flags field or the default value if not set.
+ * @return {!sketchology.proto.SetCallbackFlags} The value.
+ */
+sketchology.proto.Command.prototype.getSetCallbackFlagsOrDefault = function() {
+ return /** @type {!sketchology.proto.SetCallbackFlags} */ (this.get$ValueOrDefault(16));
+};
+
+
+/**
+ * Sets the value of the set_callback_flags field.
+ * @param {!sketchology.proto.SetCallbackFlags} value The value.
+ */
+sketchology.proto.Command.prototype.setSetCallbackFlags = function(value) {
+ this.set$Value(16, value);
+};
+
+
+/**
+ * @return {boolean} Whether the set_callback_flags field has a value.
+ */
+sketchology.proto.Command.prototype.hasSetCallbackFlags = function() {
+ return this.has$Value(16);
+};
+
+
+/**
+ * @return {number} The number of values in the set_callback_flags field.
+ */
+sketchology.proto.Command.prototype.setCallbackFlagsCount = function() {
+ return this.count$Values(16);
+};
+
+
+/**
+ * Clears the values in the set_callback_flags field.
+ */
+sketchology.proto.Command.prototype.clearSetCallbackFlags = function() {
+ this.clear$Field(16);
+};
+
+
+/**
+ * Gets the value of the set_camera_bounds_config field.
+ * @return {?sketchology.proto.CameraBoundsConfig} The value.
+ */
+sketchology.proto.Command.prototype.getSetCameraBoundsConfig = function() {
+ return /** @type {?sketchology.proto.CameraBoundsConfig} */ (this.get$Value(17));
+};
+
+
+/**
+ * Gets the value of the set_camera_bounds_config field or the default value if not set.
+ * @return {!sketchology.proto.CameraBoundsConfig} The value.
+ */
+sketchology.proto.Command.prototype.getSetCameraBoundsConfigOrDefault = function() {
+ return /** @type {!sketchology.proto.CameraBoundsConfig} */ (this.get$ValueOrDefault(17));
+};
+
+
+/**
+ * Sets the value of the set_camera_bounds_config field.
+ * @param {!sketchology.proto.CameraBoundsConfig} value The value.
+ */
+sketchology.proto.Command.prototype.setSetCameraBoundsConfig = function(value) {
+ this.set$Value(17, value);
+};
+
+
+/**
+ * @return {boolean} Whether the set_camera_bounds_config field has a value.
+ */
+sketchology.proto.Command.prototype.hasSetCameraBoundsConfig = function() {
+ return this.has$Value(17);
+};
+
+
+/**
+ * @return {number} The number of values in the set_camera_bounds_config field.
+ */
+sketchology.proto.Command.prototype.setCameraBoundsConfigCount = function() {
+ return this.count$Values(17);
+};
+
+
+/**
+ * Clears the values in the set_camera_bounds_config field.
+ */
+sketchology.proto.Command.prototype.clearSetCameraBoundsConfig = function() {
+ this.clear$Field(17);
+};
+
+
+/**
+ * Gets the value of the deselect_all field.
+ * @return {?sketchology.proto.NoArgCommand} The value.
+ */
+sketchology.proto.Command.prototype.getDeselectAll = function() {
+ return /** @type {?sketchology.proto.NoArgCommand} */ (this.get$Value(18));
+};
+
+
+/**
+ * Gets the value of the deselect_all field or the default value if not set.
+ * @return {!sketchology.proto.NoArgCommand} The value.
+ */
+sketchology.proto.Command.prototype.getDeselectAllOrDefault = function() {
+ return /** @type {!sketchology.proto.NoArgCommand} */ (this.get$ValueOrDefault(18));
+};
+
+
+/**
+ * Sets the value of the deselect_all field.
+ * @param {!sketchology.proto.NoArgCommand} value The value.
+ */
+sketchology.proto.Command.prototype.setDeselectAll = function(value) {
+ this.set$Value(18, value);
+};
+
+
+/**
+ * @return {boolean} Whether the deselect_all field has a value.
+ */
+sketchology.proto.Command.prototype.hasDeselectAll = function() {
+ return this.has$Value(18);
+};
+
+
+/**
+ * @return {number} The number of values in the deselect_all field.
+ */
+sketchology.proto.Command.prototype.deselectAllCount = function() {
+ return this.count$Values(18);
+};
+
+
+/**
+ * Clears the values in the deselect_all field.
+ */
+sketchology.proto.Command.prototype.clearDeselectAll = function() {
+ this.clear$Field(18);
+};
+
+
+/**
+ * Gets the value of the add_image_rect field.
+ * @return {?sketchology.proto.ImageRect} The value.
+ */
+sketchology.proto.Command.prototype.getAddImageRect = function() {
+ return /** @type {?sketchology.proto.ImageRect} */ (this.get$Value(19));
+};
+
+
+/**
+ * Gets the value of the add_image_rect field or the default value if not set.
+ * @return {!sketchology.proto.ImageRect} The value.
+ */
+sketchology.proto.Command.prototype.getAddImageRectOrDefault = function() {
+ return /** @type {!sketchology.proto.ImageRect} */ (this.get$ValueOrDefault(19));
+};
+
+
+/**
+ * Sets the value of the add_image_rect field.
+ * @param {!sketchology.proto.ImageRect} value The value.
+ */
+sketchology.proto.Command.prototype.setAddImageRect = function(value) {
+ this.set$Value(19, value);
+};
+
+
+/**
+ * @return {boolean} Whether the add_image_rect field has a value.
+ */
+sketchology.proto.Command.prototype.hasAddImageRect = function() {
+ return this.has$Value(19);
+};
+
+
+/**
+ * @return {number} The number of values in the add_image_rect field.
+ */
+sketchology.proto.Command.prototype.addImageRectCount = function() {
+ return this.count$Values(19);
+};
+
+
+/**
+ * Clears the values in the add_image_rect field.
+ */
+sketchology.proto.Command.prototype.clearAddImageRect = function() {
+ this.clear$Field(19);
+};
+
+
+/**
+ * Gets the value of the clear field.
+ * @return {?sketchology.proto.NoArgCommand} The value.
+ */
+sketchology.proto.Command.prototype.getClear = function() {
+ return /** @type {?sketchology.proto.NoArgCommand} */ (this.get$Value(21));
+};
+
+
+/**
+ * Gets the value of the clear field or the default value if not set.
+ * @return {!sketchology.proto.NoArgCommand} The value.
+ */
+sketchology.proto.Command.prototype.getClearOrDefault = function() {
+ return /** @type {!sketchology.proto.NoArgCommand} */ (this.get$ValueOrDefault(21));
+};
+
+
+/**
+ * Sets the value of the clear field.
+ * @param {!sketchology.proto.NoArgCommand} value The value.
+ */
+sketchology.proto.Command.prototype.setClear = function(value) {
+ this.set$Value(21, value);
+};
+
+
+/**
+ * @return {boolean} Whether the clear field has a value.
+ */
+sketchology.proto.Command.prototype.hasClear = function() {
+ return this.has$Value(21);
+};
+
+
+/**
+ * @return {number} The number of values in the clear field.
+ */
+sketchology.proto.Command.prototype.clearCount = function() {
+ return this.count$Values(21);
+};
+
+
+/**
+ * Clears the values in the clear field.
+ */
+sketchology.proto.Command.prototype.clearClear = function() {
+ this.clear$Field(21);
+};
+
+
+/**
+ * Gets the value of the remove_all_elements field.
+ * @return {?sketchology.proto.NoArgCommand} The value.
+ */
+sketchology.proto.Command.prototype.getRemoveAllElements = function() {
+ return /** @type {?sketchology.proto.NoArgCommand} */ (this.get$Value(22));
+};
+
+
+/**
+ * Gets the value of the remove_all_elements field or the default value if not set.
+ * @return {!sketchology.proto.NoArgCommand} The value.
+ */
+sketchology.proto.Command.prototype.getRemoveAllElementsOrDefault = function() {
+ return /** @type {!sketchology.proto.NoArgCommand} */ (this.get$ValueOrDefault(22));
+};
+
+
+/**
+ * Sets the value of the remove_all_elements field.
+ * @param {!sketchology.proto.NoArgCommand} value The value.
+ */
+sketchology.proto.Command.prototype.setRemoveAllElements = function(value) {
+ this.set$Value(22, value);
+};
+
+
+/**
+ * @return {boolean} Whether the remove_all_elements field has a value.
+ */
+sketchology.proto.Command.prototype.hasRemoveAllElements = function() {
+ return this.has$Value(22);
+};
+
+
+/**
+ * @return {number} The number of values in the remove_all_elements field.
+ */
+sketchology.proto.Command.prototype.removeAllElementsCount = function() {
+ return this.count$Values(22);
+};
+
+
+/**
+ * Clears the values in the remove_all_elements field.
+ */
+sketchology.proto.Command.prototype.clearRemoveAllElements = function() {
+ this.clear$Field(22);
+};
+
+
+/**
+ * Gets the value of the undo field.
+ * @return {?sketchology.proto.NoArgCommand} The value.
+ */
+sketchology.proto.Command.prototype.getUndo = function() {
+ return /** @type {?sketchology.proto.NoArgCommand} */ (this.get$Value(23));
+};
+
+
+/**
+ * Gets the value of the undo field or the default value if not set.
+ * @return {!sketchology.proto.NoArgCommand} The value.
+ */
+sketchology.proto.Command.prototype.getUndoOrDefault = function() {
+ return /** @type {!sketchology.proto.NoArgCommand} */ (this.get$ValueOrDefault(23));
+};
+
+
+/**
+ * Sets the value of the undo field.
+ * @param {!sketchology.proto.NoArgCommand} value The value.
+ */
+sketchology.proto.Command.prototype.setUndo = function(value) {
+ this.set$Value(23, value);
+};
+
+
+/**
+ * @return {boolean} Whether the undo field has a value.
+ */
+sketchology.proto.Command.prototype.hasUndo = function() {
+ return this.has$Value(23);
+};
+
+
+/**
+ * @return {number} The number of values in the undo field.
+ */
+sketchology.proto.Command.prototype.undoCount = function() {
+ return this.count$Values(23);
+};
+
+
+/**
+ * Clears the values in the undo field.
+ */
+sketchology.proto.Command.prototype.clearUndo = function() {
+ this.clear$Field(23);
+};
+
+
+/**
+ * Gets the value of the redo field.
+ * @return {?sketchology.proto.NoArgCommand} The value.
+ */
+sketchology.proto.Command.prototype.getRedo = function() {
+ return /** @type {?sketchology.proto.NoArgCommand} */ (this.get$Value(24));
+};
+
+
+/**
+ * Gets the value of the redo field or the default value if not set.
+ * @return {!sketchology.proto.NoArgCommand} The value.
+ */
+sketchology.proto.Command.prototype.getRedoOrDefault = function() {
+ return /** @type {!sketchology.proto.NoArgCommand} */ (this.get$ValueOrDefault(24));
+};
+
+
+/**
+ * Sets the value of the redo field.
+ * @param {!sketchology.proto.NoArgCommand} value The value.
+ */
+sketchology.proto.Command.prototype.setRedo = function(value) {
+ this.set$Value(24, value);
+};
+
+
+/**
+ * @return {boolean} Whether the redo field has a value.
+ */
+sketchology.proto.Command.prototype.hasRedo = function() {
+ return this.has$Value(24);
+};
+
+
+/**
+ * @return {number} The number of values in the redo field.
+ */
+sketchology.proto.Command.prototype.redoCount = function() {
+ return this.count$Values(24);
+};
+
+
+/**
+ * Clears the values in the redo field.
+ */
+sketchology.proto.Command.prototype.clearRedo = function() {
+ this.clear$Field(24);
+};
+
+
+/**
+ * Gets the value of the evict_image_data field.
+ * @return {?sketchology.proto.EvictImageData} The value.
+ */
+sketchology.proto.Command.prototype.getEvictImageData = function() {
+ return /** @type {?sketchology.proto.EvictImageData} */ (this.get$Value(25));
+};
+
+
+/**
+ * Gets the value of the evict_image_data field or the default value if not set.
+ * @return {!sketchology.proto.EvictImageData} The value.
+ */
+sketchology.proto.Command.prototype.getEvictImageDataOrDefault = function() {
+ return /** @type {!sketchology.proto.EvictImageData} */ (this.get$ValueOrDefault(25));
+};
+
+
+/**
+ * Sets the value of the evict_image_data field.
+ * @param {!sketchology.proto.EvictImageData} value The value.
+ */
+sketchology.proto.Command.prototype.setEvictImageData = function(value) {
+ this.set$Value(25, value);
+};
+
+
+/**
+ * @return {boolean} Whether the evict_image_data field has a value.
+ */
+sketchology.proto.Command.prototype.hasEvictImageData = function() {
+ return this.has$Value(25);
+};
+
+
+/**
+ * @return {number} The number of values in the evict_image_data field.
+ */
+sketchology.proto.Command.prototype.evictImageDataCount = function() {
+ return this.count$Values(25);
+};
+
+
+/**
+ * Clears the values in the evict_image_data field.
+ */
+sketchology.proto.Command.prototype.clearEvictImageData = function() {
+ this.clear$Field(25);
+};
+
+
+/**
+ * Gets the value of the replace_elements field.
+ * @return {?sketchology.proto.ReplaceElementsCommand} The value.
+ */
+sketchology.proto.Command.prototype.getReplaceElements = function() {
+ return /** @type {?sketchology.proto.ReplaceElementsCommand} */ (this.get$Value(26));
+};
+
+
+/**
+ * Gets the value of the replace_elements field or the default value if not set.
+ * @return {!sketchology.proto.ReplaceElementsCommand} The value.
+ */
+sketchology.proto.Command.prototype.getReplaceElementsOrDefault = function() {
+ return /** @type {!sketchology.proto.ReplaceElementsCommand} */ (this.get$ValueOrDefault(26));
+};
+
+
+/**
+ * Sets the value of the replace_elements field.
+ * @param {!sketchology.proto.ReplaceElementsCommand} value The value.
+ */
+sketchology.proto.Command.prototype.setReplaceElements = function(value) {
+ this.set$Value(26, value);
+};
+
+
+/**
+ * @return {boolean} Whether the replace_elements field has a value.
+ */
+sketchology.proto.Command.prototype.hasReplaceElements = function() {
+ return this.has$Value(26);
+};
+
+
+/**
+ * @return {number} The number of values in the replace_elements field.
+ */
+sketchology.proto.Command.prototype.replaceElementsCount = function() {
+ return this.count$Values(26);
+};
+
+
+/**
+ * Clears the values in the replace_elements field.
+ */
+sketchology.proto.Command.prototype.clearReplaceElements = function() {
+ this.clear$Field(26);
+};
+
+
+/**
+ * Gets the value of the commit_crop field.
+ * @return {?sketchology.proto.NoArgCommand} The value.
+ */
+sketchology.proto.Command.prototype.getCommitCrop = function() {
+ return /** @type {?sketchology.proto.NoArgCommand} */ (this.get$Value(27));
+};
+
+
+/**
+ * Gets the value of the commit_crop field or the default value if not set.
+ * @return {!sketchology.proto.NoArgCommand} The value.
+ */
+sketchology.proto.Command.prototype.getCommitCropOrDefault = function() {
+ return /** @type {!sketchology.proto.NoArgCommand} */ (this.get$ValueOrDefault(27));
+};
+
+
+/**
+ * Sets the value of the commit_crop field.
+ * @param {!sketchology.proto.NoArgCommand} value The value.
+ */
+sketchology.proto.Command.prototype.setCommitCrop = function(value) {
+ this.set$Value(27, value);
+};
+
+
+/**
+ * @return {boolean} Whether the commit_crop field has a value.
+ */
+sketchology.proto.Command.prototype.hasCommitCrop = function() {
+ return this.has$Value(27);
+};
+
+
+/**
+ * @return {number} The number of values in the commit_crop field.
+ */
+sketchology.proto.Command.prototype.commitCropCount = function() {
+ return this.count$Values(27);
+};
+
+
+/**
+ * Clears the values in the commit_crop field.
+ */
+sketchology.proto.Command.prototype.clearCommitCrop = function() {
+ this.clear$Field(27);
+};
+
+
+/**
+ * Gets the value of the element_animation field.
+ * @return {?sketchology.proto.ElementAnimation} The value.
+ */
+sketchology.proto.Command.prototype.getElementAnimation = function() {
+ return /** @type {?sketchology.proto.ElementAnimation} */ (this.get$Value(28));
+};
+
+
+/**
+ * Gets the value of the element_animation field or the default value if not set.
+ * @return {!sketchology.proto.ElementAnimation} The value.
+ */
+sketchology.proto.Command.prototype.getElementAnimationOrDefault = function() {
+ return /** @type {!sketchology.proto.ElementAnimation} */ (this.get$ValueOrDefault(28));
+};
+
+
+/**
+ * Sets the value of the element_animation field.
+ * @param {!sketchology.proto.ElementAnimation} value The value.
+ */
+sketchology.proto.Command.prototype.setElementAnimation = function(value) {
+ this.set$Value(28, value);
+};
+
+
+/**
+ * @return {boolean} Whether the element_animation field has a value.
+ */
+sketchology.proto.Command.prototype.hasElementAnimation = function() {
+ return this.has$Value(28);
+};
+
+
+/**
+ * @return {number} The number of values in the element_animation field.
+ */
+sketchology.proto.Command.prototype.elementAnimationCount = function() {
+ return this.count$Values(28);
+};
+
+
+/**
+ * Clears the values in the element_animation field.
+ */
+sketchology.proto.Command.prototype.clearElementAnimation = function() {
+ this.clear$Field(28);
+};
+
+
+/**
+ * Gets the value of the set_grid field.
+ * @return {?sketchology.proto.GridInfo} The value.
+ */
+sketchology.proto.Command.prototype.getSetGrid = function() {
+ return /** @type {?sketchology.proto.GridInfo} */ (this.get$Value(29));
+};
+
+
+/**
+ * Gets the value of the set_grid field or the default value if not set.
+ * @return {!sketchology.proto.GridInfo} The value.
+ */
+sketchology.proto.Command.prototype.getSetGridOrDefault = function() {
+ return /** @type {!sketchology.proto.GridInfo} */ (this.get$ValueOrDefault(29));
+};
+
+
+/**
+ * Sets the value of the set_grid field.
+ * @param {!sketchology.proto.GridInfo} value The value.
+ */
+sketchology.proto.Command.prototype.setSetGrid = function(value) {
+ this.set$Value(29, value);
+};
+
+
+/**
+ * @return {boolean} Whether the set_grid field has a value.
+ */
+sketchology.proto.Command.prototype.hasSetGrid = function() {
+ return this.has$Value(29);
+};
+
+
+/**
+ * @return {number} The number of values in the set_grid field.
+ */
+sketchology.proto.Command.prototype.setGridCount = function() {
+ return this.count$Values(29);
+};
+
+
+/**
+ * Clears the values in the set_grid field.
+ */
+sketchology.proto.Command.prototype.clearSetGrid = function() {
+ this.clear$Field(29);
+};
+
+
+/**
+ * Gets the value of the clear_grid field.
+ * @return {?sketchology.proto.NoArgCommand} The value.
+ */
+sketchology.proto.Command.prototype.getClearGrid = function() {
+ return /** @type {?sketchology.proto.NoArgCommand} */ (this.get$Value(30));
+};
+
+
+/**
+ * Gets the value of the clear_grid field or the default value if not set.
+ * @return {!sketchology.proto.NoArgCommand} The value.
+ */
+sketchology.proto.Command.prototype.getClearGridOrDefault = function() {
+ return /** @type {!sketchology.proto.NoArgCommand} */ (this.get$ValueOrDefault(30));
+};
+
+
+/**
+ * Sets the value of the clear_grid field.
+ * @param {!sketchology.proto.NoArgCommand} value The value.
+ */
+sketchology.proto.Command.prototype.setClearGrid = function(value) {
+ this.set$Value(30, value);
+};
+
+
+/**
+ * @return {boolean} Whether the clear_grid field has a value.
+ */
+sketchology.proto.Command.prototype.hasClearGrid = function() {
+ return this.has$Value(30);
+};
+
+
+/**
+ * @return {number} The number of values in the clear_grid field.
+ */
+sketchology.proto.Command.prototype.clearGridCount = function() {
+ return this.count$Values(30);
+};
+
+
+/**
+ * Clears the values in the clear_grid field.
+ */
+sketchology.proto.Command.prototype.clearClearGrid = function() {
+ this.clear$Field(30);
+};
+
+
+
+/**
+ * Message CommandList.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.CommandList = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.CommandList, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.CommandList.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.CommandList} The cloned message.
+ * @override
+ */
+sketchology.proto.CommandList.prototype.clone;
+
+
+/**
+ * Gets the value of the commands field at the index given.
+ * @param {number} index The index to lookup.
+ * @return {?sketchology.proto.Command} The value.
+ */
+sketchology.proto.CommandList.prototype.getCommands = function(index) {
+ return /** @type {?sketchology.proto.Command} */ (this.get$Value(1, index));
+};
+
+
+/**
+ * Gets the value of the commands field at the index given or the default value if not set.
+ * @param {number} index The index to lookup.
+ * @return {!sketchology.proto.Command} The value.
+ */
+sketchology.proto.CommandList.prototype.getCommandsOrDefault = function(index) {
+ return /** @type {!sketchology.proto.Command} */ (this.get$ValueOrDefault(1, index));
+};
+
+
+/**
+ * Adds a value to the commands field.
+ * @param {!sketchology.proto.Command} value The value to add.
+ */
+sketchology.proto.CommandList.prototype.addCommands = function(value) {
+ this.add$Value(1, value);
+};
+
+
+/**
+ * Returns the array of values in the commands field.
+ * @return {!Array<!sketchology.proto.Command>} The values in the field.
+ */
+sketchology.proto.CommandList.prototype.commandsArray = function() {
+ return /** @type {!Array<!sketchology.proto.Command>} */ (this.array$Values(1));
+};
+
+
+/**
+ * @return {boolean} Whether the commands field has a value.
+ */
+sketchology.proto.CommandList.prototype.hasCommands = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the commands field.
+ */
+sketchology.proto.CommandList.prototype.commandsCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the commands field.
+ */
+sketchology.proto.CommandList.prototype.clearCommands = function() {
+ this.clear$Field(1);
+};
+
+
+
+/**
+ * Message NoArgCommand.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.NoArgCommand = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.NoArgCommand, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.NoArgCommand.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.NoArgCommand} The cloned message.
+ * @override
+ */
+sketchology.proto.NoArgCommand.prototype.clone;
+
+
+
+/**
+ * Message ReplaceElementsCommand.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.ReplaceElementsCommand = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.ReplaceElementsCommand, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.ReplaceElementsCommand.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.ReplaceElementsCommand} The cloned message.
+ * @override
+ */
+sketchology.proto.ReplaceElementsCommand.prototype.clone;
+
+
+/**
+ * Gets the value of the uuids_to_remove field at the index given.
+ * @param {number} index The index to lookup.
+ * @return {?string} The value.
+ */
+sketchology.proto.ReplaceElementsCommand.prototype.getUuidsToRemove = function(index) {
+ return /** @type {?string} */ (this.get$Value(1, index));
+};
+
+
+/**
+ * Gets the value of the uuids_to_remove field at the index given or the default value if not set.
+ * @param {number} index The index to lookup.
+ * @return {string} The value.
+ */
+sketchology.proto.ReplaceElementsCommand.prototype.getUuidsToRemoveOrDefault = function(index) {
+ return /** @type {string} */ (this.get$ValueOrDefault(1, index));
+};
+
+
+/**
+ * Adds a value to the uuids_to_remove field.
+ * @param {string} value The value to add.
+ */
+sketchology.proto.ReplaceElementsCommand.prototype.addUuidsToRemove = function(value) {
+ this.add$Value(1, value);
+};
+
+
+/**
+ * Returns the array of values in the uuids_to_remove field.
+ * @return {!Array<string>} The values in the field.
+ */
+sketchology.proto.ReplaceElementsCommand.prototype.uuidsToRemoveArray = function() {
+ return /** @type {!Array<string>} */ (this.array$Values(1));
+};
+
+
+/**
+ * @return {boolean} Whether the uuids_to_remove field has a value.
+ */
+sketchology.proto.ReplaceElementsCommand.prototype.hasUuidsToRemove = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the uuids_to_remove field.
+ */
+sketchology.proto.ReplaceElementsCommand.prototype.uuidsToRemoveCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the uuids_to_remove field.
+ */
+sketchology.proto.ReplaceElementsCommand.prototype.clearUuidsToRemove = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the paths_to_add field at the index given.
+ * @param {number} index The index to lookup.
+ * @return {?sketchology.proto.Path} The value.
+ */
+sketchology.proto.ReplaceElementsCommand.prototype.getPathsToAdd = function(index) {
+ return /** @type {?sketchology.proto.Path} */ (this.get$Value(2, index));
+};
+
+
+/**
+ * Gets the value of the paths_to_add field at the index given or the default value if not set.
+ * @param {number} index The index to lookup.
+ * @return {!sketchology.proto.Path} The value.
+ */
+sketchology.proto.ReplaceElementsCommand.prototype.getPathsToAddOrDefault = function(index) {
+ return /** @type {!sketchology.proto.Path} */ (this.get$ValueOrDefault(2, index));
+};
+
+
+/**
+ * Adds a value to the paths_to_add field.
+ * @param {!sketchology.proto.Path} value The value to add.
+ */
+sketchology.proto.ReplaceElementsCommand.prototype.addPathsToAdd = function(value) {
+ this.add$Value(2, value);
+};
+
+
+/**
+ * Returns the array of values in the paths_to_add field.
+ * @return {!Array<!sketchology.proto.Path>} The values in the field.
+ */
+sketchology.proto.ReplaceElementsCommand.prototype.pathsToAddArray = function() {
+ return /** @type {!Array<!sketchology.proto.Path>} */ (this.array$Values(2));
+};
+
+
+/**
+ * @return {boolean} Whether the paths_to_add field has a value.
+ */
+sketchology.proto.ReplaceElementsCommand.prototype.hasPathsToAdd = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the paths_to_add field.
+ */
+sketchology.proto.ReplaceElementsCommand.prototype.pathsToAddCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the paths_to_add field.
+ */
+sketchology.proto.ReplaceElementsCommand.prototype.clearPathsToAdd = function() {
+ this.clear$Field(2);
+};
+
+
+
+/**
+ * Message EvictImageData.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.EvictImageData = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.EvictImageData, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.EvictImageData.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.EvictImageData} The cloned message.
+ * @override
+ */
+sketchology.proto.EvictImageData.prototype.clone;
+
+
+/**
+ * Gets the value of the uri field.
+ * @return {?string} The value.
+ */
+sketchology.proto.EvictImageData.prototype.getUri = function() {
+ return /** @type {?string} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the uri field or the default value if not set.
+ * @return {string} The value.
+ */
+sketchology.proto.EvictImageData.prototype.getUriOrDefault = function() {
+ return /** @type {string} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the uri field.
+ * @param {string} value The value.
+ */
+sketchology.proto.EvictImageData.prototype.setUri = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the uri field has a value.
+ */
+sketchology.proto.EvictImageData.prototype.hasUri = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the uri field.
+ */
+sketchology.proto.EvictImageData.prototype.uriCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the uri field.
+ */
+sketchology.proto.EvictImageData.prototype.clearUri = function() {
+ this.clear$Field(1);
+};
+
+
+
+/**
+ * Message Viewport.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.Viewport = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.Viewport, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.Viewport.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.Viewport} The cloned message.
+ * @override
+ */
+sketchology.proto.Viewport.prototype.clone;
+
+
+/**
+ * Gets the value of the fbo_handle field.
+ * @return {?number} The value.
+ */
+sketchology.proto.Viewport.prototype.getFboHandle = function() {
+ return /** @type {?number} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the fbo_handle field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.Viewport.prototype.getFboHandleOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the fbo_handle field.
+ * @param {number} value The value.
+ */
+sketchology.proto.Viewport.prototype.setFboHandle = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the fbo_handle field has a value.
+ */
+sketchology.proto.Viewport.prototype.hasFboHandle = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the fbo_handle field.
+ */
+sketchology.proto.Viewport.prototype.fboHandleCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the fbo_handle field.
+ */
+sketchology.proto.Viewport.prototype.clearFboHandle = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the width field.
+ * @return {?number} The value.
+ */
+sketchology.proto.Viewport.prototype.getWidth = function() {
+ return /** @type {?number} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the width field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.Viewport.prototype.getWidthOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the width field.
+ * @param {number} value The value.
+ */
+sketchology.proto.Viewport.prototype.setWidth = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the width field has a value.
+ */
+sketchology.proto.Viewport.prototype.hasWidth = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the width field.
+ */
+sketchology.proto.Viewport.prototype.widthCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the width field.
+ */
+sketchology.proto.Viewport.prototype.clearWidth = function() {
+ this.clear$Field(2);
+};
+
+
+/**
+ * Gets the value of the height field.
+ * @return {?number} The value.
+ */
+sketchology.proto.Viewport.prototype.getHeight = function() {
+ return /** @type {?number} */ (this.get$Value(3));
+};
+
+
+/**
+ * Gets the value of the height field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.Viewport.prototype.getHeightOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(3));
+};
+
+
+/**
+ * Sets the value of the height field.
+ * @param {number} value The value.
+ */
+sketchology.proto.Viewport.prototype.setHeight = function(value) {
+ this.set$Value(3, value);
+};
+
+
+/**
+ * @return {boolean} Whether the height field has a value.
+ */
+sketchology.proto.Viewport.prototype.hasHeight = function() {
+ return this.has$Value(3);
+};
+
+
+/**
+ * @return {number} The number of values in the height field.
+ */
+sketchology.proto.Viewport.prototype.heightCount = function() {
+ return this.count$Values(3);
+};
+
+
+/**
+ * Clears the values in the height field.
+ */
+sketchology.proto.Viewport.prototype.clearHeight = function() {
+ this.clear$Field(3);
+};
+
+
+/**
+ * Gets the value of the ppi field.
+ * @return {?number} The value.
+ */
+sketchology.proto.Viewport.prototype.getPpi = function() {
+ return /** @type {?number} */ (this.get$Value(4));
+};
+
+
+/**
+ * Gets the value of the ppi field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.Viewport.prototype.getPpiOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(4));
+};
+
+
+/**
+ * Sets the value of the ppi field.
+ * @param {number} value The value.
+ */
+sketchology.proto.Viewport.prototype.setPpi = function(value) {
+ this.set$Value(4, value);
+};
+
+
+/**
+ * @return {boolean} Whether the ppi field has a value.
+ */
+sketchology.proto.Viewport.prototype.hasPpi = function() {
+ return this.has$Value(4);
+};
+
+
+/**
+ * @return {number} The number of values in the ppi field.
+ */
+sketchology.proto.Viewport.prototype.ppiCount = function() {
+ return this.count$Values(4);
+};
+
+
+/**
+ * Clears the values in the ppi field.
+ */
+sketchology.proto.Viewport.prototype.clearPpi = function() {
+ this.clear$Field(4);
+};
+
+
+
+/**
+ * Message ImageExport.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.ImageExport = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.ImageExport, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.ImageExport.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.ImageExport} The cloned message.
+ * @override
+ */
+sketchology.proto.ImageExport.prototype.clone;
+
+
+/**
+ * Gets the value of the max_dimension_px field.
+ * @return {?number} The value.
+ */
+sketchology.proto.ImageExport.prototype.getMaxDimensionPx = function() {
+ return /** @type {?number} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the max_dimension_px field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.ImageExport.prototype.getMaxDimensionPxOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the max_dimension_px field.
+ * @param {number} value The value.
+ */
+sketchology.proto.ImageExport.prototype.setMaxDimensionPx = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the max_dimension_px field has a value.
+ */
+sketchology.proto.ImageExport.prototype.hasMaxDimensionPx = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the max_dimension_px field.
+ */
+sketchology.proto.ImageExport.prototype.maxDimensionPxCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the max_dimension_px field.
+ */
+sketchology.proto.ImageExport.prototype.clearMaxDimensionPx = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the should_draw_background field.
+ * @return {?boolean} The value.
+ */
+sketchology.proto.ImageExport.prototype.getShouldDrawBackground = function() {
+ return /** @type {?boolean} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the should_draw_background field or the default value if not set.
+ * @return {boolean} The value.
+ */
+sketchology.proto.ImageExport.prototype.getShouldDrawBackgroundOrDefault = function() {
+ return /** @type {boolean} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the should_draw_background field.
+ * @param {boolean} value The value.
+ */
+sketchology.proto.ImageExport.prototype.setShouldDrawBackground = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the should_draw_background field has a value.
+ */
+sketchology.proto.ImageExport.prototype.hasShouldDrawBackground = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the should_draw_background field.
+ */
+sketchology.proto.ImageExport.prototype.shouldDrawBackgroundCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the should_draw_background field.
+ */
+sketchology.proto.ImageExport.prototype.clearShouldDrawBackground = function() {
+ this.clear$Field(2);
+};
+
+
+
+/**
+ * Message LinearPathAnimation.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.LinearPathAnimation = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.LinearPathAnimation, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.LinearPathAnimation.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.LinearPathAnimation} The cloned message.
+ * @override
+ */
+sketchology.proto.LinearPathAnimation.prototype.clone;
+
+
+/**
+ * Gets the value of the rgba_from field.
+ * @return {?number} The value.
+ */
+sketchology.proto.LinearPathAnimation.prototype.getRgbaFrom = function() {
+ return /** @type {?number} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the rgba_from field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.LinearPathAnimation.prototype.getRgbaFromOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the rgba_from field.
+ * @param {number} value The value.
+ */
+sketchology.proto.LinearPathAnimation.prototype.setRgbaFrom = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the rgba_from field has a value.
+ */
+sketchology.proto.LinearPathAnimation.prototype.hasRgbaFrom = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the rgba_from field.
+ */
+sketchology.proto.LinearPathAnimation.prototype.rgbaFromCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the rgba_from field.
+ */
+sketchology.proto.LinearPathAnimation.prototype.clearRgbaFrom = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the rgba_seconds field.
+ * @return {?number} The value.
+ */
+sketchology.proto.LinearPathAnimation.prototype.getRgbaSeconds = function() {
+ return /** @type {?number} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the rgba_seconds field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.LinearPathAnimation.prototype.getRgbaSecondsOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the rgba_seconds field.
+ * @param {number} value The value.
+ */
+sketchology.proto.LinearPathAnimation.prototype.setRgbaSeconds = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the rgba_seconds field has a value.
+ */
+sketchology.proto.LinearPathAnimation.prototype.hasRgbaSeconds = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the rgba_seconds field.
+ */
+sketchology.proto.LinearPathAnimation.prototype.rgbaSecondsCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the rgba_seconds field.
+ */
+sketchology.proto.LinearPathAnimation.prototype.clearRgbaSeconds = function() {
+ this.clear$Field(2);
+};
+
+
+/**
+ * Gets the value of the dilation_from field.
+ * @return {?number} The value.
+ */
+sketchology.proto.LinearPathAnimation.prototype.getDilationFrom = function() {
+ return /** @type {?number} */ (this.get$Value(3));
+};
+
+
+/**
+ * Gets the value of the dilation_from field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.LinearPathAnimation.prototype.getDilationFromOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(3));
+};
+
+
+/**
+ * Sets the value of the dilation_from field.
+ * @param {number} value The value.
+ */
+sketchology.proto.LinearPathAnimation.prototype.setDilationFrom = function(value) {
+ this.set$Value(3, value);
+};
+
+
+/**
+ * @return {boolean} Whether the dilation_from field has a value.
+ */
+sketchology.proto.LinearPathAnimation.prototype.hasDilationFrom = function() {
+ return this.has$Value(3);
+};
+
+
+/**
+ * @return {number} The number of values in the dilation_from field.
+ */
+sketchology.proto.LinearPathAnimation.prototype.dilationFromCount = function() {
+ return this.count$Values(3);
+};
+
+
+/**
+ * Clears the values in the dilation_from field.
+ */
+sketchology.proto.LinearPathAnimation.prototype.clearDilationFrom = function() {
+ this.clear$Field(3);
+};
+
+
+/**
+ * Gets the value of the dilation_seconds field.
+ * @return {?number} The value.
+ */
+sketchology.proto.LinearPathAnimation.prototype.getDilationSeconds = function() {
+ return /** @type {?number} */ (this.get$Value(4));
+};
+
+
+/**
+ * Gets the value of the dilation_seconds field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.LinearPathAnimation.prototype.getDilationSecondsOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(4));
+};
+
+
+/**
+ * Sets the value of the dilation_seconds field.
+ * @param {number} value The value.
+ */
+sketchology.proto.LinearPathAnimation.prototype.setDilationSeconds = function(value) {
+ this.set$Value(4, value);
+};
+
+
+/**
+ * @return {boolean} Whether the dilation_seconds field has a value.
+ */
+sketchology.proto.LinearPathAnimation.prototype.hasDilationSeconds = function() {
+ return this.has$Value(4);
+};
+
+
+/**
+ * @return {number} The number of values in the dilation_seconds field.
+ */
+sketchology.proto.LinearPathAnimation.prototype.dilationSecondsCount = function() {
+ return this.count$Values(4);
+};
+
+
+/**
+ * Clears the values in the dilation_seconds field.
+ */
+sketchology.proto.LinearPathAnimation.prototype.clearDilationSeconds = function() {
+ this.clear$Field(4);
+};
+
+
+
+/**
+ * Message LineSize.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.LineSize = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.LineSize, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.LineSize.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.LineSize} The cloned message.
+ * @override
+ */
+sketchology.proto.LineSize.prototype.clone;
+
+
+/**
+ * Gets the value of the stroke_width field.
+ * @return {?number} The value.
+ */
+sketchology.proto.LineSize.prototype.getStrokeWidth = function() {
+ return /** @type {?number} */ (this.get$Value(7));
+};
+
+
+/**
+ * Gets the value of the stroke_width field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.LineSize.prototype.getStrokeWidthOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(7));
+};
+
+
+/**
+ * Sets the value of the stroke_width field.
+ * @param {number} value The value.
+ */
+sketchology.proto.LineSize.prototype.setStrokeWidth = function(value) {
+ this.set$Value(7, value);
+};
+
+
+/**
+ * @return {boolean} Whether the stroke_width field has a value.
+ */
+sketchology.proto.LineSize.prototype.hasStrokeWidth = function() {
+ return this.has$Value(7);
+};
+
+
+/**
+ * @return {number} The number of values in the stroke_width field.
+ */
+sketchology.proto.LineSize.prototype.strokeWidthCount = function() {
+ return this.count$Values(7);
+};
+
+
+/**
+ * Clears the values in the stroke_width field.
+ */
+sketchology.proto.LineSize.prototype.clearStrokeWidth = function() {
+ this.clear$Field(7);
+};
+
+
+/**
+ * Gets the value of the units field.
+ * @return {?sketchology.proto.LineSize.SizeType} The value.
+ */
+sketchology.proto.LineSize.prototype.getUnits = function() {
+ return /** @type {?sketchology.proto.LineSize.SizeType} */ (this.get$Value(8));
+};
+
+
+/**
+ * Gets the value of the units field or the default value if not set.
+ * @return {!sketchology.proto.LineSize.SizeType} The value.
+ */
+sketchology.proto.LineSize.prototype.getUnitsOrDefault = function() {
+ return /** @type {!sketchology.proto.LineSize.SizeType} */ (this.get$ValueOrDefault(8));
+};
+
+
+/**
+ * Sets the value of the units field.
+ * @param {!sketchology.proto.LineSize.SizeType} value The value.
+ */
+sketchology.proto.LineSize.prototype.setUnits = function(value) {
+ this.set$Value(8, value);
+};
+
+
+/**
+ * @return {boolean} Whether the units field has a value.
+ */
+sketchology.proto.LineSize.prototype.hasUnits = function() {
+ return this.has$Value(8);
+};
+
+
+/**
+ * @return {number} The number of values in the units field.
+ */
+sketchology.proto.LineSize.prototype.unitsCount = function() {
+ return this.count$Values(8);
+};
+
+
+/**
+ * Clears the values in the units field.
+ */
+sketchology.proto.LineSize.prototype.clearUnits = function() {
+ this.clear$Field(8);
+};
+
+
+/**
+ * Enumeration SizeType.
+ * @enum {number}
+ */
+sketchology.proto.LineSize.SizeType = {
+ UNKNOWN_SIZE: 0,
+ WORLD_UNITS: 1,
+ POINTS: 2,
+ ZOOM_INDEPENDENT_DP: 3,
+ PERCENT_WORLD: 4,
+ PERCENT_ZOOM_INDEPENDENT: 5
+};
+
+
+
+/**
+ * Message PusherToolParams.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.PusherToolParams = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.PusherToolParams, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.PusherToolParams.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.PusherToolParams} The cloned message.
+ * @override
+ */
+sketchology.proto.PusherToolParams.prototype.clone;
+
+
+/**
+ * Gets the value of the manipulate_stickers field.
+ * @return {?boolean} The value.
+ */
+sketchology.proto.PusherToolParams.prototype.getManipulateStickers = function() {
+ return /** @type {?boolean} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the manipulate_stickers field or the default value if not set.
+ * @return {boolean} The value.
+ */
+sketchology.proto.PusherToolParams.prototype.getManipulateStickersOrDefault = function() {
+ return /** @type {boolean} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the manipulate_stickers field.
+ * @param {boolean} value The value.
+ */
+sketchology.proto.PusherToolParams.prototype.setManipulateStickers = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the manipulate_stickers field has a value.
+ */
+sketchology.proto.PusherToolParams.prototype.hasManipulateStickers = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the manipulate_stickers field.
+ */
+sketchology.proto.PusherToolParams.prototype.manipulateStickersCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the manipulate_stickers field.
+ */
+sketchology.proto.PusherToolParams.prototype.clearManipulateStickers = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the manipulate_text field.
+ * @return {?boolean} The value.
+ */
+sketchology.proto.PusherToolParams.prototype.getManipulateText = function() {
+ return /** @type {?boolean} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the manipulate_text field or the default value if not set.
+ * @return {boolean} The value.
+ */
+sketchology.proto.PusherToolParams.prototype.getManipulateTextOrDefault = function() {
+ return /** @type {boolean} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the manipulate_text field.
+ * @param {boolean} value The value.
+ */
+sketchology.proto.PusherToolParams.prototype.setManipulateText = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the manipulate_text field has a value.
+ */
+sketchology.proto.PusherToolParams.prototype.hasManipulateText = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the manipulate_text field.
+ */
+sketchology.proto.PusherToolParams.prototype.manipulateTextCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the manipulate_text field.
+ */
+sketchology.proto.PusherToolParams.prototype.clearManipulateText = function() {
+ this.clear$Field(2);
+};
+
+
+
+/**
+ * Message ToolParams.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.ToolParams = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.ToolParams, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.ToolParams.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.ToolParams} The cloned message.
+ * @override
+ */
+sketchology.proto.ToolParams.prototype.clone;
+
+
+/**
+ * Gets the value of the tool field.
+ * @return {?sketchology.proto.ToolParams.ToolType} The value.
+ */
+sketchology.proto.ToolParams.prototype.getTool = function() {
+ return /** @type {?sketchology.proto.ToolParams.ToolType} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the tool field or the default value if not set.
+ * @return {!sketchology.proto.ToolParams.ToolType} The value.
+ */
+sketchology.proto.ToolParams.prototype.getToolOrDefault = function() {
+ return /** @type {!sketchology.proto.ToolParams.ToolType} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the tool field.
+ * @param {!sketchology.proto.ToolParams.ToolType} value The value.
+ */
+sketchology.proto.ToolParams.prototype.setTool = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the tool field has a value.
+ */
+sketchology.proto.ToolParams.prototype.hasTool = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the tool field.
+ */
+sketchology.proto.ToolParams.prototype.toolCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the tool field.
+ */
+sketchology.proto.ToolParams.prototype.clearTool = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the rgba field.
+ * @return {?number} The value.
+ */
+sketchology.proto.ToolParams.prototype.getRgba = function() {
+ return /** @type {?number} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the rgba field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.ToolParams.prototype.getRgbaOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the rgba field.
+ * @param {number} value The value.
+ */
+sketchology.proto.ToolParams.prototype.setRgba = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the rgba field has a value.
+ */
+sketchology.proto.ToolParams.prototype.hasRgba = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the rgba field.
+ */
+sketchology.proto.ToolParams.prototype.rgbaCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the rgba field.
+ */
+sketchology.proto.ToolParams.prototype.clearRgba = function() {
+ this.clear$Field(2);
+};
+
+
+/**
+ * Gets the value of the line_size field.
+ * @return {?sketchology.proto.LineSize} The value.
+ */
+sketchology.proto.ToolParams.prototype.getLineSize = function() {
+ return /** @type {?sketchology.proto.LineSize} */ (this.get$Value(3));
+};
+
+
+/**
+ * Gets the value of the line_size field or the default value if not set.
+ * @return {!sketchology.proto.LineSize} The value.
+ */
+sketchology.proto.ToolParams.prototype.getLineSizeOrDefault = function() {
+ return /** @type {!sketchology.proto.LineSize} */ (this.get$ValueOrDefault(3));
+};
+
+
+/**
+ * Sets the value of the line_size field.
+ * @param {!sketchology.proto.LineSize} value The value.
+ */
+sketchology.proto.ToolParams.prototype.setLineSize = function(value) {
+ this.set$Value(3, value);
+};
+
+
+/**
+ * @return {boolean} Whether the line_size field has a value.
+ */
+sketchology.proto.ToolParams.prototype.hasLineSize = function() {
+ return this.has$Value(3);
+};
+
+
+/**
+ * @return {number} The number of values in the line_size field.
+ */
+sketchology.proto.ToolParams.prototype.lineSizeCount = function() {
+ return this.count$Values(3);
+};
+
+
+/**
+ * Clears the values in the line_size field.
+ */
+sketchology.proto.ToolParams.prototype.clearLineSize = function() {
+ this.clear$Field(3);
+};
+
+
+/**
+ * Gets the value of the pusher_tool_params field.
+ * @return {?sketchology.proto.PusherToolParams} The value.
+ */
+sketchology.proto.ToolParams.prototype.getPusherToolParams = function() {
+ return /** @type {?sketchology.proto.PusherToolParams} */ (this.get$Value(4));
+};
+
+
+/**
+ * Gets the value of the pusher_tool_params field or the default value if not set.
+ * @return {!sketchology.proto.PusherToolParams} The value.
+ */
+sketchology.proto.ToolParams.prototype.getPusherToolParamsOrDefault = function() {
+ return /** @type {!sketchology.proto.PusherToolParams} */ (this.get$ValueOrDefault(4));
+};
+
+
+/**
+ * Sets the value of the pusher_tool_params field.
+ * @param {!sketchology.proto.PusherToolParams} value The value.
+ */
+sketchology.proto.ToolParams.prototype.setPusherToolParams = function(value) {
+ this.set$Value(4, value);
+};
+
+
+/**
+ * @return {boolean} Whether the pusher_tool_params field has a value.
+ */
+sketchology.proto.ToolParams.prototype.hasPusherToolParams = function() {
+ return this.has$Value(4);
+};
+
+
+/**
+ * @return {number} The number of values in the pusher_tool_params field.
+ */
+sketchology.proto.ToolParams.prototype.pusherToolParamsCount = function() {
+ return this.count$Values(4);
+};
+
+
+/**
+ * Clears the values in the pusher_tool_params field.
+ */
+sketchology.proto.ToolParams.prototype.clearPusherToolParams = function() {
+ this.clear$Field(4);
+};
+
+
+/**
+ * Gets the value of the brush_type field.
+ * @return {?sketchology.proto.BrushType} The value.
+ */
+sketchology.proto.ToolParams.prototype.getBrushType = function() {
+ return /** @type {?sketchology.proto.BrushType} */ (this.get$Value(5));
+};
+
+
+/**
+ * Gets the value of the brush_type field or the default value if not set.
+ * @return {!sketchology.proto.BrushType} The value.
+ */
+sketchology.proto.ToolParams.prototype.getBrushTypeOrDefault = function() {
+ return /** @type {!sketchology.proto.BrushType} */ (this.get$ValueOrDefault(5));
+};
+
+
+/**
+ * Sets the value of the brush_type field.
+ * @param {!sketchology.proto.BrushType} value The value.
+ */
+sketchology.proto.ToolParams.prototype.setBrushType = function(value) {
+ this.set$Value(5, value);
+};
+
+
+/**
+ * @return {boolean} Whether the brush_type field has a value.
+ */
+sketchology.proto.ToolParams.prototype.hasBrushType = function() {
+ return this.has$Value(5);
+};
+
+
+/**
+ * @return {number} The number of values in the brush_type field.
+ */
+sketchology.proto.ToolParams.prototype.brushTypeCount = function() {
+ return this.count$Values(5);
+};
+
+
+/**
+ * Clears the values in the brush_type field.
+ */
+sketchology.proto.ToolParams.prototype.clearBrushType = function() {
+ this.clear$Field(5);
+};
+
+
+/**
+ * Gets the value of the linear_path_animation field.
+ * @return {?sketchology.proto.LinearPathAnimation} The value.
+ */
+sketchology.proto.ToolParams.prototype.getLinearPathAnimation = function() {
+ return /** @type {?sketchology.proto.LinearPathAnimation} */ (this.get$Value(6));
+};
+
+
+/**
+ * Gets the value of the linear_path_animation field or the default value if not set.
+ * @return {!sketchology.proto.LinearPathAnimation} The value.
+ */
+sketchology.proto.ToolParams.prototype.getLinearPathAnimationOrDefault = function() {
+ return /** @type {!sketchology.proto.LinearPathAnimation} */ (this.get$ValueOrDefault(6));
+};
+
+
+/**
+ * Sets the value of the linear_path_animation field.
+ * @param {!sketchology.proto.LinearPathAnimation} value The value.
+ */
+sketchology.proto.ToolParams.prototype.setLinearPathAnimation = function(value) {
+ this.set$Value(6, value);
+};
+
+
+/**
+ * @return {boolean} Whether the linear_path_animation field has a value.
+ */
+sketchology.proto.ToolParams.prototype.hasLinearPathAnimation = function() {
+ return this.has$Value(6);
+};
+
+
+/**
+ * @return {number} The number of values in the linear_path_animation field.
+ */
+sketchology.proto.ToolParams.prototype.linearPathAnimationCount = function() {
+ return this.count$Values(6);
+};
+
+
+/**
+ * Clears the values in the linear_path_animation field.
+ */
+sketchology.proto.ToolParams.prototype.clearLinearPathAnimation = function() {
+ this.clear$Field(6);
+};
+
+
+/**
+ * Enumeration ToolType.
+ * @enum {number}
+ */
+sketchology.proto.ToolParams.ToolType = {
+ UNKNOWN: 0,
+ LINE: 1,
+ EDIT: 2,
+ MAGIC_ERASE: 3,
+ QUERY: 4,
+ NOTOOL: 5,
+ FILTER_CHOOSER: 6,
+ PUSHER: 7,
+ CROP: 8
+};
+
+
+
+/**
+ * Message FlagAssignment.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.FlagAssignment = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.FlagAssignment, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.FlagAssignment.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.FlagAssignment} The cloned message.
+ * @override
+ */
+sketchology.proto.FlagAssignment.prototype.clone;
+
+
+/**
+ * Gets the value of the flag field.
+ * @return {?sketchology.proto.Flag} The value.
+ */
+sketchology.proto.FlagAssignment.prototype.getFlag = function() {
+ return /** @type {?sketchology.proto.Flag} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the flag field or the default value if not set.
+ * @return {!sketchology.proto.Flag} The value.
+ */
+sketchology.proto.FlagAssignment.prototype.getFlagOrDefault = function() {
+ return /** @type {!sketchology.proto.Flag} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the flag field.
+ * @param {!sketchology.proto.Flag} value The value.
+ */
+sketchology.proto.FlagAssignment.prototype.setFlag = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the flag field has a value.
+ */
+sketchology.proto.FlagAssignment.prototype.hasFlag = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the flag field.
+ */
+sketchology.proto.FlagAssignment.prototype.flagCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the flag field.
+ */
+sketchology.proto.FlagAssignment.prototype.clearFlag = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the bool_value field.
+ * @return {?boolean} The value.
+ */
+sketchology.proto.FlagAssignment.prototype.getBoolValue = function() {
+ return /** @type {?boolean} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the bool_value field or the default value if not set.
+ * @return {boolean} The value.
+ */
+sketchology.proto.FlagAssignment.prototype.getBoolValueOrDefault = function() {
+ return /** @type {boolean} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the bool_value field.
+ * @param {boolean} value The value.
+ */
+sketchology.proto.FlagAssignment.prototype.setBoolValue = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the bool_value field has a value.
+ */
+sketchology.proto.FlagAssignment.prototype.hasBoolValue = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the bool_value field.
+ */
+sketchology.proto.FlagAssignment.prototype.boolValueCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the bool_value field.
+ */
+sketchology.proto.FlagAssignment.prototype.clearBoolValue = function() {
+ this.clear$Field(2);
+};
+
+
+
+/**
+ * Message AddElement.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.AddElement = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.AddElement, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.AddElement.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.AddElement} The cloned message.
+ * @override
+ */
+sketchology.proto.AddElement.prototype.clone;
+
+
+/**
+ * Gets the value of the bundle field.
+ * @return {?sketchology.proto.ElementBundle} The value.
+ */
+sketchology.proto.AddElement.prototype.getBundle = function() {
+ return /** @type {?sketchology.proto.ElementBundle} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the bundle field or the default value if not set.
+ * @return {!sketchology.proto.ElementBundle} The value.
+ */
+sketchology.proto.AddElement.prototype.getBundleOrDefault = function() {
+ return /** @type {!sketchology.proto.ElementBundle} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the bundle field.
+ * @param {!sketchology.proto.ElementBundle} value The value.
+ */
+sketchology.proto.AddElement.prototype.setBundle = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the bundle field has a value.
+ */
+sketchology.proto.AddElement.prototype.hasBundle = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the bundle field.
+ */
+sketchology.proto.AddElement.prototype.bundleCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the bundle field.
+ */
+sketchology.proto.AddElement.prototype.clearBundle = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the below_element_with_uuid field.
+ * @return {?string} The value.
+ */
+sketchology.proto.AddElement.prototype.getBelowElementWithUuid = function() {
+ return /** @type {?string} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the below_element_with_uuid field or the default value if not set.
+ * @return {string} The value.
+ */
+sketchology.proto.AddElement.prototype.getBelowElementWithUuidOrDefault = function() {
+ return /** @type {string} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the below_element_with_uuid field.
+ * @param {string} value The value.
+ */
+sketchology.proto.AddElement.prototype.setBelowElementWithUuid = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the below_element_with_uuid field has a value.
+ */
+sketchology.proto.AddElement.prototype.hasBelowElementWithUuid = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the below_element_with_uuid field.
+ */
+sketchology.proto.AddElement.prototype.belowElementWithUuidCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the below_element_with_uuid field.
+ */
+sketchology.proto.AddElement.prototype.clearBelowElementWithUuid = function() {
+ this.clear$Field(2);
+};
+
+
+
+/**
+ * Message OutOfBoundsColor.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.OutOfBoundsColor = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.OutOfBoundsColor, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.OutOfBoundsColor.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.OutOfBoundsColor} The cloned message.
+ * @override
+ */
+sketchology.proto.OutOfBoundsColor.prototype.clone;
+
+
+/**
+ * Gets the value of the rgba field.
+ * @return {?number} The value.
+ */
+sketchology.proto.OutOfBoundsColor.prototype.getRgba = function() {
+ return /** @type {?number} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the rgba field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.OutOfBoundsColor.prototype.getRgbaOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the rgba field.
+ * @param {number} value The value.
+ */
+sketchology.proto.OutOfBoundsColor.prototype.setRgba = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the rgba field has a value.
+ */
+sketchology.proto.OutOfBoundsColor.prototype.hasRgba = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the rgba field.
+ */
+sketchology.proto.OutOfBoundsColor.prototype.rgbaCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the rgba field.
+ */
+sketchology.proto.OutOfBoundsColor.prototype.clearRgba = function() {
+ this.clear$Field(1);
+};
+
+
+
+/**
+ * Message SInputStream.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.SInputStream = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.SInputStream, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.SInputStream.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.SInputStream} The cloned message.
+ * @override
+ */
+sketchology.proto.SInputStream.prototype.clone;
+
+
+/**
+ * Gets the value of the screen_width field.
+ * @return {?number} The value.
+ */
+sketchology.proto.SInputStream.prototype.getScreenWidth = function() {
+ return /** @type {?number} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the screen_width field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.SInputStream.prototype.getScreenWidthOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the screen_width field.
+ * @param {number} value The value.
+ */
+sketchology.proto.SInputStream.prototype.setScreenWidth = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the screen_width field has a value.
+ */
+sketchology.proto.SInputStream.prototype.hasScreenWidth = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the screen_width field.
+ */
+sketchology.proto.SInputStream.prototype.screenWidthCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the screen_width field.
+ */
+sketchology.proto.SInputStream.prototype.clearScreenWidth = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the screen_height field.
+ * @return {?number} The value.
+ */
+sketchology.proto.SInputStream.prototype.getScreenHeight = function() {
+ return /** @type {?number} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the screen_height field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.SInputStream.prototype.getScreenHeightOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the screen_height field.
+ * @param {number} value The value.
+ */
+sketchology.proto.SInputStream.prototype.setScreenHeight = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the screen_height field has a value.
+ */
+sketchology.proto.SInputStream.prototype.hasScreenHeight = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the screen_height field.
+ */
+sketchology.proto.SInputStream.prototype.screenHeightCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the screen_height field.
+ */
+sketchology.proto.SInputStream.prototype.clearScreenHeight = function() {
+ this.clear$Field(2);
+};
+
+
+/**
+ * Gets the value of the screen_ppi field.
+ * @return {?number} The value.
+ */
+sketchology.proto.SInputStream.prototype.getScreenPpi = function() {
+ return /** @type {?number} */ (this.get$Value(3));
+};
+
+
+/**
+ * Gets the value of the screen_ppi field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.SInputStream.prototype.getScreenPpiOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(3));
+};
+
+
+/**
+ * Sets the value of the screen_ppi field.
+ * @param {number} value The value.
+ */
+sketchology.proto.SInputStream.prototype.setScreenPpi = function(value) {
+ this.set$Value(3, value);
+};
+
+
+/**
+ * @return {boolean} Whether the screen_ppi field has a value.
+ */
+sketchology.proto.SInputStream.prototype.hasScreenPpi = function() {
+ return this.has$Value(3);
+};
+
+
+/**
+ * @return {number} The number of values in the screen_ppi field.
+ */
+sketchology.proto.SInputStream.prototype.screenPpiCount = function() {
+ return this.count$Values(3);
+};
+
+
+/**
+ * Clears the values in the screen_ppi field.
+ */
+sketchology.proto.SInputStream.prototype.clearScreenPpi = function() {
+ this.clear$Field(3);
+};
+
+
+/**
+ * Gets the value of the input field at the index given.
+ * @param {number} index The index to lookup.
+ * @return {?sketchology.proto.SInput} The value.
+ */
+sketchology.proto.SInputStream.prototype.getInput = function(index) {
+ return /** @type {?sketchology.proto.SInput} */ (this.get$Value(4, index));
+};
+
+
+/**
+ * Gets the value of the input field at the index given or the default value if not set.
+ * @param {number} index The index to lookup.
+ * @return {!sketchology.proto.SInput} The value.
+ */
+sketchology.proto.SInputStream.prototype.getInputOrDefault = function(index) {
+ return /** @type {!sketchology.proto.SInput} */ (this.get$ValueOrDefault(4, index));
+};
+
+
+/**
+ * Adds a value to the input field.
+ * @param {!sketchology.proto.SInput} value The value to add.
+ */
+sketchology.proto.SInputStream.prototype.addInput = function(value) {
+ this.add$Value(4, value);
+};
+
+
+/**
+ * Returns the array of values in the input field.
+ * @return {!Array<!sketchology.proto.SInput>} The values in the field.
+ */
+sketchology.proto.SInputStream.prototype.inputArray = function() {
+ return /** @type {!Array<!sketchology.proto.SInput>} */ (this.array$Values(4));
+};
+
+
+/**
+ * @return {boolean} Whether the input field has a value.
+ */
+sketchology.proto.SInputStream.prototype.hasInput = function() {
+ return this.has$Value(4);
+};
+
+
+/**
+ * @return {number} The number of values in the input field.
+ */
+sketchology.proto.SInputStream.prototype.inputCount = function() {
+ return this.count$Values(4);
+};
+
+
+/**
+ * Clears the values in the input field.
+ */
+sketchology.proto.SInputStream.prototype.clearInput = function() {
+ this.clear$Field(4);
+};
+
+
+
+/**
+ * Message SInput.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.SInput = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.SInput, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.SInput.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.SInput} The cloned message.
+ * @override
+ */
+sketchology.proto.SInput.prototype.clone;
+
+
+/**
+ * Gets the value of the type field.
+ * @return {?sketchology.proto.SInput.InputType} The value.
+ */
+sketchology.proto.SInput.prototype.getType = function() {
+ return /** @type {?sketchology.proto.SInput.InputType} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the type field or the default value if not set.
+ * @return {!sketchology.proto.SInput.InputType} The value.
+ */
+sketchology.proto.SInput.prototype.getTypeOrDefault = function() {
+ return /** @type {!sketchology.proto.SInput.InputType} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the type field.
+ * @param {!sketchology.proto.SInput.InputType} value The value.
+ */
+sketchology.proto.SInput.prototype.setType = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the type field has a value.
+ */
+sketchology.proto.SInput.prototype.hasType = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the type field.
+ */
+sketchology.proto.SInput.prototype.typeCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the type field.
+ */
+sketchology.proto.SInput.prototype.clearType = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the id field.
+ * @return {?number} The value.
+ */
+sketchology.proto.SInput.prototype.getId = function() {
+ return /** @type {?number} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the id field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.SInput.prototype.getIdOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the id field.
+ * @param {number} value The value.
+ */
+sketchology.proto.SInput.prototype.setId = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the id field has a value.
+ */
+sketchology.proto.SInput.prototype.hasId = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the id field.
+ */
+sketchology.proto.SInput.prototype.idCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the id field.
+ */
+sketchology.proto.SInput.prototype.clearId = function() {
+ this.clear$Field(2);
+};
+
+
+/**
+ * Gets the value of the flags field.
+ * @return {?number} The value.
+ */
+sketchology.proto.SInput.prototype.getFlags = function() {
+ return /** @type {?number} */ (this.get$Value(3));
+};
+
+
+/**
+ * Gets the value of the flags field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.SInput.prototype.getFlagsOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(3));
+};
+
+
+/**
+ * Sets the value of the flags field.
+ * @param {number} value The value.
+ */
+sketchology.proto.SInput.prototype.setFlags = function(value) {
+ this.set$Value(3, value);
+};
+
+
+/**
+ * @return {boolean} Whether the flags field has a value.
+ */
+sketchology.proto.SInput.prototype.hasFlags = function() {
+ return this.has$Value(3);
+};
+
+
+/**
+ * @return {number} The number of values in the flags field.
+ */
+sketchology.proto.SInput.prototype.flagsCount = function() {
+ return this.count$Values(3);
+};
+
+
+/**
+ * Clears the values in the flags field.
+ */
+sketchology.proto.SInput.prototype.clearFlags = function() {
+ this.clear$Field(3);
+};
+
+
+/**
+ * Gets the value of the time_s field.
+ * @return {?number} The value.
+ */
+sketchology.proto.SInput.prototype.getTimeS = function() {
+ return /** @type {?number} */ (this.get$Value(4));
+};
+
+
+/**
+ * Gets the value of the time_s field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.SInput.prototype.getTimeSOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(4));
+};
+
+
+/**
+ * Sets the value of the time_s field.
+ * @param {number} value The value.
+ */
+sketchology.proto.SInput.prototype.setTimeS = function(value) {
+ this.set$Value(4, value);
+};
+
+
+/**
+ * @return {boolean} Whether the time_s field has a value.
+ */
+sketchology.proto.SInput.prototype.hasTimeS = function() {
+ return this.has$Value(4);
+};
+
+
+/**
+ * @return {number} The number of values in the time_s field.
+ */
+sketchology.proto.SInput.prototype.timeSCount = function() {
+ return this.count$Values(4);
+};
+
+
+/**
+ * Clears the values in the time_s field.
+ */
+sketchology.proto.SInput.prototype.clearTimeS = function() {
+ this.clear$Field(4);
+};
+
+
+/**
+ * Gets the value of the screen_pos_x field.
+ * @return {?number} The value.
+ */
+sketchology.proto.SInput.prototype.getScreenPosX = function() {
+ return /** @type {?number} */ (this.get$Value(5));
+};
+
+
+/**
+ * Gets the value of the screen_pos_x field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.SInput.prototype.getScreenPosXOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(5));
+};
+
+
+/**
+ * Sets the value of the screen_pos_x field.
+ * @param {number} value The value.
+ */
+sketchology.proto.SInput.prototype.setScreenPosX = function(value) {
+ this.set$Value(5, value);
+};
+
+
+/**
+ * @return {boolean} Whether the screen_pos_x field has a value.
+ */
+sketchology.proto.SInput.prototype.hasScreenPosX = function() {
+ return this.has$Value(5);
+};
+
+
+/**
+ * @return {number} The number of values in the screen_pos_x field.
+ */
+sketchology.proto.SInput.prototype.screenPosXCount = function() {
+ return this.count$Values(5);
+};
+
+
+/**
+ * Clears the values in the screen_pos_x field.
+ */
+sketchology.proto.SInput.prototype.clearScreenPosX = function() {
+ this.clear$Field(5);
+};
+
+
+/**
+ * Gets the value of the screen_pos_y field.
+ * @return {?number} The value.
+ */
+sketchology.proto.SInput.prototype.getScreenPosY = function() {
+ return /** @type {?number} */ (this.get$Value(6));
+};
+
+
+/**
+ * Gets the value of the screen_pos_y field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.SInput.prototype.getScreenPosYOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(6));
+};
+
+
+/**
+ * Sets the value of the screen_pos_y field.
+ * @param {number} value The value.
+ */
+sketchology.proto.SInput.prototype.setScreenPosY = function(value) {
+ this.set$Value(6, value);
+};
+
+
+/**
+ * @return {boolean} Whether the screen_pos_y field has a value.
+ */
+sketchology.proto.SInput.prototype.hasScreenPosY = function() {
+ return this.has$Value(6);
+};
+
+
+/**
+ * @return {number} The number of values in the screen_pos_y field.
+ */
+sketchology.proto.SInput.prototype.screenPosYCount = function() {
+ return this.count$Values(6);
+};
+
+
+/**
+ * Clears the values in the screen_pos_y field.
+ */
+sketchology.proto.SInput.prototype.clearScreenPosY = function() {
+ this.clear$Field(6);
+};
+
+
+/**
+ * Gets the value of the pressure field.
+ * @return {?number} The value.
+ */
+sketchology.proto.SInput.prototype.getPressure = function() {
+ return /** @type {?number} */ (this.get$Value(7));
+};
+
+
+/**
+ * Gets the value of the pressure field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.SInput.prototype.getPressureOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(7));
+};
+
+
+/**
+ * Sets the value of the pressure field.
+ * @param {number} value The value.
+ */
+sketchology.proto.SInput.prototype.setPressure = function(value) {
+ this.set$Value(7, value);
+};
+
+
+/**
+ * @return {boolean} Whether the pressure field has a value.
+ */
+sketchology.proto.SInput.prototype.hasPressure = function() {
+ return this.has$Value(7);
+};
+
+
+/**
+ * @return {number} The number of values in the pressure field.
+ */
+sketchology.proto.SInput.prototype.pressureCount = function() {
+ return this.count$Values(7);
+};
+
+
+/**
+ * Clears the values in the pressure field.
+ */
+sketchology.proto.SInput.prototype.clearPressure = function() {
+ this.clear$Field(7);
+};
+
+
+/**
+ * Gets the value of the wheel_delta field.
+ * @return {?number} The value.
+ */
+sketchology.proto.SInput.prototype.getWheelDelta = function() {
+ return /** @type {?number} */ (this.get$Value(8));
+};
+
+
+/**
+ * Gets the value of the wheel_delta field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.SInput.prototype.getWheelDeltaOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(8));
+};
+
+
+/**
+ * Sets the value of the wheel_delta field.
+ * @param {number} value The value.
+ */
+sketchology.proto.SInput.prototype.setWheelDelta = function(value) {
+ this.set$Value(8, value);
+};
+
+
+/**
+ * @return {boolean} Whether the wheel_delta field has a value.
+ */
+sketchology.proto.SInput.prototype.hasWheelDelta = function() {
+ return this.has$Value(8);
+};
+
+
+/**
+ * @return {number} The number of values in the wheel_delta field.
+ */
+sketchology.proto.SInput.prototype.wheelDeltaCount = function() {
+ return this.count$Values(8);
+};
+
+
+/**
+ * Clears the values in the wheel_delta field.
+ */
+sketchology.proto.SInput.prototype.clearWheelDelta = function() {
+ this.clear$Field(8);
+};
+
+
+/**
+ * Gets the value of the tilt field.
+ * @return {?number} The value.
+ */
+sketchology.proto.SInput.prototype.getTilt = function() {
+ return /** @type {?number} */ (this.get$Value(9));
+};
+
+
+/**
+ * Gets the value of the tilt field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.SInput.prototype.getTiltOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(9));
+};
+
+
+/**
+ * Sets the value of the tilt field.
+ * @param {number} value The value.
+ */
+sketchology.proto.SInput.prototype.setTilt = function(value) {
+ this.set$Value(9, value);
+};
+
+
+/**
+ * @return {boolean} Whether the tilt field has a value.
+ */
+sketchology.proto.SInput.prototype.hasTilt = function() {
+ return this.has$Value(9);
+};
+
+
+/**
+ * @return {number} The number of values in the tilt field.
+ */
+sketchology.proto.SInput.prototype.tiltCount = function() {
+ return this.count$Values(9);
+};
+
+
+/**
+ * Clears the values in the tilt field.
+ */
+sketchology.proto.SInput.prototype.clearTilt = function() {
+ this.clear$Field(9);
+};
+
+
+/**
+ * Gets the value of the orientation field.
+ * @return {?number} The value.
+ */
+sketchology.proto.SInput.prototype.getOrientation = function() {
+ return /** @type {?number} */ (this.get$Value(10));
+};
+
+
+/**
+ * Gets the value of the orientation field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.SInput.prototype.getOrientationOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(10));
+};
+
+
+/**
+ * Sets the value of the orientation field.
+ * @param {number} value The value.
+ */
+sketchology.proto.SInput.prototype.setOrientation = function(value) {
+ this.set$Value(10, value);
+};
+
+
+/**
+ * @return {boolean} Whether the orientation field has a value.
+ */
+sketchology.proto.SInput.prototype.hasOrientation = function() {
+ return this.has$Value(10);
+};
+
+
+/**
+ * @return {number} The number of values in the orientation field.
+ */
+sketchology.proto.SInput.prototype.orientationCount = function() {
+ return this.count$Values(10);
+};
+
+
+/**
+ * Clears the values in the orientation field.
+ */
+sketchology.proto.SInput.prototype.clearOrientation = function() {
+ this.clear$Field(10);
+};
+
+
+/**
+ * Enumeration InputType.
+ * @enum {number}
+ */
+sketchology.proto.SInput.InputType = {
+ UNKNOWN: 0,
+ MOUSE: 1,
+ TOUCH: 2,
+ PEN: 3,
+ ERASER: 4
+};
+
+
+
+/**
+ * Message SimulatedInput.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.SimulatedInput = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.SimulatedInput, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.SimulatedInput.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.SimulatedInput} The cloned message.
+ * @override
+ */
+sketchology.proto.SimulatedInput.prototype.clone;
+
+
+/**
+ * Gets the value of the xs field at the index given.
+ * @param {number} index The index to lookup.
+ * @return {?number} The value.
+ */
+sketchology.proto.SimulatedInput.prototype.getXs = function(index) {
+ return /** @type {?number} */ (this.get$Value(1, index));
+};
+
+
+/**
+ * Gets the value of the xs field at the index given or the default value if not set.
+ * @param {number} index The index to lookup.
+ * @return {number} The value.
+ */
+sketchology.proto.SimulatedInput.prototype.getXsOrDefault = function(index) {
+ return /** @type {number} */ (this.get$ValueOrDefault(1, index));
+};
+
+
+/**
+ * Adds a value to the xs field.
+ * @param {number} value The value to add.
+ */
+sketchology.proto.SimulatedInput.prototype.addXs = function(value) {
+ this.add$Value(1, value);
+};
+
+
+/**
+ * Returns the array of values in the xs field.
+ * @return {!Array<number>} The values in the field.
+ */
+sketchology.proto.SimulatedInput.prototype.xsArray = function() {
+ return /** @type {!Array<number>} */ (this.array$Values(1));
+};
+
+
+/**
+ * @return {boolean} Whether the xs field has a value.
+ */
+sketchology.proto.SimulatedInput.prototype.hasXs = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the xs field.
+ */
+sketchology.proto.SimulatedInput.prototype.xsCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the xs field.
+ */
+sketchology.proto.SimulatedInput.prototype.clearXs = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the ys field at the index given.
+ * @param {number} index The index to lookup.
+ * @return {?number} The value.
+ */
+sketchology.proto.SimulatedInput.prototype.getYs = function(index) {
+ return /** @type {?number} */ (this.get$Value(2, index));
+};
+
+
+/**
+ * Gets the value of the ys field at the index given or the default value if not set.
+ * @param {number} index The index to lookup.
+ * @return {number} The value.
+ */
+sketchology.proto.SimulatedInput.prototype.getYsOrDefault = function(index) {
+ return /** @type {number} */ (this.get$ValueOrDefault(2, index));
+};
+
+
+/**
+ * Adds a value to the ys field.
+ * @param {number} value The value to add.
+ */
+sketchology.proto.SimulatedInput.prototype.addYs = function(value) {
+ this.add$Value(2, value);
+};
+
+
+/**
+ * Returns the array of values in the ys field.
+ * @return {!Array<number>} The values in the field.
+ */
+sketchology.proto.SimulatedInput.prototype.ysArray = function() {
+ return /** @type {!Array<number>} */ (this.array$Values(2));
+};
+
+
+/**
+ * @return {boolean} Whether the ys field has a value.
+ */
+sketchology.proto.SimulatedInput.prototype.hasYs = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the ys field.
+ */
+sketchology.proto.SimulatedInput.prototype.ysCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the ys field.
+ */
+sketchology.proto.SimulatedInput.prototype.clearYs = function() {
+ this.clear$Field(2);
+};
+
+
+/**
+ * Gets the value of the ts_secs field at the index given.
+ * @param {number} index The index to lookup.
+ * @return {?number} The value.
+ */
+sketchology.proto.SimulatedInput.prototype.getTsSecs = function(index) {
+ return /** @type {?number} */ (this.get$Value(3, index));
+};
+
+
+/**
+ * Gets the value of the ts_secs field at the index given or the default value if not set.
+ * @param {number} index The index to lookup.
+ * @return {number} The value.
+ */
+sketchology.proto.SimulatedInput.prototype.getTsSecsOrDefault = function(index) {
+ return /** @type {number} */ (this.get$ValueOrDefault(3, index));
+};
+
+
+/**
+ * Adds a value to the ts_secs field.
+ * @param {number} value The value to add.
+ */
+sketchology.proto.SimulatedInput.prototype.addTsSecs = function(value) {
+ this.add$Value(3, value);
+};
+
+
+/**
+ * Returns the array of values in the ts_secs field.
+ * @return {!Array<number>} The values in the field.
+ */
+sketchology.proto.SimulatedInput.prototype.tsSecsArray = function() {
+ return /** @type {!Array<number>} */ (this.array$Values(3));
+};
+
+
+/**
+ * @return {boolean} Whether the ts_secs field has a value.
+ */
+sketchology.proto.SimulatedInput.prototype.hasTsSecs = function() {
+ return this.has$Value(3);
+};
+
+
+/**
+ * @return {number} The number of values in the ts_secs field.
+ */
+sketchology.proto.SimulatedInput.prototype.tsSecsCount = function() {
+ return this.count$Values(3);
+};
+
+
+/**
+ * Clears the values in the ts_secs field.
+ */
+sketchology.proto.SimulatedInput.prototype.clearTsSecs = function() {
+ this.clear$Field(3);
+};
+
+
+/**
+ * Gets the value of the source_details field.
+ * @return {?sketchology.proto.SourceDetails} The value.
+ */
+sketchology.proto.SimulatedInput.prototype.getSourceDetails = function() {
+ return /** @type {?sketchology.proto.SourceDetails} */ (this.get$Value(4));
+};
+
+
+/**
+ * Gets the value of the source_details field or the default value if not set.
+ * @return {!sketchology.proto.SourceDetails} The value.
+ */
+sketchology.proto.SimulatedInput.prototype.getSourceDetailsOrDefault = function() {
+ return /** @type {!sketchology.proto.SourceDetails} */ (this.get$ValueOrDefault(4));
+};
+
+
+/**
+ * Sets the value of the source_details field.
+ * @param {!sketchology.proto.SourceDetails} value The value.
+ */
+sketchology.proto.SimulatedInput.prototype.setSourceDetails = function(value) {
+ this.set$Value(4, value);
+};
+
+
+/**
+ * @return {boolean} Whether the source_details field has a value.
+ */
+sketchology.proto.SimulatedInput.prototype.hasSourceDetails = function() {
+ return this.has$Value(4);
+};
+
+
+/**
+ * @return {number} The number of values in the source_details field.
+ */
+sketchology.proto.SimulatedInput.prototype.sourceDetailsCount = function() {
+ return this.count$Values(4);
+};
+
+
+/**
+ * Clears the values in the source_details field.
+ */
+sketchology.proto.SimulatedInput.prototype.clearSourceDetails = function() {
+ this.clear$Field(4);
+};
+
+
+
+/**
+ * Message SequencePoint.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.SequencePoint = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.SequencePoint, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.SequencePoint.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.SequencePoint} The cloned message.
+ * @override
+ */
+sketchology.proto.SequencePoint.prototype.clone;
+
+
+/**
+ * Gets the value of the id field.
+ * @return {?number} The value.
+ */
+sketchology.proto.SequencePoint.prototype.getId = function() {
+ return /** @type {?number} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the id field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.SequencePoint.prototype.getIdOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the id field.
+ * @param {number} value The value.
+ */
+sketchology.proto.SequencePoint.prototype.setId = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the id field has a value.
+ */
+sketchology.proto.SequencePoint.prototype.hasId = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the id field.
+ */
+sketchology.proto.SequencePoint.prototype.idCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the id field.
+ */
+sketchology.proto.SequencePoint.prototype.clearId = function() {
+ this.clear$Field(1);
+};
+
+
+
+/**
+ * Message SetCallbackFlags.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.SetCallbackFlags = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.SetCallbackFlags, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.SetCallbackFlags.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.SetCallbackFlags} The cloned message.
+ * @override
+ */
+sketchology.proto.SetCallbackFlags.prototype.clone;
+
+
+/**
+ * Gets the value of the source_details field.
+ * @return {?sketchology.proto.SourceDetails} The value.
+ */
+sketchology.proto.SetCallbackFlags.prototype.getSourceDetails = function() {
+ return /** @type {?sketchology.proto.SourceDetails} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the source_details field or the default value if not set.
+ * @return {!sketchology.proto.SourceDetails} The value.
+ */
+sketchology.proto.SetCallbackFlags.prototype.getSourceDetailsOrDefault = function() {
+ return /** @type {!sketchology.proto.SourceDetails} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the source_details field.
+ * @param {!sketchology.proto.SourceDetails} value The value.
+ */
+sketchology.proto.SetCallbackFlags.prototype.setSourceDetails = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the source_details field has a value.
+ */
+sketchology.proto.SetCallbackFlags.prototype.hasSourceDetails = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the source_details field.
+ */
+sketchology.proto.SetCallbackFlags.prototype.sourceDetailsCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the source_details field.
+ */
+sketchology.proto.SetCallbackFlags.prototype.clearSourceDetails = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the callback_flags field.
+ * @return {?sketchology.proto.CallbackFlags} The value.
+ */
+sketchology.proto.SetCallbackFlags.prototype.getCallbackFlags = function() {
+ return /** @type {?sketchology.proto.CallbackFlags} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the callback_flags field or the default value if not set.
+ * @return {!sketchology.proto.CallbackFlags} The value.
+ */
+sketchology.proto.SetCallbackFlags.prototype.getCallbackFlagsOrDefault = function() {
+ return /** @type {!sketchology.proto.CallbackFlags} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the callback_flags field.
+ * @param {!sketchology.proto.CallbackFlags} value The value.
+ */
+sketchology.proto.SetCallbackFlags.prototype.setCallbackFlags = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the callback_flags field has a value.
+ */
+sketchology.proto.SetCallbackFlags.prototype.hasCallbackFlags = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the callback_flags field.
+ */
+sketchology.proto.SetCallbackFlags.prototype.callbackFlagsCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the callback_flags field.
+ */
+sketchology.proto.SetCallbackFlags.prototype.clearCallbackFlags = function() {
+ this.clear$Field(2);
+};
+
+
+
+/**
+ * Message EngineState.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.EngineState = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.EngineState, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.EngineState.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.EngineState} The cloned message.
+ * @override
+ */
+sketchology.proto.EngineState.prototype.clone;
+
+
+/**
+ * Gets the value of the camera_position field.
+ * @return {?sketchology.proto.Rect} The value.
+ */
+sketchology.proto.EngineState.prototype.getCameraPosition = function() {
+ return /** @type {?sketchology.proto.Rect} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the camera_position field or the default value if not set.
+ * @return {!sketchology.proto.Rect} The value.
+ */
+sketchology.proto.EngineState.prototype.getCameraPositionOrDefault = function() {
+ return /** @type {!sketchology.proto.Rect} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the camera_position field.
+ * @param {!sketchology.proto.Rect} value The value.
+ */
+sketchology.proto.EngineState.prototype.setCameraPosition = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the camera_position field has a value.
+ */
+sketchology.proto.EngineState.prototype.hasCameraPosition = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the camera_position field.
+ */
+sketchology.proto.EngineState.prototype.cameraPositionCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the camera_position field.
+ */
+sketchology.proto.EngineState.prototype.clearCameraPosition = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the page_bounds field.
+ * @return {?sketchology.proto.Rect} The value.
+ */
+sketchology.proto.EngineState.prototype.getPageBounds = function() {
+ return /** @type {?sketchology.proto.Rect} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the page_bounds field or the default value if not set.
+ * @return {!sketchology.proto.Rect} The value.
+ */
+sketchology.proto.EngineState.prototype.getPageBoundsOrDefault = function() {
+ return /** @type {!sketchology.proto.Rect} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the page_bounds field.
+ * @param {!sketchology.proto.Rect} value The value.
+ */
+sketchology.proto.EngineState.prototype.setPageBounds = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the page_bounds field has a value.
+ */
+sketchology.proto.EngineState.prototype.hasPageBounds = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the page_bounds field.
+ */
+sketchology.proto.EngineState.prototype.pageBoundsCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the page_bounds field.
+ */
+sketchology.proto.EngineState.prototype.clearPageBounds = function() {
+ this.clear$Field(2);
+};
+
+
+/**
+ * Gets the value of the selection_is_live field.
+ * @return {?boolean} The value.
+ */
+sketchology.proto.EngineState.prototype.getSelectionIsLive = function() {
+ return /** @type {?boolean} */ (this.get$Value(3));
+};
+
+
+/**
+ * Gets the value of the selection_is_live field or the default value if not set.
+ * @return {boolean} The value.
+ */
+sketchology.proto.EngineState.prototype.getSelectionIsLiveOrDefault = function() {
+ return /** @type {boolean} */ (this.get$ValueOrDefault(3));
+};
+
+
+/**
+ * Sets the value of the selection_is_live field.
+ * @param {boolean} value The value.
+ */
+sketchology.proto.EngineState.prototype.setSelectionIsLive = function(value) {
+ this.set$Value(3, value);
+};
+
+
+/**
+ * @return {boolean} Whether the selection_is_live field has a value.
+ */
+sketchology.proto.EngineState.prototype.hasSelectionIsLive = function() {
+ return this.has$Value(3);
+};
+
+
+/**
+ * @return {number} The number of values in the selection_is_live field.
+ */
+sketchology.proto.EngineState.prototype.selectionIsLiveCount = function() {
+ return this.count$Values(3);
+};
+
+
+/**
+ * Clears the values in the selection_is_live field.
+ */
+sketchology.proto.EngineState.prototype.clearSelectionIsLive = function() {
+ this.clear$Field(3);
+};
+
+
+
+/**
+ * Message CameraBoundsConfig.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.CameraBoundsConfig = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.CameraBoundsConfig, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.CameraBoundsConfig.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.CameraBoundsConfig} The cloned message.
+ * @override
+ */
+sketchology.proto.CameraBoundsConfig.prototype.clone;
+
+
+/**
+ * Gets the value of the margin_left_px field.
+ * @return {?number} The value.
+ */
+sketchology.proto.CameraBoundsConfig.prototype.getMarginLeftPx = function() {
+ return /** @type {?number} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the margin_left_px field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.CameraBoundsConfig.prototype.getMarginLeftPxOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the margin_left_px field.
+ * @param {number} value The value.
+ */
+sketchology.proto.CameraBoundsConfig.prototype.setMarginLeftPx = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the margin_left_px field has a value.
+ */
+sketchology.proto.CameraBoundsConfig.prototype.hasMarginLeftPx = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the margin_left_px field.
+ */
+sketchology.proto.CameraBoundsConfig.prototype.marginLeftPxCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the margin_left_px field.
+ */
+sketchology.proto.CameraBoundsConfig.prototype.clearMarginLeftPx = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the margin_right_px field.
+ * @return {?number} The value.
+ */
+sketchology.proto.CameraBoundsConfig.prototype.getMarginRightPx = function() {
+ return /** @type {?number} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the margin_right_px field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.CameraBoundsConfig.prototype.getMarginRightPxOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the margin_right_px field.
+ * @param {number} value The value.
+ */
+sketchology.proto.CameraBoundsConfig.prototype.setMarginRightPx = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the margin_right_px field has a value.
+ */
+sketchology.proto.CameraBoundsConfig.prototype.hasMarginRightPx = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the margin_right_px field.
+ */
+sketchology.proto.CameraBoundsConfig.prototype.marginRightPxCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the margin_right_px field.
+ */
+sketchology.proto.CameraBoundsConfig.prototype.clearMarginRightPx = function() {
+ this.clear$Field(2);
+};
+
+
+/**
+ * Gets the value of the margin_bottom_px field.
+ * @return {?number} The value.
+ */
+sketchology.proto.CameraBoundsConfig.prototype.getMarginBottomPx = function() {
+ return /** @type {?number} */ (this.get$Value(3));
+};
+
+
+/**
+ * Gets the value of the margin_bottom_px field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.CameraBoundsConfig.prototype.getMarginBottomPxOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(3));
+};
+
+
+/**
+ * Sets the value of the margin_bottom_px field.
+ * @param {number} value The value.
+ */
+sketchology.proto.CameraBoundsConfig.prototype.setMarginBottomPx = function(value) {
+ this.set$Value(3, value);
+};
+
+
+/**
+ * @return {boolean} Whether the margin_bottom_px field has a value.
+ */
+sketchology.proto.CameraBoundsConfig.prototype.hasMarginBottomPx = function() {
+ return this.has$Value(3);
+};
+
+
+/**
+ * @return {number} The number of values in the margin_bottom_px field.
+ */
+sketchology.proto.CameraBoundsConfig.prototype.marginBottomPxCount = function() {
+ return this.count$Values(3);
+};
+
+
+/**
+ * Clears the values in the margin_bottom_px field.
+ */
+sketchology.proto.CameraBoundsConfig.prototype.clearMarginBottomPx = function() {
+ this.clear$Field(3);
+};
+
+
+/**
+ * Gets the value of the margin_top_px field.
+ * @return {?number} The value.
+ */
+sketchology.proto.CameraBoundsConfig.prototype.getMarginTopPx = function() {
+ return /** @type {?number} */ (this.get$Value(4));
+};
+
+
+/**
+ * Gets the value of the margin_top_px field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.CameraBoundsConfig.prototype.getMarginTopPxOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(4));
+};
+
+
+/**
+ * Sets the value of the margin_top_px field.
+ * @param {number} value The value.
+ */
+sketchology.proto.CameraBoundsConfig.prototype.setMarginTopPx = function(value) {
+ this.set$Value(4, value);
+};
+
+
+/**
+ * @return {boolean} Whether the margin_top_px field has a value.
+ */
+sketchology.proto.CameraBoundsConfig.prototype.hasMarginTopPx = function() {
+ return this.has$Value(4);
+};
+
+
+/**
+ * @return {number} The number of values in the margin_top_px field.
+ */
+sketchology.proto.CameraBoundsConfig.prototype.marginTopPxCount = function() {
+ return this.count$Values(4);
+};
+
+
+/**
+ * Clears the values in the margin_top_px field.
+ */
+sketchology.proto.CameraBoundsConfig.prototype.clearMarginTopPx = function() {
+ this.clear$Field(4);
+};
+
+
+/**
+ * Gets the value of the fraction_padding field.
+ * @return {?number} The value.
+ */
+sketchology.proto.CameraBoundsConfig.prototype.getFractionPadding = function() {
+ return /** @type {?number} */ (this.get$Value(5));
+};
+
+
+/**
+ * Gets the value of the fraction_padding field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.CameraBoundsConfig.prototype.getFractionPaddingOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(5));
+};
+
+
+/**
+ * Sets the value of the fraction_padding field.
+ * @param {number} value The value.
+ */
+sketchology.proto.CameraBoundsConfig.prototype.setFractionPadding = function(value) {
+ this.set$Value(5, value);
+};
+
+
+/**
+ * @return {boolean} Whether the fraction_padding field has a value.
+ */
+sketchology.proto.CameraBoundsConfig.prototype.hasFractionPadding = function() {
+ return this.has$Value(5);
+};
+
+
+/**
+ * @return {number} The number of values in the fraction_padding field.
+ */
+sketchology.proto.CameraBoundsConfig.prototype.fractionPaddingCount = function() {
+ return this.count$Values(5);
+};
+
+
+/**
+ * Clears the values in the fraction_padding field.
+ */
+sketchology.proto.CameraBoundsConfig.prototype.clearFractionPadding = function() {
+ this.clear$Field(5);
+};
+
+
+
+/**
+ * Message ImageInfo.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.ImageInfo = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.ImageInfo, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.ImageInfo.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.ImageInfo} The cloned message.
+ * @override
+ */
+sketchology.proto.ImageInfo.prototype.clone;
+
+
+/**
+ * Gets the value of the uri field.
+ * @return {?string} The value.
+ */
+sketchology.proto.ImageInfo.prototype.getUri = function() {
+ return /** @type {?string} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the uri field or the default value if not set.
+ * @return {string} The value.
+ */
+sketchology.proto.ImageInfo.prototype.getUriOrDefault = function() {
+ return /** @type {string} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the uri field.
+ * @param {string} value The value.
+ */
+sketchology.proto.ImageInfo.prototype.setUri = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the uri field has a value.
+ */
+sketchology.proto.ImageInfo.prototype.hasUri = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the uri field.
+ */
+sketchology.proto.ImageInfo.prototype.uriCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the uri field.
+ */
+sketchology.proto.ImageInfo.prototype.clearUri = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the asset_type field.
+ * @return {?sketchology.proto.ImageInfo.AssetType} The value.
+ */
+sketchology.proto.ImageInfo.prototype.getAssetType = function() {
+ return /** @type {?sketchology.proto.ImageInfo.AssetType} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the asset_type field or the default value if not set.
+ * @return {!sketchology.proto.ImageInfo.AssetType} The value.
+ */
+sketchology.proto.ImageInfo.prototype.getAssetTypeOrDefault = function() {
+ return /** @type {!sketchology.proto.ImageInfo.AssetType} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the asset_type field.
+ * @param {!sketchology.proto.ImageInfo.AssetType} value The value.
+ */
+sketchology.proto.ImageInfo.prototype.setAssetType = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the asset_type field has a value.
+ */
+sketchology.proto.ImageInfo.prototype.hasAssetType = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the asset_type field.
+ */
+sketchology.proto.ImageInfo.prototype.assetTypeCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the asset_type field.
+ */
+sketchology.proto.ImageInfo.prototype.clearAssetType = function() {
+ this.clear$Field(2);
+};
+
+
+/**
+ * Enumeration AssetType.
+ * @enum {number}
+ */
+sketchology.proto.ImageInfo.AssetType = {
+ DEFAULT: 0,
+ BORDER: 1,
+ STICKER: 2,
+ GRID: 3
+};
+
+
+
+/**
+ * Message ImageRect.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.ImageRect = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.ImageRect, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.ImageRect.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.ImageRect} The cloned message.
+ * @override
+ */
+sketchology.proto.ImageRect.prototype.clone;
+
+
+/**
+ * Gets the value of the rect field.
+ * @return {?sketchology.proto.Rect} The value.
+ */
+sketchology.proto.ImageRect.prototype.getRect = function() {
+ return /** @type {?sketchology.proto.Rect} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the rect field or the default value if not set.
+ * @return {!sketchology.proto.Rect} The value.
+ */
+sketchology.proto.ImageRect.prototype.getRectOrDefault = function() {
+ return /** @type {!sketchology.proto.Rect} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the rect field.
+ * @param {!sketchology.proto.Rect} value The value.
+ */
+sketchology.proto.ImageRect.prototype.setRect = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the rect field has a value.
+ */
+sketchology.proto.ImageRect.prototype.hasRect = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the rect field.
+ */
+sketchology.proto.ImageRect.prototype.rectCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the rect field.
+ */
+sketchology.proto.ImageRect.prototype.clearRect = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the bitmap_uri field.
+ * @return {?string} The value.
+ */
+sketchology.proto.ImageRect.prototype.getBitmapUri = function() {
+ return /** @type {?string} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the bitmap_uri field or the default value if not set.
+ * @return {string} The value.
+ */
+sketchology.proto.ImageRect.prototype.getBitmapUriOrDefault = function() {
+ return /** @type {string} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the bitmap_uri field.
+ * @param {string} value The value.
+ */
+sketchology.proto.ImageRect.prototype.setBitmapUri = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the bitmap_uri field has a value.
+ */
+sketchology.proto.ImageRect.prototype.hasBitmapUri = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the bitmap_uri field.
+ */
+sketchology.proto.ImageRect.prototype.bitmapUriCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the bitmap_uri field.
+ */
+sketchology.proto.ImageRect.prototype.clearBitmapUri = function() {
+ this.clear$Field(2);
+};
+
+
+/**
+ * Gets the value of the attributes field.
+ * @return {?sketchology.proto.ElementAttributes} The value.
+ */
+sketchology.proto.ImageRect.prototype.getAttributes = function() {
+ return /** @type {?sketchology.proto.ElementAttributes} */ (this.get$Value(3));
+};
+
+
+/**
+ * Gets the value of the attributes field or the default value if not set.
+ * @return {!sketchology.proto.ElementAttributes} The value.
+ */
+sketchology.proto.ImageRect.prototype.getAttributesOrDefault = function() {
+ return /** @type {!sketchology.proto.ElementAttributes} */ (this.get$ValueOrDefault(3));
+};
+
+
+/**
+ * Sets the value of the attributes field.
+ * @param {!sketchology.proto.ElementAttributes} value The value.
+ */
+sketchology.proto.ImageRect.prototype.setAttributes = function(value) {
+ this.set$Value(3, value);
+};
+
+
+/**
+ * @return {boolean} Whether the attributes field has a value.
+ */
+sketchology.proto.ImageRect.prototype.hasAttributes = function() {
+ return this.has$Value(3);
+};
+
+
+/**
+ * @return {number} The number of values in the attributes field.
+ */
+sketchology.proto.ImageRect.prototype.attributesCount = function() {
+ return this.count$Values(3);
+};
+
+
+/**
+ * Clears the values in the attributes field.
+ */
+sketchology.proto.ImageRect.prototype.clearAttributes = function() {
+ this.clear$Field(3);
+};
+
+
+/**
+ * Gets the value of the rotation_radians field.
+ * @return {?number} The value.
+ */
+sketchology.proto.ImageRect.prototype.getRotationRadians = function() {
+ return /** @type {?number} */ (this.get$Value(4));
+};
+
+
+/**
+ * Gets the value of the rotation_radians field or the default value if not set.
+ * @return {number} The value.
+ */
+sketchology.proto.ImageRect.prototype.getRotationRadiansOrDefault = function() {
+ return /** @type {number} */ (this.get$ValueOrDefault(4));
+};
+
+
+/**
+ * Sets the value of the rotation_radians field.
+ * @param {number} value The value.
+ */
+sketchology.proto.ImageRect.prototype.setRotationRadians = function(value) {
+ this.set$Value(4, value);
+};
+
+
+/**
+ * @return {boolean} Whether the rotation_radians field has a value.
+ */
+sketchology.proto.ImageRect.prototype.hasRotationRadians = function() {
+ return this.has$Value(4);
+};
+
+
+/**
+ * @return {number} The number of values in the rotation_radians field.
+ */
+sketchology.proto.ImageRect.prototype.rotationRadiansCount = function() {
+ return this.count$Values(4);
+};
+
+
+/**
+ * Clears the values in the rotation_radians field.
+ */
+sketchology.proto.ImageRect.prototype.clearRotationRadians = function() {
+ this.clear$Field(4);
+};
+
+
+
+/**
+ * Message GridInfo.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.GridInfo = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.GridInfo, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.GridInfo.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.GridInfo} The cloned message.
+ * @override
+ */
+sketchology.proto.GridInfo.prototype.clone;
+
+
+/**
+ * Gets the value of the uri field.
+ * @return {?string} The value.
+ */
+sketchology.proto.GridInfo.prototype.getUri = function() {
+ return /** @type {?string} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the uri field or the default value if not set.
+ * @return {string} The value.
+ */
+sketchology.proto.GridInfo.prototype.getUriOrDefault = function() {
+ return /** @type {string} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the uri field.
+ * @param {string} value The value.
+ */
+sketchology.proto.GridInfo.prototype.setUri = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the uri field has a value.
+ */
+sketchology.proto.GridInfo.prototype.hasUri = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the uri field.
+ */
+sketchology.proto.GridInfo.prototype.uriCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the uri field.
+ */
+sketchology.proto.GridInfo.prototype.clearUri = function() {
+ this.clear$Field(1);
+};
+
+
+
+/**
+ * Message CreateDocument.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.CreateDocument = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.CreateDocument, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.CreateDocument.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.CreateDocument} The cloned message.
+ * @override
+ */
+sketchology.proto.CreateDocument.prototype.clone;
+
+
+/**
+ * Gets the value of the document_type field.
+ * @return {?sketchology.proto.DocumentType} The value.
+ */
+sketchology.proto.CreateDocument.prototype.getDocumentType = function() {
+ return /** @type {?sketchology.proto.DocumentType} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the document_type field or the default value if not set.
+ * @return {!sketchology.proto.DocumentType} The value.
+ */
+sketchology.proto.CreateDocument.prototype.getDocumentTypeOrDefault = function() {
+ return /** @type {!sketchology.proto.DocumentType} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the document_type field.
+ * @param {!sketchology.proto.DocumentType} value The value.
+ */
+sketchology.proto.CreateDocument.prototype.setDocumentType = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the document_type field has a value.
+ */
+sketchology.proto.CreateDocument.prototype.hasDocumentType = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the document_type field.
+ */
+sketchology.proto.CreateDocument.prototype.documentTypeCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the document_type field.
+ */
+sketchology.proto.CreateDocument.prototype.clearDocumentType = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the storage_type field.
+ * @return {?sketchology.proto.StorageType} The value.
+ */
+sketchology.proto.CreateDocument.prototype.getStorageType = function() {
+ return /** @type {?sketchology.proto.StorageType} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the storage_type field or the default value if not set.
+ * @return {!sketchology.proto.StorageType} The value.
+ */
+sketchology.proto.CreateDocument.prototype.getStorageTypeOrDefault = function() {
+ return /** @type {!sketchology.proto.StorageType} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the storage_type field.
+ * @param {!sketchology.proto.StorageType} value The value.
+ */
+sketchology.proto.CreateDocument.prototype.setStorageType = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the storage_type field has a value.
+ */
+sketchology.proto.CreateDocument.prototype.hasStorageType = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the storage_type field.
+ */
+sketchology.proto.CreateDocument.prototype.storageTypeCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the storage_type field.
+ */
+sketchology.proto.CreateDocument.prototype.clearStorageType = function() {
+ this.clear$Field(2);
+};
+
+
+/**
+ * Gets the value of the storage_path field.
+ * @return {?string} The value.
+ */
+sketchology.proto.CreateDocument.prototype.getStoragePath = function() {
+ return /** @type {?string} */ (this.get$Value(3));
+};
+
+
+/**
+ * Gets the value of the storage_path field or the default value if not set.
+ * @return {string} The value.
+ */
+sketchology.proto.CreateDocument.prototype.getStoragePathOrDefault = function() {
+ return /** @type {string} */ (this.get$ValueOrDefault(3));
+};
+
+
+/**
+ * Sets the value of the storage_path field.
+ * @param {string} value The value.
+ */
+sketchology.proto.CreateDocument.prototype.setStoragePath = function(value) {
+ this.set$Value(3, value);
+};
+
+
+/**
+ * @return {boolean} Whether the storage_path field has a value.
+ */
+sketchology.proto.CreateDocument.prototype.hasStoragePath = function() {
+ return this.has$Value(3);
+};
+
+
+/**
+ * @return {number} The number of values in the storage_path field.
+ */
+sketchology.proto.CreateDocument.prototype.storagePathCount = function() {
+ return this.count$Values(3);
+};
+
+
+/**
+ * Clears the values in the storage_path field.
+ */
+sketchology.proto.CreateDocument.prototype.clearStoragePath = function() {
+ this.clear$Field(3);
+};
+
+
+/**
+ * Gets the value of the snapshot field.
+ * @return {?sketchology.proto.Snapshot} The value.
+ */
+sketchology.proto.CreateDocument.prototype.getSnapshot = function() {
+ return /** @type {?sketchology.proto.Snapshot} */ (this.get$Value(4));
+};
+
+
+/**
+ * Gets the value of the snapshot field or the default value if not set.
+ * @return {!sketchology.proto.Snapshot} The value.
+ */
+sketchology.proto.CreateDocument.prototype.getSnapshotOrDefault = function() {
+ return /** @type {!sketchology.proto.Snapshot} */ (this.get$ValueOrDefault(4));
+};
+
+
+/**
+ * Sets the value of the snapshot field.
+ * @param {!sketchology.proto.Snapshot} value The value.
+ */
+sketchology.proto.CreateDocument.prototype.setSnapshot = function(value) {
+ this.set$Value(4, value);
+};
+
+
+/**
+ * @return {boolean} Whether the snapshot field has a value.
+ */
+sketchology.proto.CreateDocument.prototype.hasSnapshot = function() {
+ return this.has$Value(4);
+};
+
+
+/**
+ * @return {number} The number of values in the snapshot field.
+ */
+sketchology.proto.CreateDocument.prototype.snapshotCount = function() {
+ return this.count$Values(4);
+};
+
+
+/**
+ * Clears the values in the snapshot field.
+ */
+sketchology.proto.CreateDocument.prototype.clearSnapshot = function() {
+ this.clear$Field(4);
+};
+
+
+
+/**
+ * Message AddPath.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.AddPath = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.AddPath, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.AddPath.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.AddPath} The cloned message.
+ * @override
+ */
+sketchology.proto.AddPath.prototype.clone;
+
+
+/**
+ * Gets the value of the path field.
+ * @return {?sketchology.proto.Path} The value.
+ */
+sketchology.proto.AddPath.prototype.getPath = function() {
+ return /** @type {?sketchology.proto.Path} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the path field or the default value if not set.
+ * @return {!sketchology.proto.Path} The value.
+ */
+sketchology.proto.AddPath.prototype.getPathOrDefault = function() {
+ return /** @type {!sketchology.proto.Path} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the path field.
+ * @param {!sketchology.proto.Path} value The value.
+ */
+sketchology.proto.AddPath.prototype.setPath = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the path field has a value.
+ */
+sketchology.proto.AddPath.prototype.hasPath = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the path field.
+ */
+sketchology.proto.AddPath.prototype.pathCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the path field.
+ */
+sketchology.proto.AddPath.prototype.clearPath = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the uuid field.
+ * @return {?string} The value.
+ */
+sketchology.proto.AddPath.prototype.getUuid = function() {
+ return /** @type {?string} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the uuid field or the default value if not set.
+ * @return {string} The value.
+ */
+sketchology.proto.AddPath.prototype.getUuidOrDefault = function() {
+ return /** @type {string} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the uuid field.
+ * @param {string} value The value.
+ */
+sketchology.proto.AddPath.prototype.setUuid = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the uuid field has a value.
+ */
+sketchology.proto.AddPath.prototype.hasUuid = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the uuid field.
+ */
+sketchology.proto.AddPath.prototype.uuidCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the uuid field.
+ */
+sketchology.proto.AddPath.prototype.clearUuid = function() {
+ this.clear$Field(2);
+};
+
+
+
+/**
+ * Message PusherPositionUpdate.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.PusherPositionUpdate = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.PusherPositionUpdate, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.PusherPositionUpdate.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.PusherPositionUpdate} The cloned message.
+ * @override
+ */
+sketchology.proto.PusherPositionUpdate.prototype.clone;
+
+
+/**
+ * Gets the value of the uuid field.
+ * @return {?string} The value.
+ */
+sketchology.proto.PusherPositionUpdate.prototype.getUuid = function() {
+ return /** @type {?string} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the uuid field or the default value if not set.
+ * @return {string} The value.
+ */
+sketchology.proto.PusherPositionUpdate.prototype.getUuidOrDefault = function() {
+ return /** @type {string} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the uuid field.
+ * @param {string} value The value.
+ */
+sketchology.proto.PusherPositionUpdate.prototype.setUuid = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the uuid field has a value.
+ */
+sketchology.proto.PusherPositionUpdate.prototype.hasUuid = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the uuid field.
+ */
+sketchology.proto.PusherPositionUpdate.prototype.uuidCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the uuid field.
+ */
+sketchology.proto.PusherPositionUpdate.prototype.clearUuid = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the pointer_location field at the index given.
+ * @param {number} index The index to lookup.
+ * @return {?sketchology.proto.Point} The value.
+ */
+sketchology.proto.PusherPositionUpdate.prototype.getPointerLocation = function(index) {
+ return /** @type {?sketchology.proto.Point} */ (this.get$Value(2, index));
+};
+
+
+/**
+ * Gets the value of the pointer_location field at the index given or the default value if not set.
+ * @param {number} index The index to lookup.
+ * @return {!sketchology.proto.Point} The value.
+ */
+sketchology.proto.PusherPositionUpdate.prototype.getPointerLocationOrDefault = function(index) {
+ return /** @type {!sketchology.proto.Point} */ (this.get$ValueOrDefault(2, index));
+};
+
+
+/**
+ * Adds a value to the pointer_location field.
+ * @param {!sketchology.proto.Point} value The value to add.
+ */
+sketchology.proto.PusherPositionUpdate.prototype.addPointerLocation = function(value) {
+ this.add$Value(2, value);
+};
+
+
+/**
+ * Returns the array of values in the pointer_location field.
+ * @return {!Array<!sketchology.proto.Point>} The values in the field.
+ */
+sketchology.proto.PusherPositionUpdate.prototype.pointerLocationArray = function() {
+ return /** @type {!Array<!sketchology.proto.Point>} */ (this.array$Values(2));
+};
+
+
+/**
+ * @return {boolean} Whether the pointer_location field has a value.
+ */
+sketchology.proto.PusherPositionUpdate.prototype.hasPointerLocation = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the pointer_location field.
+ */
+sketchology.proto.PusherPositionUpdate.prototype.pointerLocationCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the pointer_location field.
+ */
+sketchology.proto.PusherPositionUpdate.prototype.clearPointerLocation = function() {
+ this.clear$Field(2);
+};
+
+
+
+/**
+ * Message ElementQueryData.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.ElementQueryData = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.ElementQueryData, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.ElementQueryData.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.ElementQueryData} The cloned message.
+ * @override
+ */
+sketchology.proto.ElementQueryData.prototype.clone;
+
+
+/**
+ * Gets the value of the item field at the index given.
+ * @param {number} index The index to lookup.
+ * @return {?sketchology.proto.ElementQueryItem} The value.
+ */
+sketchology.proto.ElementQueryData.prototype.getItem = function(index) {
+ return /** @type {?sketchology.proto.ElementQueryItem} */ (this.get$Value(1, index));
+};
+
+
+/**
+ * Gets the value of the item field at the index given or the default value if not set.
+ * @param {number} index The index to lookup.
+ * @return {!sketchology.proto.ElementQueryItem} The value.
+ */
+sketchology.proto.ElementQueryData.prototype.getItemOrDefault = function(index) {
+ return /** @type {!sketchology.proto.ElementQueryItem} */ (this.get$ValueOrDefault(1, index));
+};
+
+
+/**
+ * Adds a value to the item field.
+ * @param {!sketchology.proto.ElementQueryItem} value The value to add.
+ */
+sketchology.proto.ElementQueryData.prototype.addItem = function(value) {
+ this.add$Value(1, value);
+};
+
+
+/**
+ * Returns the array of values in the item field.
+ * @return {!Array<!sketchology.proto.ElementQueryItem>} The values in the field.
+ */
+sketchology.proto.ElementQueryData.prototype.itemArray = function() {
+ return /** @type {!Array<!sketchology.proto.ElementQueryItem>} */ (this.array$Values(1));
+};
+
+
+/**
+ * @return {boolean} Whether the item field has a value.
+ */
+sketchology.proto.ElementQueryData.prototype.hasItem = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the item field.
+ */
+sketchology.proto.ElementQueryData.prototype.itemCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the item field.
+ */
+sketchology.proto.ElementQueryData.prototype.clearItem = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the up_world_location field.
+ * @return {?sketchology.proto.Point} The value.
+ */
+sketchology.proto.ElementQueryData.prototype.getUpWorldLocation = function() {
+ return /** @type {?sketchology.proto.Point} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the up_world_location field or the default value if not set.
+ * @return {!sketchology.proto.Point} The value.
+ */
+sketchology.proto.ElementQueryData.prototype.getUpWorldLocationOrDefault = function() {
+ return /** @type {!sketchology.proto.Point} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the up_world_location field.
+ * @param {!sketchology.proto.Point} value The value.
+ */
+sketchology.proto.ElementQueryData.prototype.setUpWorldLocation = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the up_world_location field has a value.
+ */
+sketchology.proto.ElementQueryData.prototype.hasUpWorldLocation = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the up_world_location field.
+ */
+sketchology.proto.ElementQueryData.prototype.upWorldLocationCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the up_world_location field.
+ */
+sketchology.proto.ElementQueryData.prototype.clearUpWorldLocation = function() {
+ this.clear$Field(2);
+};
+
+
+/**
+ * Gets the value of the down_world_location field.
+ * @return {?sketchology.proto.Point} The value.
+ */
+sketchology.proto.ElementQueryData.prototype.getDownWorldLocation = function() {
+ return /** @type {?sketchology.proto.Point} */ (this.get$Value(3));
+};
+
+
+/**
+ * Gets the value of the down_world_location field or the default value if not set.
+ * @return {!sketchology.proto.Point} The value.
+ */
+sketchology.proto.ElementQueryData.prototype.getDownWorldLocationOrDefault = function() {
+ return /** @type {!sketchology.proto.Point} */ (this.get$ValueOrDefault(3));
+};
+
+
+/**
+ * Sets the value of the down_world_location field.
+ * @param {!sketchology.proto.Point} value The value.
+ */
+sketchology.proto.ElementQueryData.prototype.setDownWorldLocation = function(value) {
+ this.set$Value(3, value);
+};
+
+
+/**
+ * @return {boolean} Whether the down_world_location field has a value.
+ */
+sketchology.proto.ElementQueryData.prototype.hasDownWorldLocation = function() {
+ return this.has$Value(3);
+};
+
+
+/**
+ * @return {number} The number of values in the down_world_location field.
+ */
+sketchology.proto.ElementQueryData.prototype.downWorldLocationCount = function() {
+ return this.count$Values(3);
+};
+
+
+/**
+ * Clears the values in the down_world_location field.
+ */
+sketchology.proto.ElementQueryData.prototype.clearDownWorldLocation = function() {
+ this.clear$Field(3);
+};
+
+
+
+/**
+ * Message ElementQueryItem.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.ElementQueryItem = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.ElementQueryItem, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.ElementQueryItem.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.ElementQueryItem} The cloned message.
+ * @override
+ */
+sketchology.proto.ElementQueryItem.prototype.clone;
+
+
+/**
+ * Gets the value of the uuid field.
+ * @return {?string} The value.
+ */
+sketchology.proto.ElementQueryItem.prototype.getUuid = function() {
+ return /** @type {?string} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the uuid field or the default value if not set.
+ * @return {string} The value.
+ */
+sketchology.proto.ElementQueryItem.prototype.getUuidOrDefault = function() {
+ return /** @type {string} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the uuid field.
+ * @param {string} value The value.
+ */
+sketchology.proto.ElementQueryItem.prototype.setUuid = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the uuid field has a value.
+ */
+sketchology.proto.ElementQueryItem.prototype.hasUuid = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the uuid field.
+ */
+sketchology.proto.ElementQueryItem.prototype.uuidCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the uuid field.
+ */
+sketchology.proto.ElementQueryItem.prototype.clearUuid = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the world_bounds field.
+ * @return {?sketchology.proto.Rect} The value.
+ */
+sketchology.proto.ElementQueryItem.prototype.getWorldBounds = function() {
+ return /** @type {?sketchology.proto.Rect} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the world_bounds field or the default value if not set.
+ * @return {!sketchology.proto.Rect} The value.
+ */
+sketchology.proto.ElementQueryItem.prototype.getWorldBoundsOrDefault = function() {
+ return /** @type {!sketchology.proto.Rect} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the world_bounds field.
+ * @param {!sketchology.proto.Rect} value The value.
+ */
+sketchology.proto.ElementQueryItem.prototype.setWorldBounds = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the world_bounds field has a value.
+ */
+sketchology.proto.ElementQueryItem.prototype.hasWorldBounds = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the world_bounds field.
+ */
+sketchology.proto.ElementQueryItem.prototype.worldBoundsCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the world_bounds field.
+ */
+sketchology.proto.ElementQueryItem.prototype.clearWorldBounds = function() {
+ this.clear$Field(2);
+};
+
+
+/**
+ * Gets the value of the uri field.
+ * @return {?string} The value.
+ */
+sketchology.proto.ElementQueryItem.prototype.getUri = function() {
+ return /** @type {?string} */ (this.get$Value(3));
+};
+
+
+/**
+ * Gets the value of the uri field or the default value if not set.
+ * @return {string} The value.
+ */
+sketchology.proto.ElementQueryItem.prototype.getUriOrDefault = function() {
+ return /** @type {string} */ (this.get$ValueOrDefault(3));
+};
+
+
+/**
+ * Sets the value of the uri field.
+ * @param {string} value The value.
+ */
+sketchology.proto.ElementQueryItem.prototype.setUri = function(value) {
+ this.set$Value(3, value);
+};
+
+
+/**
+ * @return {boolean} Whether the uri field has a value.
+ */
+sketchology.proto.ElementQueryItem.prototype.hasUri = function() {
+ return this.has$Value(3);
+};
+
+
+/**
+ * @return {number} The number of values in the uri field.
+ */
+sketchology.proto.ElementQueryItem.prototype.uriCount = function() {
+ return this.count$Values(3);
+};
+
+
+/**
+ * Clears the values in the uri field.
+ */
+sketchology.proto.ElementQueryItem.prototype.clearUri = function() {
+ this.clear$Field(3);
+};
+
+
+
+/**
+ * Message SelectionState.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.SelectionState = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.SelectionState, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.SelectionState.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.SelectionState} The cloned message.
+ * @override
+ */
+sketchology.proto.SelectionState.prototype.clone;
+
+
+/**
+ * Gets the value of the anything_selected field.
+ * @return {?boolean} The value.
+ */
+sketchology.proto.SelectionState.prototype.getAnythingSelected = function() {
+ return /** @type {?boolean} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the anything_selected field or the default value if not set.
+ * @return {boolean} The value.
+ */
+sketchology.proto.SelectionState.prototype.getAnythingSelectedOrDefault = function() {
+ return /** @type {boolean} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the anything_selected field.
+ * @param {boolean} value The value.
+ */
+sketchology.proto.SelectionState.prototype.setAnythingSelected = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the anything_selected field has a value.
+ */
+sketchology.proto.SelectionState.prototype.hasAnythingSelected = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the anything_selected field.
+ */
+sketchology.proto.SelectionState.prototype.anythingSelectedCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the anything_selected field.
+ */
+sketchology.proto.SelectionState.prototype.clearAnythingSelected = function() {
+ this.clear$Field(1);
+};
+
+
+
+/**
+ * Message ToolEvent.
+ * @constructor
+ * @extends {goog.proto2.Message}
+ * @final
+ */
+sketchology.proto.ToolEvent = function() {
+ goog.proto2.Message.call(this);
+};
+goog.inherits(sketchology.proto.ToolEvent, goog.proto2.Message);
+
+
+/**
+ * Descriptor for this message, deserialized lazily in getDescriptor().
+ * @private {?goog.proto2.Descriptor}
+ */
+sketchology.proto.ToolEvent.descriptor_ = null;
+
+
+/**
+ * Overrides {@link goog.proto2.Message#clone} to specify its exact return type.
+ * @return {!sketchology.proto.ToolEvent} The cloned message.
+ * @override
+ */
+sketchology.proto.ToolEvent.prototype.clone;
+
+
+/**
+ * Gets the value of the pusher_position_update field.
+ * @return {?sketchology.proto.PusherPositionUpdate} The value.
+ */
+sketchology.proto.ToolEvent.prototype.getPusherPositionUpdate = function() {
+ return /** @type {?sketchology.proto.PusherPositionUpdate} */ (this.get$Value(1));
+};
+
+
+/**
+ * Gets the value of the pusher_position_update field or the default value if not set.
+ * @return {!sketchology.proto.PusherPositionUpdate} The value.
+ */
+sketchology.proto.ToolEvent.prototype.getPusherPositionUpdateOrDefault = function() {
+ return /** @type {!sketchology.proto.PusherPositionUpdate} */ (this.get$ValueOrDefault(1));
+};
+
+
+/**
+ * Sets the value of the pusher_position_update field.
+ * @param {!sketchology.proto.PusherPositionUpdate} value The value.
+ */
+sketchology.proto.ToolEvent.prototype.setPusherPositionUpdate = function(value) {
+ this.set$Value(1, value);
+};
+
+
+/**
+ * @return {boolean} Whether the pusher_position_update field has a value.
+ */
+sketchology.proto.ToolEvent.prototype.hasPusherPositionUpdate = function() {
+ return this.has$Value(1);
+};
+
+
+/**
+ * @return {number} The number of values in the pusher_position_update field.
+ */
+sketchology.proto.ToolEvent.prototype.pusherPositionUpdateCount = function() {
+ return this.count$Values(1);
+};
+
+
+/**
+ * Clears the values in the pusher_position_update field.
+ */
+sketchology.proto.ToolEvent.prototype.clearPusherPositionUpdate = function() {
+ this.clear$Field(1);
+};
+
+
+/**
+ * Gets the value of the element_query_data field.
+ * @return {?sketchology.proto.ElementQueryData} The value.
+ */
+sketchology.proto.ToolEvent.prototype.getElementQueryData = function() {
+ return /** @type {?sketchology.proto.ElementQueryData} */ (this.get$Value(2));
+};
+
+
+/**
+ * Gets the value of the element_query_data field or the default value if not set.
+ * @return {!sketchology.proto.ElementQueryData} The value.
+ */
+sketchology.proto.ToolEvent.prototype.getElementQueryDataOrDefault = function() {
+ return /** @type {!sketchology.proto.ElementQueryData} */ (this.get$ValueOrDefault(2));
+};
+
+
+/**
+ * Sets the value of the element_query_data field.
+ * @param {!sketchology.proto.ElementQueryData} value The value.
+ */
+sketchology.proto.ToolEvent.prototype.setElementQueryData = function(value) {
+ this.set$Value(2, value);
+};
+
+
+/**
+ * @return {boolean} Whether the element_query_data field has a value.
+ */
+sketchology.proto.ToolEvent.prototype.hasElementQueryData = function() {
+ return this.has$Value(2);
+};
+
+
+/**
+ * @return {number} The number of values in the element_query_data field.
+ */
+sketchology.proto.ToolEvent.prototype.elementQueryDataCount = function() {
+ return this.count$Values(2);
+};
+
+
+/**
+ * Clears the values in the element_query_data field.
+ */
+sketchology.proto.ToolEvent.prototype.clearElementQueryData = function() {
+ this.clear$Field(2);
+};
+
+
+/**
+ * Gets the value of the selection_state field.
+ * @return {?sketchology.proto.SelectionState} The value.
+ */
+sketchology.proto.ToolEvent.prototype.getSelectionState = function() {
+ return /** @type {?sketchology.proto.SelectionState} */ (this.get$Value(3));
+};
+
+
+/**
+ * Gets the value of the selection_state field or the default value if not set.
+ * @return {!sketchology.proto.SelectionState} The value.
+ */
+sketchology.proto.ToolEvent.prototype.getSelectionStateOrDefault = function() {
+ return /** @type {!sketchology.proto.SelectionState} */ (this.get$ValueOrDefault(3));
+};
+
+
+/**
+ * Sets the value of the selection_state field.
+ * @param {!sketchology.proto.SelectionState} value The value.
+ */
+sketchology.proto.ToolEvent.prototype.setSelectionState = function(value) {
+ this.set$Value(3, value);
+};
+
+
+/**
+ * @return {boolean} Whether the selection_state field has a value.
+ */
+sketchology.proto.ToolEvent.prototype.hasSelectionState = function() {
+ return this.has$Value(3);
+};
+
+
+/**
+ * @return {number} The number of values in the selection_state field.
+ */
+sketchology.proto.ToolEvent.prototype.selectionStateCount = function() {
+ return this.count$Values(3);
+};
+
+
+/**
+ * Clears the values in the selection_state field.
+ */
+sketchology.proto.ToolEvent.prototype.clearSelectionState = function() {
+ this.clear$Field(3);
+};
+
+
+/** @override */
+sketchology.proto.Command.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.Command.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'Command',
+ fullName: 'sketchology.proto.Command'
+ },
+ 1: {
+ name: 'set_viewport',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.Viewport
+ },
+ 2: {
+ name: 'tool_params',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.ToolParams
+ },
+ 3: {
+ name: 'add_path',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.AddPath
+ },
+ 4: {
+ name: 'camera_position',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.Rect
+ },
+ 5: {
+ name: 'page_bounds',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.Rect
+ },
+ 6: {
+ name: 'image_export',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.ImageExport
+ },
+ 7: {
+ name: 'flag_assignment',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.FlagAssignment
+ },
+ 8: {
+ name: 'set_element_transforms',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.ElementMutation
+ },
+ 9: {
+ name: 'add_element',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.AddElement
+ },
+ 10: {
+ name: 'background_image',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.BackgroundImageInfo
+ },
+ 11: {
+ name: 'background_color',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.BackgroundColor
+ },
+ 12: {
+ name: 'set_out_of_bounds_color',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.OutOfBoundsColor
+ },
+ 13: {
+ name: 'set_page_border',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.Border
+ },
+ 14: {
+ name: 'send_input_stream',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.SInputStream
+ },
+ 15: {
+ name: 'sequence_point',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.SequencePoint
+ },
+ 16: {
+ name: 'set_callback_flags',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.SetCallbackFlags
+ },
+ 17: {
+ name: 'set_camera_bounds_config',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.CameraBoundsConfig
+ },
+ 18: {
+ name: 'deselect_all',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.NoArgCommand
+ },
+ 19: {
+ name: 'add_image_rect',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.ImageRect
+ },
+ 21: {
+ name: 'clear',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.NoArgCommand
+ },
+ 22: {
+ name: 'remove_all_elements',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.NoArgCommand
+ },
+ 23: {
+ name: 'undo',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.NoArgCommand
+ },
+ 24: {
+ name: 'redo',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.NoArgCommand
+ },
+ 25: {
+ name: 'evict_image_data',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.EvictImageData
+ },
+ 26: {
+ name: 'replace_elements',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.ReplaceElementsCommand
+ },
+ 27: {
+ name: 'commit_crop',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.NoArgCommand
+ },
+ 28: {
+ name: 'element_animation',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.ElementAnimation
+ },
+ 29: {
+ name: 'set_grid',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.GridInfo
+ },
+ 30: {
+ name: 'clear_grid',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.NoArgCommand
+ }
+ };
+ sketchology.proto.Command.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.Command, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.Command.getDescriptor =
+ sketchology.proto.Command.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.CommandList.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.CommandList.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'CommandList',
+ fullName: 'sketchology.proto.CommandList'
+ },
+ 1: {
+ name: 'commands',
+ repeated: true,
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.Command
+ }
+ };
+ sketchology.proto.CommandList.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.CommandList, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.CommandList.getDescriptor =
+ sketchology.proto.CommandList.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.NoArgCommand.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.NoArgCommand.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'NoArgCommand',
+ fullName: 'sketchology.proto.NoArgCommand'
+ }
+ };
+ sketchology.proto.NoArgCommand.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.NoArgCommand, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.NoArgCommand.getDescriptor =
+ sketchology.proto.NoArgCommand.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.ReplaceElementsCommand.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.ReplaceElementsCommand.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'ReplaceElementsCommand',
+ fullName: 'sketchology.proto.ReplaceElementsCommand'
+ },
+ 1: {
+ name: 'uuids_to_remove',
+ repeated: true,
+ fieldType: goog.proto2.Message.FieldType.STRING,
+ type: String
+ },
+ 2: {
+ name: 'paths_to_add',
+ repeated: true,
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.Path
+ }
+ };
+ sketchology.proto.ReplaceElementsCommand.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.ReplaceElementsCommand, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.ReplaceElementsCommand.getDescriptor =
+ sketchology.proto.ReplaceElementsCommand.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.EvictImageData.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.EvictImageData.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'EvictImageData',
+ fullName: 'sketchology.proto.EvictImageData'
+ },
+ 1: {
+ name: 'uri',
+ fieldType: goog.proto2.Message.FieldType.STRING,
+ type: String
+ }
+ };
+ sketchology.proto.EvictImageData.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.EvictImageData, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.EvictImageData.getDescriptor =
+ sketchology.proto.EvictImageData.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.Viewport.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.Viewport.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'Viewport',
+ fullName: 'sketchology.proto.Viewport'
+ },
+ 1: {
+ name: 'fbo_handle',
+ fieldType: goog.proto2.Message.FieldType.UINT32,
+ type: Number
+ },
+ 2: {
+ name: 'width',
+ fieldType: goog.proto2.Message.FieldType.UINT32,
+ type: Number
+ },
+ 3: {
+ name: 'height',
+ fieldType: goog.proto2.Message.FieldType.UINT32,
+ type: Number
+ },
+ 4: {
+ name: 'ppi',
+ fieldType: goog.proto2.Message.FieldType.FLOAT,
+ type: Number
+ }
+ };
+ sketchology.proto.Viewport.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.Viewport, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.Viewport.getDescriptor =
+ sketchology.proto.Viewport.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.ImageExport.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.ImageExport.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'ImageExport',
+ fullName: 'sketchology.proto.ImageExport'
+ },
+ 1: {
+ name: 'max_dimension_px',
+ fieldType: goog.proto2.Message.FieldType.UINT32,
+ defaultValue: 1024,
+ type: Number
+ },
+ 2: {
+ name: 'should_draw_background',
+ fieldType: goog.proto2.Message.FieldType.BOOL,
+ defaultValue: true,
+ type: Boolean
+ }
+ };
+ sketchology.proto.ImageExport.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.ImageExport, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.ImageExport.getDescriptor =
+ sketchology.proto.ImageExport.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.LinearPathAnimation.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.LinearPathAnimation.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'LinearPathAnimation',
+ fullName: 'sketchology.proto.LinearPathAnimation'
+ },
+ 1: {
+ name: 'rgba_from',
+ fieldType: goog.proto2.Message.FieldType.UINT32,
+ type: Number
+ },
+ 2: {
+ name: 'rgba_seconds',
+ fieldType: goog.proto2.Message.FieldType.DOUBLE,
+ type: Number
+ },
+ 3: {
+ name: 'dilation_from',
+ fieldType: goog.proto2.Message.FieldType.FLOAT,
+ type: Number
+ },
+ 4: {
+ name: 'dilation_seconds',
+ fieldType: goog.proto2.Message.FieldType.DOUBLE,
+ type: Number
+ }
+ };
+ sketchology.proto.LinearPathAnimation.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.LinearPathAnimation, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.LinearPathAnimation.getDescriptor =
+ sketchology.proto.LinearPathAnimation.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.LineSize.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.LineSize.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'LineSize',
+ fullName: 'sketchology.proto.LineSize'
+ },
+ 7: {
+ name: 'stroke_width',
+ fieldType: goog.proto2.Message.FieldType.FLOAT,
+ type: Number
+ },
+ 8: {
+ name: 'units',
+ fieldType: goog.proto2.Message.FieldType.ENUM,
+ defaultValue: sketchology.proto.LineSize.SizeType.WORLD_UNITS,
+ type: sketchology.proto.LineSize.SizeType
+ }
+ };
+ sketchology.proto.LineSize.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.LineSize, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.LineSize.getDescriptor =
+ sketchology.proto.LineSize.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.PusherToolParams.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.PusherToolParams.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'PusherToolParams',
+ fullName: 'sketchology.proto.PusherToolParams'
+ },
+ 1: {
+ name: 'manipulate_stickers',
+ fieldType: goog.proto2.Message.FieldType.BOOL,
+ type: Boolean
+ },
+ 2: {
+ name: 'manipulate_text',
+ fieldType: goog.proto2.Message.FieldType.BOOL,
+ type: Boolean
+ }
+ };
+ sketchology.proto.PusherToolParams.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.PusherToolParams, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.PusherToolParams.getDescriptor =
+ sketchology.proto.PusherToolParams.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.ToolParams.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.ToolParams.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'ToolParams',
+ fullName: 'sketchology.proto.ToolParams'
+ },
+ 1: {
+ name: 'tool',
+ fieldType: goog.proto2.Message.FieldType.ENUM,
+ defaultValue: sketchology.proto.ToolParams.ToolType.UNKNOWN,
+ type: sketchology.proto.ToolParams.ToolType
+ },
+ 2: {
+ name: 'rgba',
+ fieldType: goog.proto2.Message.FieldType.UINT32,
+ type: Number
+ },
+ 3: {
+ name: 'line_size',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.LineSize
+ },
+ 4: {
+ name: 'pusher_tool_params',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.PusherToolParams
+ },
+ 5: {
+ name: 'brush_type',
+ fieldType: goog.proto2.Message.FieldType.ENUM,
+ defaultValue: sketchology.proto.BrushType.UNKNOWN_BRUSH,
+ type: sketchology.proto.BrushType
+ },
+ 6: {
+ name: 'linear_path_animation',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.LinearPathAnimation
+ }
+ };
+ sketchology.proto.ToolParams.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.ToolParams, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.ToolParams.getDescriptor =
+ sketchology.proto.ToolParams.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.FlagAssignment.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.FlagAssignment.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'FlagAssignment',
+ fullName: 'sketchology.proto.FlagAssignment'
+ },
+ 1: {
+ name: 'flag',
+ fieldType: goog.proto2.Message.FieldType.ENUM,
+ defaultValue: sketchology.proto.Flag.UNKNOWN,
+ type: sketchology.proto.Flag
+ },
+ 2: {
+ name: 'bool_value',
+ fieldType: goog.proto2.Message.FieldType.BOOL,
+ type: Boolean
+ }
+ };
+ sketchology.proto.FlagAssignment.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.FlagAssignment, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.FlagAssignment.getDescriptor =
+ sketchology.proto.FlagAssignment.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.AddElement.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.AddElement.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'AddElement',
+ fullName: 'sketchology.proto.AddElement'
+ },
+ 1: {
+ name: 'bundle',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.ElementBundle
+ },
+ 2: {
+ name: 'below_element_with_uuid',
+ fieldType: goog.proto2.Message.FieldType.STRING,
+ type: String
+ }
+ };
+ sketchology.proto.AddElement.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.AddElement, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.AddElement.getDescriptor =
+ sketchology.proto.AddElement.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.OutOfBoundsColor.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.OutOfBoundsColor.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'OutOfBoundsColor',
+ fullName: 'sketchology.proto.OutOfBoundsColor'
+ },
+ 1: {
+ name: 'rgba',
+ fieldType: goog.proto2.Message.FieldType.UINT32,
+ type: Number
+ }
+ };
+ sketchology.proto.OutOfBoundsColor.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.OutOfBoundsColor, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.OutOfBoundsColor.getDescriptor =
+ sketchology.proto.OutOfBoundsColor.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.SInputStream.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.SInputStream.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'SInputStream',
+ fullName: 'sketchology.proto.SInputStream'
+ },
+ 1: {
+ name: 'screen_width',
+ fieldType: goog.proto2.Message.FieldType.UINT32,
+ type: Number
+ },
+ 2: {
+ name: 'screen_height',
+ fieldType: goog.proto2.Message.FieldType.UINT32,
+ type: Number
+ },
+ 3: {
+ name: 'screen_ppi',
+ fieldType: goog.proto2.Message.FieldType.FLOAT,
+ type: Number
+ },
+ 4: {
+ name: 'input',
+ repeated: true,
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.SInput
+ }
+ };
+ sketchology.proto.SInputStream.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.SInputStream, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.SInputStream.getDescriptor =
+ sketchology.proto.SInputStream.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.SInput.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.SInput.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'SInput',
+ fullName: 'sketchology.proto.SInput'
+ },
+ 1: {
+ name: 'type',
+ fieldType: goog.proto2.Message.FieldType.ENUM,
+ defaultValue: sketchology.proto.SInput.InputType.UNKNOWN,
+ type: sketchology.proto.SInput.InputType
+ },
+ 2: {
+ name: 'id',
+ fieldType: goog.proto2.Message.FieldType.UINT32,
+ type: Number
+ },
+ 3: {
+ name: 'flags',
+ fieldType: goog.proto2.Message.FieldType.UINT32,
+ type: Number
+ },
+ 4: {
+ name: 'time_s',
+ fieldType: goog.proto2.Message.FieldType.DOUBLE,
+ type: Number
+ },
+ 5: {
+ name: 'screen_pos_x',
+ fieldType: goog.proto2.Message.FieldType.FLOAT,
+ type: Number
+ },
+ 6: {
+ name: 'screen_pos_y',
+ fieldType: goog.proto2.Message.FieldType.FLOAT,
+ type: Number
+ },
+ 7: {
+ name: 'pressure',
+ fieldType: goog.proto2.Message.FieldType.FLOAT,
+ type: Number
+ },
+ 8: {
+ name: 'wheel_delta',
+ fieldType: goog.proto2.Message.FieldType.FLOAT,
+ type: Number
+ },
+ 9: {
+ name: 'tilt',
+ fieldType: goog.proto2.Message.FieldType.FLOAT,
+ type: Number
+ },
+ 10: {
+ name: 'orientation',
+ fieldType: goog.proto2.Message.FieldType.FLOAT,
+ type: Number
+ }
+ };
+ sketchology.proto.SInput.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.SInput, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.SInput.getDescriptor =
+ sketchology.proto.SInput.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.SimulatedInput.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.SimulatedInput.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'SimulatedInput',
+ fullName: 'sketchology.proto.SimulatedInput'
+ },
+ 1: {
+ name: 'xs',
+ repeated: true,
+ fieldType: goog.proto2.Message.FieldType.FLOAT,
+ type: Number
+ },
+ 2: {
+ name: 'ys',
+ repeated: true,
+ fieldType: goog.proto2.Message.FieldType.FLOAT,
+ type: Number
+ },
+ 3: {
+ name: 'ts_secs',
+ repeated: true,
+ fieldType: goog.proto2.Message.FieldType.DOUBLE,
+ type: Number
+ },
+ 4: {
+ name: 'source_details',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.SourceDetails
+ }
+ };
+ sketchology.proto.SimulatedInput.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.SimulatedInput, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.SimulatedInput.getDescriptor =
+ sketchology.proto.SimulatedInput.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.SequencePoint.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.SequencePoint.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'SequencePoint',
+ fullName: 'sketchology.proto.SequencePoint'
+ },
+ 1: {
+ name: 'id',
+ fieldType: goog.proto2.Message.FieldType.INT32,
+ type: Number
+ }
+ };
+ sketchology.proto.SequencePoint.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.SequencePoint, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.SequencePoint.getDescriptor =
+ sketchology.proto.SequencePoint.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.SetCallbackFlags.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.SetCallbackFlags.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'SetCallbackFlags',
+ fullName: 'sketchology.proto.SetCallbackFlags'
+ },
+ 1: {
+ name: 'source_details',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.SourceDetails
+ },
+ 2: {
+ name: 'callback_flags',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.CallbackFlags
+ }
+ };
+ sketchology.proto.SetCallbackFlags.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.SetCallbackFlags, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.SetCallbackFlags.getDescriptor =
+ sketchology.proto.SetCallbackFlags.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.EngineState.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.EngineState.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'EngineState',
+ fullName: 'sketchology.proto.EngineState'
+ },
+ 1: {
+ name: 'camera_position',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.Rect
+ },
+ 2: {
+ name: 'page_bounds',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.Rect
+ },
+ 3: {
+ name: 'selection_is_live',
+ fieldType: goog.proto2.Message.FieldType.BOOL,
+ type: Boolean
+ }
+ };
+ sketchology.proto.EngineState.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.EngineState, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.EngineState.getDescriptor =
+ sketchology.proto.EngineState.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.CameraBoundsConfig.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.CameraBoundsConfig.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'CameraBoundsConfig',
+ fullName: 'sketchology.proto.CameraBoundsConfig'
+ },
+ 1: {
+ name: 'margin_left_px',
+ fieldType: goog.proto2.Message.FieldType.FLOAT,
+ type: Number
+ },
+ 2: {
+ name: 'margin_right_px',
+ fieldType: goog.proto2.Message.FieldType.FLOAT,
+ type: Number
+ },
+ 3: {
+ name: 'margin_bottom_px',
+ fieldType: goog.proto2.Message.FieldType.FLOAT,
+ type: Number
+ },
+ 4: {
+ name: 'margin_top_px',
+ fieldType: goog.proto2.Message.FieldType.FLOAT,
+ type: Number
+ },
+ 5: {
+ name: 'fraction_padding',
+ fieldType: goog.proto2.Message.FieldType.FLOAT,
+ defaultValue: 0.1,
+ type: Number
+ }
+ };
+ sketchology.proto.CameraBoundsConfig.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.CameraBoundsConfig, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.CameraBoundsConfig.getDescriptor =
+ sketchology.proto.CameraBoundsConfig.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.ImageInfo.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.ImageInfo.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'ImageInfo',
+ fullName: 'sketchology.proto.ImageInfo'
+ },
+ 1: {
+ name: 'uri',
+ fieldType: goog.proto2.Message.FieldType.STRING,
+ type: String
+ },
+ 2: {
+ name: 'asset_type',
+ fieldType: goog.proto2.Message.FieldType.ENUM,
+ defaultValue: sketchology.proto.ImageInfo.AssetType.DEFAULT,
+ type: sketchology.proto.ImageInfo.AssetType
+ }
+ };
+ sketchology.proto.ImageInfo.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.ImageInfo, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.ImageInfo.getDescriptor =
+ sketchology.proto.ImageInfo.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.ImageRect.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.ImageRect.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'ImageRect',
+ fullName: 'sketchology.proto.ImageRect'
+ },
+ 1: {
+ name: 'rect',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.Rect
+ },
+ 2: {
+ name: 'bitmap_uri',
+ fieldType: goog.proto2.Message.FieldType.STRING,
+ type: String
+ },
+ 3: {
+ name: 'attributes',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.ElementAttributes
+ },
+ 4: {
+ name: 'rotation_radians',
+ fieldType: goog.proto2.Message.FieldType.FLOAT,
+ type: Number
+ }
+ };
+ sketchology.proto.ImageRect.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.ImageRect, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.ImageRect.getDescriptor =
+ sketchology.proto.ImageRect.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.GridInfo.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.GridInfo.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'GridInfo',
+ fullName: 'sketchology.proto.GridInfo'
+ },
+ 1: {
+ name: 'uri',
+ fieldType: goog.proto2.Message.FieldType.STRING,
+ type: String
+ }
+ };
+ sketchology.proto.GridInfo.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.GridInfo, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.GridInfo.getDescriptor =
+ sketchology.proto.GridInfo.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.CreateDocument.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.CreateDocument.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'CreateDocument',
+ fullName: 'sketchology.proto.CreateDocument'
+ },
+ 1: {
+ name: 'document_type',
+ fieldType: goog.proto2.Message.FieldType.ENUM,
+ defaultValue: sketchology.proto.DocumentType.SINGLE_USER_DOCUMENT,
+ type: sketchology.proto.DocumentType
+ },
+ 2: {
+ name: 'storage_type',
+ fieldType: goog.proto2.Message.FieldType.ENUM,
+ defaultValue: sketchology.proto.StorageType.IN_MEMORY_STORAGE,
+ type: sketchology.proto.StorageType
+ },
+ 3: {
+ name: 'storage_path',
+ fieldType: goog.proto2.Message.FieldType.STRING,
+ type: String
+ },
+ 4: {
+ name: 'snapshot',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.Snapshot
+ }
+ };
+ sketchology.proto.CreateDocument.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.CreateDocument, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.CreateDocument.getDescriptor =
+ sketchology.proto.CreateDocument.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.AddPath.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.AddPath.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'AddPath',
+ fullName: 'sketchology.proto.AddPath'
+ },
+ 1: {
+ name: 'path',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.Path
+ },
+ 2: {
+ name: 'uuid',
+ fieldType: goog.proto2.Message.FieldType.STRING,
+ type: String
+ }
+ };
+ sketchology.proto.AddPath.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.AddPath, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.AddPath.getDescriptor =
+ sketchology.proto.AddPath.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.PusherPositionUpdate.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.PusherPositionUpdate.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'PusherPositionUpdate',
+ fullName: 'sketchology.proto.PusherPositionUpdate'
+ },
+ 1: {
+ name: 'uuid',
+ fieldType: goog.proto2.Message.FieldType.STRING,
+ type: String
+ },
+ 2: {
+ name: 'pointer_location',
+ repeated: true,
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.Point
+ }
+ };
+ sketchology.proto.PusherPositionUpdate.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.PusherPositionUpdate, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.PusherPositionUpdate.getDescriptor =
+ sketchology.proto.PusherPositionUpdate.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.ElementQueryData.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.ElementQueryData.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'ElementQueryData',
+ fullName: 'sketchology.proto.ElementQueryData'
+ },
+ 1: {
+ name: 'item',
+ repeated: true,
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.ElementQueryItem
+ },
+ 2: {
+ name: 'up_world_location',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.Point
+ },
+ 3: {
+ name: 'down_world_location',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.Point
+ }
+ };
+ sketchology.proto.ElementQueryData.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.ElementQueryData, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.ElementQueryData.getDescriptor =
+ sketchology.proto.ElementQueryData.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.ElementQueryItem.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.ElementQueryItem.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'ElementQueryItem',
+ fullName: 'sketchology.proto.ElementQueryItem'
+ },
+ 1: {
+ name: 'uuid',
+ fieldType: goog.proto2.Message.FieldType.STRING,
+ type: String
+ },
+ 2: {
+ name: 'world_bounds',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.Rect
+ },
+ 3: {
+ name: 'uri',
+ fieldType: goog.proto2.Message.FieldType.STRING,
+ type: String
+ }
+ };
+ sketchology.proto.ElementQueryItem.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.ElementQueryItem, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.ElementQueryItem.getDescriptor =
+ sketchology.proto.ElementQueryItem.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.SelectionState.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.SelectionState.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'SelectionState',
+ fullName: 'sketchology.proto.SelectionState'
+ },
+ 1: {
+ name: 'anything_selected',
+ fieldType: goog.proto2.Message.FieldType.BOOL,
+ type: Boolean
+ }
+ };
+ sketchology.proto.SelectionState.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.SelectionState, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.SelectionState.getDescriptor =
+ sketchology.proto.SelectionState.prototype.getDescriptor;
+
+
+/** @override */
+sketchology.proto.ToolEvent.prototype.getDescriptor = function() {
+ var descriptor = sketchology.proto.ToolEvent.descriptor_;
+ if (!descriptor) {
+ // The descriptor is created lazily when we instantiate a new instance.
+ var descriptorObj = {
+ 0: {
+ name: 'ToolEvent',
+ fullName: 'sketchology.proto.ToolEvent'
+ },
+ 1: {
+ name: 'pusher_position_update',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.PusherPositionUpdate
+ },
+ 2: {
+ name: 'element_query_data',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.ElementQueryData
+ },
+ 3: {
+ name: 'selection_state',
+ fieldType: goog.proto2.Message.FieldType.MESSAGE,
+ type: sketchology.proto.SelectionState
+ }
+ };
+ sketchology.proto.ToolEvent.descriptor_ = descriptor =
+ goog.proto2.Message.createDescriptor(
+ sketchology.proto.ToolEvent, descriptorObj);
+ }
+ return descriptor;
+};
+
+
+/** @nocollapse */
+sketchology.proto.ToolEvent.getDescriptor =
+ sketchology.proto.ToolEvent.prototype.getDescriptor;
diff --git a/chromium/third_party/ink/sketchology/public/js/common/brush_model.js b/chromium/third_party/ink/sketchology/public/js/common/brush_model.js
new file mode 100644
index 00000000000..8ffbe4c0a54
--- /dev/null
+++ b/chromium/third_party/ink/sketchology/public/js/common/brush_model.js
@@ -0,0 +1,243 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+goog.provide('ink.BrushModel');
+
+goog.require('goog.events');
+goog.require('goog.events.EventTarget');
+goog.require('ink.Model');
+goog.require('sketchology.proto.BrushType');
+goog.require('sketchology.proto.ToolParams.ToolType');
+
+
+
+/**
+ * Holds the state of the ink brush. Toolbar widgets update BrushModel, which in
+ * turn dispatches CHANGE events to update the toolbar components' states.
+ * @constructor
+ * @extends {ink.Model}
+ * @param {!goog.events.EventTarget} root Unused
+ */
+ink.BrushModel = function(root) {
+ ink.BrushModel.base(this, 'constructor');
+ /**
+ * Last brush color (not including erase color).
+ * @private {string}
+ */
+ this.color_ = ink.BrushModel.DEFAULT_DRAW_COLOR;
+
+ /**
+ * The active stroke width of the brush (includes erase size).
+ * @private {number}
+ */
+ this.strokeWidth_ = ink.BrushModel.DEFAULT_DRAW_SIZE;
+
+ /**
+ * @private {boolean}
+ */
+ this.isErasing_ = ink.BrushModel.DEFAULT_ISERASING;
+
+ /**
+ * @private {sketchology.proto.ToolParams.ToolType}
+ */
+ this.toolType_ = sketchology.proto.ToolParams.ToolType.LINE;
+
+ /**
+ * @private {sketchology.proto.BrushType}
+ */
+ this.brushType_ = sketchology.proto.BrushType.CALLIGRAPHY;
+
+ /**
+ * @private {string}
+ */
+ this.shape_ = 'CALLIGRAPHY';
+};
+goog.inherits(ink.BrushModel, ink.Model);
+ink.Model.addGetter(ink.BrushModel);
+
+
+/**
+ * @const {string}
+ */
+ink.BrushModel.DEFAULT_DRAW_COLOR = '#000000';
+
+
+/**
+ * @const {number}
+ */
+ink.BrushModel.DEFAULT_DRAW_SIZE = 0.6;
+
+
+/**
+ * @const {string}
+ */
+ink.BrushModel.DEFAULT_ERASE_COLOR = '#FFFFFF';
+
+/**
+ * @const {boolean}
+ */
+ink.BrushModel.DEFAULT_ISERASING = false;
+
+
+/**
+ * The events fired by the BrushModel.
+ * @enum {string} The event types for the BrushModel.
+ */
+ink.BrushModel.EventType = {
+ /**
+ * Fired when the BrushModel is changed.
+ */
+ CHANGE: goog.events.getUniqueId('change')
+};
+
+
+/**
+ * @const {Object}
+ */
+ink.BrushModel.SHAPE_TO_TOOLTYPE = {
+ 'AIRBRUSH': sketchology.proto.ToolParams.ToolType.LINE,
+ 'CALLIGRAPHY': sketchology.proto.ToolParams.ToolType.LINE,
+ 'EDIT': sketchology.proto.ToolParams.ToolType.EDIT,
+ 'ERASER': sketchology.proto.ToolParams.ToolType.LINE,
+ 'HIGHLIGHTER': sketchology.proto.ToolParams.ToolType.LINE,
+ 'INKPEN': sketchology.proto.ToolParams.ToolType.LINE,
+ 'MAGIC_ERASE': sketchology.proto.ToolParams.ToolType.MAGIC_ERASE,
+ 'MARKER': sketchology.proto.ToolParams.ToolType.LINE,
+ 'PENCIL': sketchology.proto.ToolParams.ToolType.LINE,
+ 'BALLPOINT': sketchology.proto.ToolParams.ToolType.LINE,
+ 'BALLPOINT_IN_PEN_MODE_ELSE_MARKER':
+ sketchology.proto.ToolParams.ToolType.LINE,
+ 'QUERY': sketchology.proto.ToolParams.ToolType.QUERY,
+};
+
+
+/**
+ * @const {Object}
+ */
+ink.BrushModel.SHAPE_TO_BRUSHTYPE = {
+ 'AIRBRUSH': sketchology.proto.BrushType.AIRBRUSH,
+ 'CALLIGRAPHY': sketchology.proto.BrushType.CALLIGRAPHY,
+ 'ERASER': sketchology.proto.BrushType.ERASER,
+ 'HIGHLIGHTER': sketchology.proto.BrushType.HIGHLIGHTER,
+ 'INKPEN': sketchology.proto.BrushType.INKPEN,
+ 'MARKER': sketchology.proto.BrushType.MARKER,
+ 'BALLPOINT': sketchology.proto.BrushType.BALLPOINT,
+ 'BALLPOINT_IN_PEN_MODE_ELSE_MARKER':
+ sketchology.proto.BrushType.BALLPOINT_IN_PEN_MODE_ELSE_MARKER,
+ 'PENCIL': sketchology.proto.BrushType.PENCIL,
+};
+
+
+/**
+ * @param {string} color The color in hex.
+ */
+ink.BrushModel.prototype.setColor = function(color) {
+ this.color_ = color;
+ this.dispatchEvent(ink.BrushModel.EventType.CHANGE);
+};
+
+
+/**
+ * @param {number} strokeWidth The brush's stroke width.
+ */
+ink.BrushModel.prototype.setStrokeWidth = function(strokeWidth) {
+ this.strokeWidth_ = strokeWidth;
+ this.dispatchEvent(ink.BrushModel.EventType.CHANGE);
+};
+
+
+/**
+ * @param {boolean} isErasing Whether user is erasing or not.
+ */
+ink.BrushModel.prototype.setIsErasing = function(isErasing) {
+ this.isErasing_ = isErasing;
+ this.dispatchEvent(ink.BrushModel.EventType.CHANGE);
+};
+
+
+/**
+ * @param {string} shape The brush shape, which is either a brush type or a tool
+ * type. If it's a brush type, implies tool type LINE.
+ */
+ink.BrushModel.prototype.setShape = function(shape) {
+ this.toolType_ = ink.BrushModel.SHAPE_TO_TOOLTYPE[shape];
+ this.brushType_ = ink.BrushModel.SHAPE_TO_BRUSHTYPE[shape] !== undefined ?
+ ink.BrushModel.SHAPE_TO_BRUSHTYPE[shape] :
+ this.brushType_;
+ this.shape_ = shape;
+ this.dispatchEvent(ink.BrushModel.EventType.CHANGE);
+};
+
+
+/**
+ * @return {string} The last used shape.
+ */
+ink.BrushModel.prototype.getShape = function() {
+ return this.shape_;
+};
+
+
+/**
+ * @return {string} The last draw color in hex (excluding erase color).
+ */
+ink.BrushModel.prototype.getColor = function() {
+ return this.color_;
+};
+
+
+/**
+ * Gets the current color being drawn on the screen (including erase color).
+ * @return {string} The brush color in hex.
+ */
+ink.BrushModel.prototype.getActiveColor = function() {
+ if (!this.isErasing_) {
+ return this.color_;
+ } else {
+ return ink.BrushModel.DEFAULT_ERASE_COLOR;
+ }
+};
+
+
+/**
+ * Wraps getActiveColor() by returning the numeric rgb of the color.
+ * @return {number} The brush color in numeric rbg.
+ */
+ink.BrushModel.prototype.getActiveColorNumericRbg = function() {
+ return parseInt(this.getActiveColor().substring(1), 16);
+};
+
+
+/**
+ * @return {number} Percentage size for stroke width, [0, 1].
+ *
+ * See sengine.proto SizeType
+ */
+ink.BrushModel.prototype.getStrokeWidth = function() {
+ return this.strokeWidth_;
+};
+
+
+/**
+ * @return {boolean} Whether user is erasing.
+ */
+ink.BrushModel.prototype.getIsErasing = function() {
+ return this.isErasing_;
+};
+
+
+/**
+ * @return {sketchology.proto.BrushType} The brush type for line
+ * tool.
+ */
+ink.BrushModel.prototype.getBrushType = function() {
+ return this.brushType_;
+};
+
+
+/**
+ * @return {sketchology.proto.ToolParams.ToolType} The tool type.
+ */
+ink.BrushModel.prototype.getToolType = function() {
+ return this.toolType_;
+};
+
diff --git a/chromium/third_party/ink/sketchology/public/js/common/color.js b/chromium/third_party/ink/sketchology/public/js/common/color.js
new file mode 100644
index 00000000000..1a612108c19
--- /dev/null
+++ b/chromium/third_party/ink/sketchology/public/js/common/color.js
@@ -0,0 +1,113 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+goog.provide('ink.Color');
+
+
+
+/**
+ * 32bit color representation. Each channels is a 8 bit uint.
+ * @param {number} argb The 32-bit color.
+ * @constructor
+ * @struct
+ */
+ink.Color = function(argb) {
+ /** @type {number} */
+ this.argb = argb;
+
+ /** @type {number} */
+ this.a = ink.Color.alphaFromArgb(argb);
+
+ /** @type {number} */
+ this.r = ink.Color.redFromArgb(argb);
+
+ /** @type {number} */
+ this.g = ink.Color.greenFromArgb(argb);
+
+ /** @type {number} */
+ this.b = ink.Color.blueFromArgb(argb);
+};
+
+
+/**
+ * @return {string} The color string (excluding alpha) that can be used as a
+ * fillStyle.
+ */
+ink.Color.prototype.getRgbString = function() {
+ return 'rgb(' + [this.r, this.g, this.b].join(',') + ')';
+};
+
+
+/** @return {string} The color string that can be used as a fillStyle. */
+ink.Color.prototype.getRgbaString = function() {
+ return 'rgba(' + [this.r, this.g, this.b, this.a / 255].join(',') + ')';
+};
+
+
+/** @return {Uint32Array} color as rgba 32-bit unsigned integer */
+ink.Color.prototype.getRgbaUint32 = function() {
+ return new Uint32Array(
+ [(this.r << 24) | (this.g << 16) | (this.b << 8) | this.a]);
+};
+
+
+/**
+ * @return {number} The alpha in the range 0-1 that can be used as a
+ * globalAlpha.
+ */
+ink.Color.prototype.getAlphaAsFloat = function() {
+ return this.a / 255;
+};
+
+
+/**
+ * Helper function that returns a function that right logical shifts by the
+ * provided amount and masks off the result.
+ * @param {number} shiftAmount The amount that the function should shift by.
+ * @return {!Function}
+ * @private
+ */
+ink.Color.shiftAndMask_ = function(shiftAmount) {
+ return function(argb) {
+ return (argb >>> shiftAmount) & 0xFF;
+ };
+};
+
+
+/**
+ * @param {number} argb The argb number.
+ * @return {number} alpha in the range 0 to 255.
+ */
+ink.Color.alphaFromArgb = ink.Color.shiftAndMask_(24);
+
+
+/**
+ * @param {number} argb The argb number.
+ * @return {number} red in the range 0 to 255.
+ */
+ink.Color.redFromArgb = ink.Color.shiftAndMask_(16);
+
+
+/**
+ * @param {number} argb The argb number.
+ * @return {number} green in the range 0 to 255.
+ */
+ink.Color.greenFromArgb = ink.Color.shiftAndMask_(8);
+
+
+/**
+ * @param {number} argb The argb number.
+ * @return {number} blue in the range 0 to 255.
+ */
+ink.Color.blueFromArgb = ink.Color.shiftAndMask_(0);
+
+
+/** @type {!ink.Color} */
+ink.Color.BLACK = new ink.Color(0xFF000000);
+
+
+/** @type {!ink.Color} */
+ink.Color.WHITE = new ink.Color(0xFFFFFFFF);
+
+/** @type {!ink.Color} */
+ink.Color.DEFAULT_BACKGROUND_COLOR = new ink.Color(0xFFFAFAFA);
diff --git a/chromium/third_party/ink/sketchology/public/js/common/element_listener.js b/chromium/third_party/ink/sketchology/public/js/common/element_listener.js
new file mode 100644
index 00000000000..a149f259d02
--- /dev/null
+++ b/chromium/third_party/ink/sketchology/public/js/common/element_listener.js
@@ -0,0 +1,32 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+/**
+ * @fileoverview Element listener interface declaration
+ */
+goog.provide('ink.ElementListener');
+
+/**
+ * @interface
+ */
+ink.ElementListener = function() {};
+
+/**
+ * @param {string} uuid
+ * @param {string} encodedElement
+ * @param {string} encodedTransform
+ */
+ink.ElementListener.prototype.onElementCreated = function(
+ uuid, encodedElement, encodedTransform) {};
+
+/**
+ * @param {Array.<string>} uuids
+ * @param {Array.<string>} encodedTransforms
+ */
+ink.ElementListener.prototype.onElementsMutated = function(
+ uuids, encodedTransforms) {};
+
+/**
+ * @param {Array.<string>} uuids
+ */
+ink.ElementListener.prototype.onElementsRemoved = function(uuids) {};
diff --git a/chromium/third_party/ink/sketchology/public/js/common/model.js b/chromium/third_party/ink/sketchology/public/js/common/model.js
new file mode 100644
index 00000000000..0a726a45141
--- /dev/null
+++ b/chromium/third_party/ink/sketchology/public/js/common/model.js
@@ -0,0 +1,89 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+goog.provide('ink.Model');
+
+goog.require('goog.asserts');
+goog.require('goog.events.EventTarget');
+goog.require('ink.util');
+
+
+
+/**
+ * A generic model base class. Each models here is a singleton per EventTarget
+ * hierarchy tree.
+ *
+ * @extends {goog.events.EventTarget}
+ * @constructor
+ * @struct
+ */
+ink.Model = function() {
+ ink.Model.base(this, 'constructor');
+};
+goog.inherits(ink.Model, goog.events.EventTarget);
+
+
+/**
+ * The models are attached to the root parent EventTargets. To have the models
+ * be automatically gc properly the relevant models need to actually be
+ * properties of those EventTargets. This property is initialized to avoid
+ * collisions with other JavaScript on the same page, similarly to the property
+ * that goog.getUid uses.
+ * @private {string}
+ */
+ink.Model.MODEL_INSTANCES_PROPERTY_ = 'ink_model_instances_' + Math.random();
+
+
+/**
+ * Adds the getter to the model constructor, allowing for the simpler
+ * ink.BrushModel.getInstance(this); instead of
+ * ink.Model.get(ink.BrushModel, this);
+ * @param {!function(new:ink.Model, !goog.events.EventTarget)} modelCtor
+ * The model constructor.
+ */
+ink.Model.addGetter = function(modelCtor) {
+ /**
+ * @param {!goog.events.EventTarget} observer
+ * @return {!ink.Model}
+ */
+ modelCtor.getInstance = function(observer) {
+ goog.asserts.assertObject(observer);
+ return ink.Model.get(modelCtor, observer);
+ };
+};
+
+
+/**
+ * Gets the relevant model for the provided viewer. The viewer should be a
+ * goog.ui.Component that has entered the document or a goog.events.EventTarget
+ * that has already had its parentEventTarget set.
+ *
+ * Note: This currently assumes that the provided models are singletons per
+ * EventTarget hierarchy tree. A more flexible design for deciding what level
+ * to have models should be added here if usage demands it.
+ *
+ * @param {!function(new:ink.Model, !goog.events.EventTarget)} modelCtor
+ * @param {!goog.events.EventTarget} observer
+ * @return {!ink.Model}
+ */
+ink.Model.get = function(modelCtor, observer) {
+ // TODO(esrauch): Maybe this should be implemented based on dom elements
+ // instead of the goog.ui.Component hierarchy. As it is, a stray setParent()
+ // call could cause the Model instance to suprisingly change for the same
+ // observer. On the other hand, reading the dom is slower and also can cause
+ // a brower reflow unnecessarily and this way also allows for vanilla
+ // EventTargets to get the relevant Models.
+ var root = ink.util.getRootParentComponent(observer);
+ var models = root[ink.Model.MODEL_INSTANCES_PROPERTY_];
+ if (!models) {
+ root[ink.Model.MODEL_INSTANCES_PROPERTY_] = models = {};
+ }
+ var key = goog.getUid(modelCtor);
+ var oldInstance = models[key];
+ if (oldInstance) {
+ return oldInstance;
+ }
+ var newInstance = new modelCtor(root);
+ models[key] = newInstance;
+ return newInstance;
+};
diff --git a/chromium/third_party/ink/sketchology/public/js/common/proto_serializer.js b/chromium/third_party/ink/sketchology/public/js/common/proto_serializer.js
new file mode 100644
index 00000000000..dc99eebda85
--- /dev/null
+++ b/chromium/third_party/ink/sketchology/public/js/common/proto_serializer.js
@@ -0,0 +1,77 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+goog.provide('ink.ProtoSerializer');
+
+
+goog.require('goog.crypt.base64');
+goog.require('goog.proto2.ObjectSerializer'); // for debugging
+goog.require('net.proto2.contrib.WireSerializer');
+
+
+
+/**
+ * A proto serializer / deserializer to and from base-64 encoded wire format.
+ * @constructor
+ * @struct
+ */
+ink.ProtoSerializer = function() {
+ /** @private {!net.proto2.contrib.WireSerializer} */
+ this.wireSerializer_ = new net.proto2.contrib.WireSerializer();
+};
+
+
+/**
+ * @param {!goog.proto2.Message} e proto to serialize
+ * @return {string} The serialized proto as a base 64 encoded string.
+ */
+ink.ProtoSerializer.prototype.serializeToBase64 = function(e) {
+ var buf = this.wireSerializer_.serialize(e);
+ return goog.crypt.base64.encodeByteArray(buf);
+};
+
+
+/**
+ * Deserializes the given opaque serialized object to a jspb object.
+ *
+ * @param {string} item serialized object as base64 text
+ * @param {!goog.proto2.Message} proto Proto to deserialize into
+ * @return {!goog.proto2.Message}
+ */
+ink.ProtoSerializer.prototype.safeDeserialize = function(item, proto) {
+ var buf = goog.crypt.base64.decodeStringToByteArray(item);
+ this.wireSerializer_.deserializeTo(proto, new Uint8Array(buf));
+ return proto;
+};
+
+
+/**
+ * @param {!sketchology.proto.Element} p
+ * @return {boolean} Whether the provided Element appears valid.
+ * @private
+ */
+ink.ProtoSerializer.prototype.isValid_ = function(p) {
+ if (p == null) {
+ return false;
+ }
+
+ if (!p.hasStroke()) {
+ return false;
+ }
+
+ return true;
+};
+
+
+/**
+ * Returns a human-readable representation of a proto.
+ *
+ * @param {!goog.proto2.Message} p The proto to debug.
+ * @return {string} A nice string to ponder.
+ * @private
+ */
+ink.ProtoSerializer.prototype.debugProto_ = function(p) {
+ var obj = new goog.proto2.ObjectSerializer(
+ goog.proto2.ObjectSerializer.KeyOption.NAME).serialize(p);
+ return JSON.stringify(obj, null, ' ');
+};
diff --git a/chromium/third_party/ink/sketchology/public/js/common/undo_state_change_event.js b/chromium/third_party/ink/sketchology/public/js/common/undo_state_change_event.js
new file mode 100644
index 00000000000..5b65a277026
--- /dev/null
+++ b/chromium/third_party/ink/sketchology/public/js/common/undo_state_change_event.js
@@ -0,0 +1,28 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+goog.provide('ink.UndoStateChangeEvent');
+
+goog.require('goog.events');
+goog.require('goog.events.Event');
+
+
+
+/**
+ * @param {boolean} canUndo Whether there is undo state available.
+ * @param {boolean} canRedo Whether there is redo state available.
+ * @constructor
+ * @struct
+ * @extends {goog.events.Event}
+ */
+ink.UndoStateChangeEvent = function(canUndo, canRedo) {
+ ink.UndoStateChangeEvent.base(
+ this, 'constructor',ink.UndoStateChangeEvent.EVENT_TYPE);
+ this.canUndo = canUndo;
+ this.canRedo = canRedo;
+};
+goog.inherits(ink.UndoStateChangeEvent, goog.events.Event);
+
+
+/** @type {string} */
+ink.UndoStateChangeEvent.EVENT_TYPE = goog.events.getUniqueId('undo-state');
diff --git a/chromium/third_party/ink/sketchology/public/js/common/util.js b/chromium/third_party/ink/sketchology/public/js/common/util.js
new file mode 100644
index 00000000000..85fd56ecb11
--- /dev/null
+++ b/chromium/third_party/ink/sketchology/public/js/common/util.js
@@ -0,0 +1,292 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+goog.provide('ink.util');
+
+goog.require('goog.dom');
+goog.require('goog.events');
+goog.require('goog.math.Size');
+goog.require('protos.research.ink.InkEvent');
+
+
+/** @enum {string} */
+ink.util.SEngineType = {
+ IN_MEMORY: 'makeSEngineInMemory',
+ LONGFORM: 'makeLongformSEngine',
+ PASSTHROUGH_DOCUMENT: 'makeSEnginePassthroughDocument'
+};
+
+
+/**
+ * @typedef {{
+ * getModel: (function():ink.util.RealtimeModel)
+ * }}
+ */
+ink.util.RealtimeDocument;
+
+
+/**
+ * @typedef {{
+ * getRoot: (function():ink.util.RealtimeRoot)
+ * }}
+ */
+ink.util.RealtimeModel;
+
+
+/**
+ * @typedef {{
+ * get: (function(string):ink.util.RealtimePages)
+ * }}
+ */
+ink.util.RealtimeRoot;
+
+
+/**
+ * @typedef {{
+ * get: (function(number):ink.util.RealtimePage),
+ * xhigh: number,
+ * xlow: number,
+ * yhigh: number,
+ * ylow: number
+ * }}
+ */
+ink.util.RealtimePages;
+
+
+/**
+ * @typedef {{
+ * get: (function(string):ink.util.RealtimeElements)
+ * }}
+ */
+ink.util.RealtimePage;
+
+
+/**
+ * @typedef {{
+ * asArray: (function():Array.<ink.util.RealtimeElement>)
+ * }}
+ */
+ink.util.RealtimeElements;
+
+
+/**
+ * @typedef {{
+ * get: (function(string):string)
+ * }}
+ */
+ink.util.RealtimeElement;
+
+
+/**
+ * Wrapper used for event handlers to pass the event target instead of the
+ * event.
+ * @param {!Function} callback The event handler function.
+ * @param {Object=} opt_handler Element in whole scope to call the callback.
+ * @return {!Function} Wrapped function.
+ */
+ink.util.eventTargetWrapper = function(callback, opt_handler) {
+ return function(evt) {
+ var self = /** @type {Object} */ (this);
+ callback.call(opt_handler || self, evt.target);
+ };
+};
+
+
+/**
+ * @param {!goog.events.EventTarget} child The child to get the root parent
+ * EventTarget for.
+ * @return {!goog.events.EventTarget} The root parent.
+ * TODO(esrauch): Rename this function and consider moving it to a new
+ * whiteboard/util.js file.
+ */
+ink.util.getRootParentComponent = function(child) {
+ var current = child;
+ var parent = current.getParentEventTarget();
+ while (parent) {
+ current = parent;
+ parent = current.getParentEventTarget();
+ }
+ return current;
+};
+
+
+/**
+ * Downloads an image, renders it to a canvas, returns the raw bytes.
+ * @param {string} imgSrc
+ * @param {Function} callback
+ */
+ink.util.getImageBytes = function(imgSrc, callback) {
+ var canvasElement = goog.dom.createElement(goog.dom.TagName.CANVAS);
+ var imgElement = goog.dom.createElement(goog.dom.TagName.IMG);
+ imgElement.setAttribute(
+ 'style',
+ 'position:absolute;visibility:hidden;top:-1000px;left:-1000px;');
+ imgElement.crossOrigin = 'Anonymous';
+
+ goog.events.listenOnce(imgElement, 'load', function() {
+ var width = imgElement.width;
+ var height = imgElement.height;
+ canvasElement.width = width;
+ canvasElement.height = height;
+ var ctx = canvasElement.getContext('2d');
+ ctx.drawImage(imgElement, 0, 0);
+ var data = ctx.getImageData(0, 0, width, height);
+
+ document.body.removeChild(imgElement);
+
+ callback(data.data, new goog.math.Size(width, height));
+ });
+
+ imgElement.setAttribute('src', imgSrc);
+ document.body.appendChild(imgElement);
+};
+
+
+/**
+ * Creates document events.
+ * @param {!protos.research.ink.InkEvent.Host} host
+ * @param {!protos.research.ink.InkEvent.DocumentEvent.DocumentEventType} type
+ * @return {!protos.research.ink.InkEvent}
+ */
+ink.util.createDocumentEvent = function(host, type) {
+ var eventProto = new protos.research.ink.InkEvent();
+ eventProto.setHost(host);
+ eventProto.setEventType(
+ protos.research.ink.InkEvent.EventType.DOCUMENT_EVENT);
+ var documentEvent = new protos.research.ink.InkEvent.DocumentEvent();
+ documentEvent.setEventType(type);
+ eventProto.setDocumentEvent(documentEvent);
+ return eventProto;
+};
+
+
+/**
+ * Helper for constructing a document created event
+ *
+ * @param {!protos.research.ink.InkEvent.Host} host
+ * @param {!protos.research.ink.InkEvent.DocumentEvent.DocumentState} state
+ * @param {number} firstLoadTime (which is when the first byte is loaded)
+ * @param {number} startLoadTime (which is when the document is first editable).
+ * @return {!protos.research.ink.InkEvent}
+ */
+ink.util.createDocumentOpenedEvent = function(
+ host, state, firstLoadTime, startLoadTime) {
+ var type =
+ protos.research.ink.InkEvent.DocumentEvent.DocumentEventType.OPENED;
+ var ev = ink.util.createDocumentEvent(host, type);
+ var openedEvent =
+ new protos.research.ink.InkEvent.DocumentEvent.OpenedEvent();
+ openedEvent.setMillisUntilFirstByteLoaded(firstLoadTime.toString());
+ openedEvent.setMillisUntilEditable((goog.now() - startLoadTime).toString());
+ var documentEvent = ev.getDocumentEventOrDefault();
+ documentEvent.setOpenedEvent(openedEvent);
+ documentEvent.setDocumentState(state);
+ return ev;
+};
+
+
+/**
+ * Helper for constructing a collaborator joined logging event.
+ *
+ * @param {!protos.research.ink.InkEvent.Host} host
+ * @param {!protos.research.ink.InkEvent.DocumentEvent.DocumentState} state
+ * @param {boolean} isMe
+ * @return {!protos.research.ink.InkEvent}
+ */
+ink.util.createCollaboratorJoinedDocumentEvent = function(host, state, isMe) {
+ var type = protos.research.ink.InkEvent.DocumentEvent.DocumentEventType
+ .COLLABORATOR_JOINED;
+ var ev = ink.util.createDocumentEvent(host, type);
+ var collaboratorJoinedEvent =
+ new protos.research.ink.InkEvent.DocumentEvent.CollaboratorJoined();
+ collaboratorJoinedEvent.setIsMe(isMe);
+ var documentEvent = ev.getDocumentEventOrDefault();
+ documentEvent.setCollaboratorJoinedEvent(collaboratorJoinedEvent);
+ documentEvent.setDocumentState(state);
+ return ev;
+};
+
+
+/**
+ * @const
+ */
+ink.util.SHAPE_TO_LOG_TOOLTYPE = {
+ 'CALLIGRAPHY': protos.research.ink.InkEvent.ToolbarEvent.ToolType.CALLIGRAPHY,
+ 'EDIT': protos.research.ink.InkEvent.ToolbarEvent.ToolType.EDIT_TOOL,
+ 'HIGHLIGHTER': protos.research.ink.InkEvent.ToolbarEvent.ToolType.HIGHLIGHTER,
+ 'MAGIC_ERASE': protos.research.ink.InkEvent.ToolbarEvent.ToolType.MAGIC_ERASER,
+ 'MARKER': protos.research.ink.InkEvent.ToolbarEvent.ToolType.MARKER
+};
+
+
+/**
+ * Creates toolbar events.
+ * @param {protos.research.ink.InkEvent.Host} host
+ * @param {protos.research.ink.InkEvent.ToolbarEvent.ToolEventType} type
+ * @param {string} toolType
+ * @param {string} color
+ * @return {protos.research.ink.InkEvent}
+ */
+ink.util.createToolbarEvent = function(host, type, toolType, color) {
+ var eventProto = new protos.research.ink.InkEvent();
+ eventProto.setHost(host);
+ eventProto.setEventType(protos.research.ink.InkEvent.EventType.TOOLBAR_EVENT);
+ var toolbarEvent = new protos.research.ink.InkEvent.ToolbarEvent();
+ // Alpha is always 0xFF. This does #RRGGBB -> #AARRGGBB -> 0xAARRGGBB.
+ var colorAsNumber = parseInt('ff' + color.substring(1, 7), 16);
+ toolbarEvent.setColor(colorAsNumber);
+ toolbarEvent.setToolType(
+ ink.util.SHAPE_TO_LOG_TOOLTYPE[toolType] ||
+ protos.research.ink.InkEvent.ToolbarEvent.ToolType.UNKNOWN_TOOL_TYPE);
+ toolbarEvent.setToolEventType(type);
+ eventProto.setToolbarEvent(toolbarEvent);
+ return eventProto;
+};
+
+
+// Note: These helpers are included here because we do not use the closure
+// browser event wrappers to avoid additional GC pauses. Logic forked from
+// cs/piper///depot/google3/javascript/closure/events/browserevent.js?l=337
+
+/**
+ * "Action button" is MouseEvent.button equal to 0 (main button) and no
+ * control-key for Mac right-click action. The button property is the one
+ * that was responsible for triggering a mousedown or mouseup event.
+ *
+ * @param {MouseEvent} evt
+ * @return {boolean}
+ */
+ink.util.isMouseActionButton = function(evt) {
+ return evt.button == 0 &&
+ !(goog.userAgent.WEBKIT && goog.userAgent.MAC && evt.ctrlKey);
+};
+
+
+/**
+ * MouseEvent.buttons has 1 (main button) held down, and no control-key
+ * for Mac right-click drag. This is for which button is currently held down,
+ * e.g. during a mousemove event.
+ *
+ * @param {MouseEvent} evt
+ * @return {boolean}
+ */
+ink.util.hasMouseActionButton = function(evt) {
+ return (evt.buttons & 1) == 1 &&
+ !(goog.userAgent.WEBKIT && goog.userAgent.MAC && evt.ctrlKey);
+};
+
+
+/**
+ * Checks MouseEvent.buttons has 2 (secondary button) held down, or 1 (main
+ * button) with control key for Mac right-click drag. This is for which
+ * button is currently held down, e.g. during a mousemove event.
+ *
+ * @param {MouseEvent} evt
+ * @return {boolean}
+ */
+ink.util.hasMouseSecondaryButton = function(evt) {
+ return (evt.buttons & 2) == 2 ||
+ ((evt.buttons & 1) == 1 && goog.userAgent.WEBKIT &&
+ goog.userAgent.MAC && evt.ctrlKey);
+};
+
diff --git a/chromium/third_party/ink/sketchology/public/nacl/embed.soy.js b/chromium/third_party/ink/sketchology/public/nacl/embed.soy.js
new file mode 100644
index 00000000000..e0b43259d00
--- /dev/null
+++ b/chromium/third_party/ink/sketchology/public/nacl/embed.soy.js
@@ -0,0 +1,50 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+// This file was automatically generated from embed.soy.
+// Please don't edit this file by hand.
+
+/**
+ * @fileoverview Templates in namespace ink.soy.nacl.
+ * @public
+ */
+
+goog.provide('ink.soy.nacl.canvasHTML');
+
+goog.require('goog.soy.data.SanitizedContent');
+goog.require('soy');
+goog.require('soy.asserts');
+goog.require('soydata.VERY_UNSAFE');
+
+
+/**
+ * @param {ink.soy.nacl.canvasHTML.Params} opt_data
+ * @param {Object<string, *>=} opt_ijData
+ * @param {Object<string, *>=} opt_ijData_deprecated
+ * @return {!goog.soy.data.SanitizedHtml}
+ * @suppress {checkTypes}
+ */
+ink.soy.nacl.canvasHTML = function(opt_data, opt_ijData, opt_ijData_deprecated) {
+ opt_ijData = opt_ijData_deprecated || opt_ijData;
+ /** @type {!goog.soy.data.SanitizedContent|string} */
+ var manifestUrl = soy.asserts.assertType(goog.isString(opt_data.manifestUrl) || opt_data.manifestUrl instanceof goog.soy.data.SanitizedContent, 'manifestUrl', opt_data.manifestUrl, '!goog.soy.data.SanitizedContent|string');
+ /** @type {!goog.soy.data.SanitizedContent|string} */
+ var useMSAA = soy.asserts.assertType(goog.isString(opt_data.useMSAA) || opt_data.useMSAA instanceof goog.soy.data.SanitizedContent, 'useMSAA', opt_data.useMSAA, '!goog.soy.data.SanitizedContent|string');
+ /** @type {!goog.soy.data.SanitizedContent|string} */
+ var useSingleBuffer = soy.asserts.assertType(goog.isString(opt_data.useSingleBuffer) || opt_data.useSingleBuffer instanceof goog.soy.data.SanitizedContent, 'useSingleBuffer', opt_data.useSingleBuffer, '!goog.soy.data.SanitizedContent|string');
+ /** @type {!goog.soy.data.SanitizedContent|string} */
+ var sengineType = soy.asserts.assertType(goog.isString(opt_data.sengineType) || opt_data.sengineType instanceof goog.soy.data.SanitizedContent, 'sengineType', opt_data.sengineType, '!goog.soy.data.SanitizedContent|string');
+ return soydata.VERY_UNSAFE.ordainSanitizedHtml(((goog.DEBUG && soy.$$debugSoyTemplateInfo) ? '<!--dta_of(ink.soy.nacl.canvasHTML, third_party/sketchology/public/nacl/embed.soy, 3)-->' : '') + '<style' + (opt_ijData && opt_ijData.csp_nonce ? ' nonce="' + soy.$$escapeHtmlAttribute(opt_ijData && opt_ijData.csp_nonce) + '"' : '') + '>\n #ink-engine-hwoverlay {\n display: none;\n position: absolute;\n width: 5px;\n height: 5px;\n left: 0px;\n top: 0px;\n /* Transforms and semi-transparent color are used to ensure the div\n * prevents use of a hardware overlay for the underlying canvas element,\n * despite future optimizations to the hardware overlay eligibility\n * detection in ChromeOS. See b/64569245 for details */\n background-color: rgba(0, 0, 0, 0.01);\n transform: translate3d(0.33, 0.14, 0);\n }\n </style><embed id="ink-engine" use_msaa="' + soy.$$escapeHtmlAttribute(useMSAA) + '" use_single_buffer="' + soy.$$escapeHtmlAttribute(useSingleBuffer) + '" src="' + soy.$$escapeHtmlAttribute(soy.$$filterNormalizeUri(manifestUrl)) + '" type="application/x-nacl" sengine_type="' + soy.$$escapeHtmlAttribute(sengineType) + '"><div id="ink-engine-hwoverlay"></div>' + ((goog.DEBUG && soy.$$debugSoyTemplateInfo) ? '<!--dta_cf(ink.soy.nacl.canvasHTML)-->' : ''));
+};
+/**
+ * @typedef {{
+ * manifestUrl: (!goog.soy.data.SanitizedContent|string),
+ * useMSAA: (!goog.soy.data.SanitizedContent|string),
+ * useSingleBuffer: (!goog.soy.data.SanitizedContent|string),
+ * sengineType: (!goog.soy.data.SanitizedContent|string),
+ * }}
+ */
+ink.soy.nacl.canvasHTML.Params;
+if (goog.DEBUG) {
+ ink.soy.nacl.canvasHTML.soyTemplateName = 'ink.soy.nacl.canvasHTML';
+}
diff --git a/chromium/third_party/ink/sketchology/public/nacl/sketchology_engine_wrapper.js b/chromium/third_party/ink/sketchology/public/nacl/sketchology_engine_wrapper.js
new file mode 100644
index 00000000000..8ef20885f59
--- /dev/null
+++ b/chromium/third_party/ink/sketchology/public/nacl/sketchology_engine_wrapper.js
@@ -0,0 +1,764 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+/**
+ * @fileoverview Wrapper to call the Sketchology engine.
+ */
+
+goog.provide('ink.SketchologyEngineWrapper');
+
+goog.require('goog.asserts');
+goog.require('goog.events');
+goog.require('goog.labs.userAgent.platform');
+goog.require('goog.math.Box');
+goog.require('goog.math.Coordinate');
+goog.require('goog.math.Rect');
+goog.require('goog.math.Size');
+goog.require('goog.soy');
+goog.require('goog.ui.Component');
+goog.require('ink.Color');
+goog.require('ink.ElementListener');
+goog.require('ink.UndoStateChangeEvent');
+goog.require('ink.soy.nacl.canvasHTML');
+goog.require('ink.util');
+goog.require('net.proto2.contrib.WireSerializer');
+goog.require('sketchology.proto.BackgroundColor');
+goog.require('sketchology.proto.BackgroundImageInfo');
+goog.require('sketchology.proto.Command');
+goog.require('sketchology.proto.Flag');
+goog.require('sketchology.proto.FlagAssignment');
+goog.require('sketchology.proto.ImageInfo');
+goog.require('sketchology.proto.MutationPacket');
+goog.require('sketchology.proto.NoArgCommand');
+goog.require('sketchology.proto.OutOfBoundsColor');
+goog.require('sketchology.proto.PageProperties');
+goog.require('sketchology.proto.Rect');
+goog.require('sketchology.proto.SequencePoint');
+goog.require('sketchology.proto.SetCallbackFlags');
+goog.require('sketchology.proto.Snapshot');
+goog.require('sketchology.proto.ToolParams');
+
+
+
+/**
+ * @param {?string} engineUrl URL to the native client manifest file
+ * @param {ink.ElementListener} elementListener
+ * @param {function(number, number, Uint8ClampedArray)} onImageExportComplete
+ * @param {!ink.util.SEngineType} sengineType
+ * @struct
+ * @constructor
+ * @extends {goog.ui.Component}
+ */
+ink.SketchologyEngineWrapper = function(
+ engineUrl, elementListener, onImageExportComplete, sengineType) {
+ ink.SketchologyEngineWrapper.base(this, 'constructor');
+
+ goog.asserts.assert(engineUrl);
+ /** @private {string} */
+ this.engineUrl_ = engineUrl;
+
+ /** @private {ink.ElementListener} */
+ this.elementListener_ = elementListener;
+
+ /** @private {function(number, number, Uint8ClampedArray)} */
+ this.onImageExportComplete_ = onImageExportComplete;
+
+ // default document bounds
+ this.pageLeft_ = 0;
+ this.pageTop_ = 600;
+ this.pageRight_ = 800;
+ this.pageBottom_ = 0;
+
+ /**
+ * Protocol Buffer wire format serializer.
+ * @type {!net.proto2.contrib.WireSerializer}
+ * @private
+ */
+ this.wireSerializer_ = new net.proto2.contrib.WireSerializer();
+
+ /** @private */
+ this.lastBrushUpdate_ = goog.nullFunction;
+
+ /** @private */
+ this.penModeEnabled_ = false;
+
+ /** @private {boolean} */
+ this.listenersAdded_ = false;
+
+ /** @private {string} */
+ this.sengineType_ = /** @type {string} */ (sengineType);
+
+ /** @private {Array.<function(!sketchology.proto.Snapshot)>} */
+ this.snapshotCallbacks_ = [];
+
+ /** @private {Array.<function(!sketchology.proto.Snapshot)>} */
+ this.brixConversionCallbacks_ = [];
+
+ /** @private {Array.<!ink.util.RealtimeDocument>} */
+ this.brixDocuments_ = [];
+
+ /** @private {Array.<function(boolean)>} */
+ this.snapshotHasPendingMutationsCallbacks_ = [];
+
+ /** @private {Array.<function(sketchology.proto.MutationPacket)>} */
+ this.extractMutationPacketCallbacks_ = [];
+
+ /** @private {Array.<function(sketchology.proto.Snapshot)>} */
+ this.clearPendingMutationsCallbacks_ = [];
+
+ /** @private {Element} */
+ this.engineElement_;
+
+ /** @private {Object<number, !Function>} */
+ this.sequencePointCallbacks_ = {};
+
+ /** @private {number} */
+ this.nextSequencePointId_ = 0;
+};
+goog.inherits(ink.SketchologyEngineWrapper, goog.ui.Component);
+
+/** @override */
+ink.SketchologyEngineWrapper.prototype.createDom = function() {
+ const useMSAA = !goog.labs.userAgent.platform.isMacintosh() ||
+ // MSAA is disabled for MacOS 10.12.4 and prior: b/38280481
+ goog.labs.userAgent.platform.isVersionOrHigher('10.12.5');
+ const useSingleBuffer = goog.labs.userAgent.platform.isChromeOS();
+ const elem = goog.soy.renderAsElement(ink.soy.nacl.canvasHTML, {
+ manifestUrl: this.engineUrl_,
+ useMSAA: !!useMSAA + '',
+ useSingleBuffer: !!useSingleBuffer + '',
+ sengineType: this.sengineType_
+ });
+ this.setElementInternal(elem);
+ this.engineElement_ = elem.querySelector('#ink-engine');
+};
+
+/** @override */
+ink.SketchologyEngineWrapper.prototype.enterDocument = function() {
+ this.engineElement_.addEventListener(goog.events.EventType.LOAD, () => {
+ this.initGl();
+ this.lastBrushUpdate_();
+ this.assignFlag(
+ sketchology.proto.Flag.ENABLE_PEN_MODE, this.penModeEnabled_);
+ });
+};
+
+
+/** @enum {string} */
+ink.SketchologyEngineWrapper.EventType = {
+ CANVAS_INITIALIZED: goog.events.getUniqueId('gl_canvas_initialized'),
+ CANVAS_FATAL_ERROR: goog.events.getUniqueId('fatal_error'),
+ PEN_MODE_ENABLED: goog.events.getUniqueId('pen_mode_enabled')
+};
+
+
+/**
+ * An event fired when pen mode is enabled or disabled.
+ *
+ * @param {boolean} enabled
+ *
+ * @extends {goog.events.Event}
+ * @constructor
+ * @struct
+ */
+ink.SketchologyEngineWrapper.PenModeEnabled = function(enabled) {
+ ink.SketchologyEngineWrapper.PenModeEnabled.base(this, 'constructor',
+ ink.SketchologyEngineWrapper.EventType.PEN_MODE_ENABLED);
+
+ /** @type {boolean} */
+ this.enabled = enabled;
+};
+goog.inherits(ink.SketchologyEngineWrapper.PenModeEnabled, goog.events.Event);
+
+
+/** Poke the engine to wake up and start drawing */
+ink.SketchologyEngineWrapper.prototype.poke = function() {
+ this.engineElement_.postMessage(['poke', '']);
+};
+
+/**
+ * Global exit function for emscripten to call.
+ * @export
+ */
+ink.SketchologyEngineWrapper.exit = function() {
+ console.log('Engine requested exit.');
+};
+
+/**
+ * @param {sketchology.proto.Command} command
+ */
+ink.SketchologyEngineWrapper.prototype.handleCommand = function(command) {
+ var commandBytes = this.wireSerializer_.serialize(command);
+ var buf = new Uint8Array(commandBytes);
+ this.engineElement_.postMessage(['handleCommand', buf.buffer]);
+};
+
+/**
+ * Tells the engine to handle a message received remotely.
+ *
+ * @param {!Object<string, string>} bundle
+ */
+ink.SketchologyEngineWrapper.prototype.addElement = function(bundle) {
+ goog.asserts.assert(bundle);
+ this.engineElement_.postMessage(['addElementToEngine', {'bundle': bundle}]);
+};
+
+
+/**
+ * Add an encoded element bundle to the engine.
+ *
+ * @param {!Object<string, string>} bundle
+ * @param {string} belowUUID
+ */
+ink.SketchologyEngineWrapper.prototype.addElementBelow = function(
+ bundle, belowUUID) {
+ goog.asserts.assert(bundle);
+ this.engineElement_.postMessage(
+ ['addElementToEngineBelow', {'bundle': bundle, 'below_uuid': belowUUID}]);
+};
+
+
+/**
+ * @param {string} uuid
+ */
+ink.SketchologyEngineWrapper.prototype.removeElement = function(uuid) {
+ this.engineElement_.postMessage(['removeElement', uuid]);
+};
+
+
+/**
+ * NaCl does its own GL initialization, so we just hook up listeners.
+ */
+ink.SketchologyEngineWrapper.prototype.initGl = function() {
+ var elem = this.engineElement_;
+ this.setPageBounds(
+ this.pageLeft_, this.pageTop_, this.pageRight_, this.pageBottom_);
+ if (!this.listenersAdded_) {
+ this.listenersAdded_ = true;
+ elem.addEventListener('message', goog.bind(function(msg) {
+ if (!('event_type' in msg['data'])) {
+ return; // Unknown event type!
+ }
+ var data = msg['data'];
+ switch (data['event_type']) {
+ case 'exit':
+ ink.SketchologyEngineWrapper.exit();
+ break;
+ case 'debug':
+ if (goog.DEBUG) {
+ console.log(data['message']);
+ }
+ break;
+ case 'image_export':
+ this.onImageExportComplete_(
+ data['width'], data['height'],
+ new Uint8ClampedArray(data['bytes']));
+ break;
+ case 'element_added':
+ if (this.elementListener_) {
+ this.elementListener_.onElementCreated(data['uuid'],
+ data['encoded_element'], data['encoded_transform']);
+ }
+ break;
+ case 'elements_mutated':
+ if (this.elementListener_) {
+ this.elementListener_.onElementsMutated(data['uuids'],
+ data['encoded_transforms']);
+ }
+ break;
+ case 'elements_removed':
+ if (this.elementListener_) {
+ this.elementListener_.onElementsRemoved(data['uuids']);
+ }
+ break;
+ case 'flag_changed':
+ if (data['which'] == sketchology.proto.Flag.ENABLE_PEN_MODE) {
+ this.penModeEnabled_ = data['enabled'];
+ this.dispatchEvent(
+ new ink.SketchologyEngineWrapper.PenModeEnabled(
+ data['enabled']));
+ }
+ break;
+ case 'undo_redo_state_changed':
+ this.dispatchEvent(new ink.UndoStateChangeEvent(
+ !!data['can_undo'], !!data['can_redo']));
+ break;
+ case 'snapshot_gotten':
+ var proto = new sketchology.proto.Snapshot();
+ this.wireSerializer_.deserializeTo(proto, data['snapshot']);
+ this.snapshotCallbacks_.shift().call(null, proto);
+ break;
+ case 'brix_elements_converted':
+ var proto = new sketchology.proto.Snapshot();
+ this.wireSerializer_.deserializeTo(proto, data['snapshot']);
+ var pageProperties = new sketchology.proto.PageProperties();
+ var rect = new sketchology.proto.Rect();
+ var brixDoc = this.brixDocuments_.shift();
+ var model = brixDoc.getModel();
+ var root = model.getRoot();
+ var brixBounds = root.get('bounds');
+ rect.setXhigh(brixBounds.xhigh || 0);
+ rect.setXlow(brixBounds.xlow || 0);
+ rect.setYhigh(brixBounds.yhigh || 0);
+ rect.setYlow(brixBounds.ylow || 0);
+ pageProperties.setBounds(rect);
+ proto.setPageProperties(pageProperties);
+ this.brixConversionCallbacks_.shift().call(null, proto);
+ break;
+ case 'snapshot_has_pending_mutations':
+ this.snapshotHasPendingMutationsCallbacks_.shift().call(
+ null, data['has_mutations']);
+ break;
+ case 'extracted_mutation_packet':
+ var proto = new sketchology.proto.MutationPacket();
+ this.wireSerializer_.deserializeTo(proto, data['extraction_packet']);
+ this.extractMutationPacketCallbacks_.shift().call(null, proto);
+ break;
+ case 'cleared_pending_mutations':
+ var proto = new sketchology.proto.Snapshot();
+ this.wireSerializer_.deserializeTo(proto, data['snapshot']);
+ this.clearPendingMutationsCallbacks_.shift().call(null, proto);
+ break;
+ case 'hwoverlay':
+ this.setHardwareOverlay(!!data['enable']);
+ break;
+ case 'single_buffer':
+ // If the Native Client module was able to obtain a single buffered
+ // graphics context, flip the embed element to allow promotion to
+ // hardware overlay on Eve in landscape mode.
+ // TODO(b/64569245): Add support for all device orientations
+ this.engineElement_.style.transform = 'scaleY(-1)';
+ break;
+ case 'sequence_point_reached':
+ var id = data['id'];
+ var cb = this.sequencePointCallbacks_[id];
+ delete this.sequencePointCallbacks_[id];
+ cb();
+ break;
+ }
+ }, this));
+ }
+ this.dispatchEvent(ink.SketchologyEngineWrapper.EventType.CANVAS_INITIALIZED);
+};
+
+
+/**
+ * Sets the border image.
+ * @param {Uint8ClampedArray} data
+ * @param {goog.math.Size} size
+ * @param {string} uri
+ * @param {!sketchology.proto.Border} borderImageProto
+ * @param {number} outOfBoundsColor The out of bounds color in rgba 8888.
+ */
+ink.SketchologyEngineWrapper.prototype.setBorderImage = function(
+ data, size, uri, borderImageProto, outOfBoundsColor) {
+ var outOfBoundsColorProto = new sketchology.proto.OutOfBoundsColor();
+ outOfBoundsColorProto.setRgba(outOfBoundsColor);
+ var commandProto = new sketchology.proto.Command();
+ commandProto.setSetOutOfBoundsColor(outOfBoundsColorProto);
+ this.handleCommand(commandProto);
+
+ var msg = {
+ 'imageData': data.buffer,
+ 'uri': uri,
+ 'width': size.width,
+ 'height': size.height,
+ 'assetType': sketchology.proto.ImageInfo.AssetType.BORDER
+ };
+ this.engineElement_.postMessage(['addImageData', msg]);
+
+ commandProto = new sketchology.proto.Command();
+ commandProto.setSetPageBorder(borderImageProto);
+ this.handleCommand(commandProto);
+};
+
+
+/**
+ * Sets the background image from a data URI.
+ *
+ * @param {Uint8ClampedArray} data
+ * @param {goog.math.Size} size
+ * @param {string} uri
+ * @param {!sketchology.proto.BackgroundImageInfo} bgImageProto
+ */
+ink.SketchologyEngineWrapper.prototype.setBackgroundImage = function(
+ data, size, uri, bgImageProto) {
+ var msg = {
+ 'imageData': data.buffer,
+ 'uri': uri,
+ 'width': size.width,
+ 'height': size.height,
+ 'assetType': sketchology.proto.ImageInfo.AssetType.DEFAULT
+ };
+ this.engineElement_.postMessage(['addImageData', msg]);
+
+ var commandProto = new sketchology.proto.Command();
+ commandProto.setBackgroundImage(bgImageProto);
+ this.handleCommand(commandProto);
+};
+
+
+/**
+ * Set the background color
+ * @param {ink.Color} color
+ */
+ink.SketchologyEngineWrapper.prototype.setBackgroundColor = function(color) {
+ var bgColorProto = new sketchology.proto.BackgroundColor();
+ bgColorProto.setRgba(color.getRgbaUint32()[0]);
+ var commandProto = new sketchology.proto.Command();
+ commandProto.setBackgroundColor(bgColorProto);
+ this.handleCommand(commandProto);
+};
+
+
+/**
+ * Sets the camera position.
+ *
+ * @param {!goog.math.Rect} cameraRect The camera rect.
+ */
+ink.SketchologyEngineWrapper.prototype.setCamera = function(cameraRect) {
+ var camera = cameraRect.toBox();
+ var rectProto = new sketchology.proto.Rect();
+ // Top and bottom are reversed in Sketchology for "reasons."
+ rectProto.setYhigh(camera.bottom);
+ rectProto.setXhigh(camera.right);
+ rectProto.setYlow(camera.top);
+ rectProto.setXlow(camera.left);
+ var commandProto = new sketchology.proto.Command();
+ commandProto.setCameraPosition(rectProto);
+ this.handleCommand(commandProto);
+};
+
+
+/**
+ * @private
+ * @param {sketchology.proto.Rect} rectProto rect js proto with top/bottom
+ * reversed
+ * @return {goog.math.Rect} proper CSS rect with top/bottom correct
+ */
+ink.SketchologyEngineWrapper.prototype.convertRect_ = function(rectProto) {
+ // Top and bottom are reversed in Sketchology for "reasons."
+ var box = new goog.math.Box(
+ rectProto.getYlowOrDefault(), rectProto.getXhighOrDefault(),
+ rectProto.getYhighOrDefault(), rectProto.getXlowOrDefault());
+ return box ?
+ goog.math.Rect.createFromBox(box) :
+ null;
+};
+
+
+/**
+ * Create a scaled rectangle with a given size/center scaled by factor.
+ *
+ * @param {!goog.math.Coordinate} center
+ * @param {!goog.math.Size} size
+ * @param {number} factor The scale factor
+ *
+ * @return {goog.math.Rect} The scaled rectangle.
+ * @private
+ */
+ink.SketchologyEngineWrapper.prototype.getScaledRect_ = function(
+ center, size, factor) {
+ size.width /= factor;
+ size.height /= factor;
+
+ var x = center.x - size.width / 2;
+ var y = center.y - size.height / 2;
+
+ var cameraRect = new goog.math.Rect(x, y, size.width, size.height);
+
+ goog.asserts.assert(
+ Math.round(cameraRect.getCenter().x) === Math.round(center.x) &&
+ Math.round(cameraRect.getCenter().y) === Math.round(center.y));
+
+ return cameraRect;
+};
+
+
+/**
+ * Sets the brush parameters.
+ *
+ * @param {Uint32Array} color rgba 32-bit unsigned color
+ * @param {number} strokeWidth brush size percent [0,1]
+ * @param {sketchology.proto.ToolParams.ToolType} toolType
+ * @param {sketchology.proto.BrushType} brushType
+ */
+ink.SketchologyEngineWrapper.prototype.brushUpdate =
+ function(color, strokeWidth, toolType, brushType) {
+ var self = this;
+ this.lastBrushUpdate_ = function() {
+ // LINE tools need special handling
+ if (toolType != sketchology.proto.ToolParams.ToolType.LINE) {
+ var toolParamsProto = new sketchology.proto.ToolParams();
+ toolParamsProto.setTool(toolType);
+ var commandProto = new sketchology.proto.Command();
+ commandProto.setToolParams(toolParamsProto);
+ this.handleCommand(commandProto);
+ } else {
+ var updateBrushData = {
+ 'brush': brushType,
+ 'rgba': color[0],
+ 'stroke_width': strokeWidth
+ };
+ self.engineElement_.postMessage(['updateBrush', updateBrushData]);
+ }
+ };
+ this.lastBrushUpdate_();
+};
+
+
+/** Clears the canvas. */
+ink.SketchologyEngineWrapper.prototype.clear = function() {
+ this.engineElement_.postMessage(['clear', '']);
+};
+
+
+/** Removes all elements from the document. */
+ink.SketchologyEngineWrapper.prototype.removeAll = function() {
+ this.engineElement_.postMessage(['removeAll', '']);
+};
+
+
+/**
+ * Sets or unsets readOnly on the canvas.
+ * @param {boolean} readOnly
+ */
+ink.SketchologyEngineWrapper.prototype.setReadOnly = function(readOnly) {
+ this.assignFlag(sketchology.proto.Flag.READ_ONLY_MODE, !!readOnly);
+};
+
+
+/**
+ * Assign a flag on the canvas
+ * @param {sketchology.proto.Flag} flag
+ * @param {boolean} enable
+ */
+ink.SketchologyEngineWrapper.prototype.assignFlag = function(flag, enable) {
+ var flagProto = new sketchology.proto.FlagAssignment();
+ flagProto.setFlag(flag);
+ flagProto.setBoolValue(!!enable);
+ var commandProto = new sketchology.proto.Command();
+ commandProto.setFlagAssignment(flagProto);
+ this.handleCommand(commandProto);
+};
+
+
+
+
+/**
+ * Sets element transforms.
+ * @param {Array.<string>} uuids
+ * @param {Array.<string>} encodedTransforms
+ */
+ink.SketchologyEngineWrapper.prototype.setElementTransforms = function(
+ uuids, encodedTransforms) {
+ if (uuids.length !== encodedTransforms.length) {
+ throw new Error('mismatch in transform array lengths');
+ }
+ this.engineElement_.postMessage([
+ 'setElementTransforms',
+ {'uuids': uuids, 'encoded_transforms': encodedTransforms}
+ ]);
+};
+
+/**
+ * Set callback flags for what data is attached to element callbacks.
+ * @param {!sketchology.proto.SetCallbackFlags} setCallbackFlags
+ */
+ink.SketchologyEngineWrapper.prototype.setCallbackFlags = function(
+ setCallbackFlags) {
+ var commandProto = new sketchology.proto.Command();
+ commandProto.setSetCallbackFlags(setCallbackFlags);
+ this.handleCommand(commandProto);
+};
+
+
+/**
+ * Sets the size of the page.
+ * @param {number} left
+ * @param {number} top
+ * @param {number} right
+ * @param {number} bottom
+ */
+ink.SketchologyEngineWrapper.prototype.setPageBounds =
+ function(left, top, right, bottom) {
+ this.pageLeft_ = left;
+ this.pageTop_ = top;
+ this.pageRight_ = right;
+ this.pageBottom_ = bottom;
+ var pageBounds = new sketchology.proto.Rect();
+ pageBounds.setXlow(this.pageLeft_);
+ pageBounds.setYlow(this.pageBottom_);
+ pageBounds.setXhigh(this.pageRight_);
+ pageBounds.setYhigh(this.pageTop_);
+ var commandProto = new sketchology.proto.Command();
+ commandProto.setPageBounds(pageBounds);
+ this.handleCommand(commandProto);
+};
+
+
+/**
+ * Deselects anything selected with the edit tool.
+ */
+ink.SketchologyEngineWrapper.prototype.deselectAll = function() {
+ throw new Error('deselectAll not yet implemented for NaCl.');
+};
+
+
+/**
+ * Start the PNG export process.
+ *
+ * @param {!sketchology.proto.ImageExport} exportProto
+ */
+ink.SketchologyEngineWrapper.prototype.exportPng = function(exportProto) {
+ var commandProto = new sketchology.proto.Command();
+ commandProto.setImageExport(exportProto);
+ this.handleCommand(commandProto);
+};
+
+
+/**
+ * Simple undo.
+ */
+ink.SketchologyEngineWrapper.prototype.undo = function() {
+ var commandProto = new sketchology.proto.Command();
+ commandProto.setUndo(new sketchology.proto.NoArgCommand());
+ this.handleCommand(commandProto);
+};
+
+
+/**
+ * Simple redo.
+ */
+ink.SketchologyEngineWrapper.prototype.redo = function() {
+ var commandProto = new sketchology.proto.Command();
+ commandProto.setRedo(new sketchology.proto.NoArgCommand());
+ this.handleCommand(commandProto);
+};
+
+
+/**
+ * Returns the current snapshot.
+ * @param {function(!sketchology.proto.Snapshot)} callback
+ */
+ink.SketchologyEngineWrapper.prototype.getSnapshot = function(callback) {
+ this.snapshotCallbacks_.push(callback);
+ this.engineElement_.postMessage(['getSnapshot']);
+};
+
+
+/**
+ * Loads a document from a snapshot.
+ *
+ * @param {!sketchology.proto.Snapshot} snapshotProto
+ */
+ink.SketchologyEngineWrapper.prototype.loadFromSnapshot =
+ function(snapshotProto) {
+ var bytes = this.wireSerializer_.serialize(snapshotProto);
+ var buf = new Uint8Array(bytes);
+ this.engineElement_.postMessage(['loadFromSnapshot', buf.buffer]);
+};
+
+
+/**
+ * Gets the raw engine object. Do not use this.
+ * @return {Object}
+ */
+ink.SketchologyEngineWrapper.prototype.getRawEngineObject = function() {
+ throw new Error('getRawEngineObject not supported for NaCl.');
+};
+
+
+/**
+ * Generates a snapshot based on a brix document.
+ * @param {!ink.util.RealtimeDocument} brixDoc
+ * @param {function(!sketchology.proto.Snapshot)} callback
+ */
+ink.SketchologyEngineWrapper.prototype.convertBrixDocumentToSnapshot =
+ function(brixDoc, callback) {
+ var model = brixDoc.getModel();
+ var root = model.getRoot();
+ var pages = root.get('pages');
+ var page = pages.get(0);
+ if (!page) {
+ throw Error('unable to get page from brix document.');
+ }
+ var elements = page.get('elements').asArray();
+
+ var jsonElements = [];
+ for (var i = 0; i < elements.length; i++) {
+ var element = elements[i];
+ jsonElements.push({'id': element.get('id'),
+ 'proto': element.get('proto'),
+ 'transform': element.get('transform')});
+ }
+ this.brixDocuments_.push(brixDoc);
+ this.brixConversionCallbacks_.push(callback);
+ this.engineElement_.postMessage(['convertBrixElements', jsonElements]);
+};
+
+
+/**
+ * @param {!sketchology.proto.Snapshot} snapshot
+ * @param {function(boolean)} callback
+ */
+ink.SketchologyEngineWrapper.prototype.snapshotHasPendingMutations =
+ function(snapshot, callback) {
+ var bytes = this.wireSerializer_.serialize(snapshot);
+ var buf = new Uint8Array(bytes);
+ this.snapshotHasPendingMutationsCallbacks_.push(callback);
+ this.engineElement_.postMessage(['snapshotHasPendingMutations', buf.buffer]);
+};
+
+
+/**
+ * @param {!sketchology.proto.Snapshot} snapshot
+ * @param {function(sketchology.proto.MutationPacket)} callback
+ */
+ink.SketchologyEngineWrapper.prototype.extractMutationPacket =
+ function(snapshot, callback) {
+ var bytes = this.wireSerializer_.serialize(snapshot);
+ var buf = new Uint8Array(bytes);
+ this.extractMutationPacketCallbacks_.push(callback);
+ this.engineElement_.postMessage(['extractMutationPacket', buf.buffer]);
+};
+
+
+/**
+ * @param {!sketchology.proto.Snapshot} snapshot
+ * @param {function(sketchology.proto.Snapshot)} callback
+ */
+ink.SketchologyEngineWrapper.prototype.clearPendingMutations = function(
+ snapshot, callback) {
+ var bytes = this.wireSerializer_.serialize(snapshot);
+ var buf = new Uint8Array(bytes);
+ this.clearPendingMutationsCallbacks_.push(callback);
+ this.engineElement_.postMessage(['clearPendingMutations', buf.buffer]);
+};
+
+
+/**
+ * Enable or disable hardware overlay by hiding or showing a 1-pixel div over
+ * the canvas.
+ *
+ * @param {boolean} enable
+ */
+ink.SketchologyEngineWrapper.prototype.setHardwareOverlay = function(enable) {
+ document.querySelector('#ink-engine-hwoverlay').style.display =
+ enable ? 'none' : 'block';
+};
+
+
+/**
+ * Calls the given callback once all previous asynchronous engine operations
+ * have been applied.
+ * @param {!Function} callback
+ */
+ink.SketchologyEngineWrapper.prototype.flush = function(callback) {
+ var commandProto = new sketchology.proto.Command();
+ var sp = new sketchology.proto.SequencePoint();
+ sp.setId(this.nextSequencePointId_);
+ this.sequencePointCallbacks_[this.nextSequencePointId_++] = callback;
+ commandProto.setSequencePoint(sp);
+ this.handleCommand(commandProto);
+};
diff --git a/chromium/third_party/ink/template/soy/soyutils_usegoog.js b/chromium/third_party/ink/template/soy/soyutils_usegoog.js
new file mode 100644
index 00000000000..8720f1caa3f
--- /dev/null
+++ b/chromium/third_party/ink/template/soy/soyutils_usegoog.js
@@ -0,0 +1,2401 @@
+/*
+ * Copyright 2008 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.
+ */
+
+/**
+ * @fileoverview
+ * Utility functions and classes for Soy gencode
+ *
+ * <p>
+ * This file contains utilities that should only be called by Soy-generated
+ * JS code. Please do not use these functions directly from
+ * your hand-written code. Their names all start with '$$', or exist within the
+ * soydata.VERY_UNSAFE namespace.
+ *
+ * <p>TODO(lukes): ensure that the above pattern is actually followed
+ * consistently.
+ *
+ * @author Garrett Boyer
+ * @author Mike Samuel
+ * @author Kai Huang
+ * @author Aharon Lanin
+ */
+goog.provide('soy');
+goog.provide('soy.asserts');
+goog.provide('soy.esc');
+goog.provide('soydata');
+goog.provide('soydata.SanitizedHtml');
+goog.provide('soydata.VERY_UNSAFE');
+
+goog.require('goog.array');
+goog.require('goog.asserts');
+goog.require('goog.debug');
+goog.require('goog.format');
+goog.require('goog.html.SafeHtml');
+goog.require('goog.html.SafeScript');
+goog.require('goog.html.SafeStyle');
+goog.require('goog.html.SafeStyleSheet');
+goog.require('goog.html.SafeUrl');
+goog.require('goog.html.TrustedResourceUrl');
+goog.require('goog.html.uncheckedconversions');
+goog.require('goog.i18n.BidiFormatter');
+goog.require('goog.i18n.bidi');
+goog.require('goog.object');
+goog.require('goog.soy.data.SanitizedContent');
+goog.require('goog.soy.data.SanitizedContentKind');
+goog.require('goog.soy.data.SanitizedCss');
+goog.require('goog.soy.data.SanitizedHtml');
+goog.require('goog.soy.data.SanitizedHtmlAttribute');
+goog.require('goog.soy.data.SanitizedJs');
+goog.require('goog.soy.data.SanitizedStyle');
+goog.require('goog.soy.data.SanitizedTrustedResourceUri');
+goog.require('goog.soy.data.SanitizedUri');
+goog.require('goog.soy.data.UnsanitizedText');
+goog.require('goog.string');
+goog.require('goog.string.Const');
+
+// -----------------------------------------------------------------------------
+// soydata: Defines typed strings, e.g. an HTML string {@code "a<b>c"} is
+// semantically distinct from the plain text string {@code "a<b>c"} and smart
+// templates can take that distinction into account.
+
+/**
+ * Checks whether a given value is of a given content kind.
+ *
+ * @param {*} value The value to be examined.
+ * @param {goog.soy.data.SanitizedContentKind} contentKind The desired content
+ * kind.
+ * @return {boolean} Whether the given value is of the given kind.
+ * @private
+ */
+soydata.isContentKind_ = function(value, contentKind) {
+ // TODO(aharon): This function should really include the assert on
+ // value.constructor that is currently sprinkled at most of the call sites.
+ // Unfortunately, that would require a (debug-mode-only) switch statement.
+ // TODO(aharon): Perhaps we should get rid of the contentKind property
+ // altogether and only at the constructor.
+ return value != null && value.contentKind === contentKind;
+};
+
+
+/**
+ * Returns a given value's contentDir property, constrained to a
+ * goog.i18n.bidi.Dir value or null. Returns null if the value is null,
+ * undefined, a primitive or does not have a contentDir property, or the
+ * property's value is not 1 (for LTR), -1 (for RTL), or 0 (for neutral).
+ *
+ * @param {*} value The value whose contentDir property, if any, is to
+ * be returned.
+ * @return {?goog.i18n.bidi.Dir} The contentDir property.
+ */
+soydata.getContentDir = function(value) {
+ if (value != null) {
+ switch (value.contentDir) {
+ case goog.i18n.bidi.Dir.LTR:
+ return goog.i18n.bidi.Dir.LTR;
+ case goog.i18n.bidi.Dir.RTL:
+ return goog.i18n.bidi.Dir.RTL;
+ case goog.i18n.bidi.Dir.NEUTRAL:
+ return goog.i18n.bidi.Dir.NEUTRAL;
+ }
+ }
+ return null;
+};
+
+
+/**
+ * This class is only a holder for {@code soydata.SanitizedHtml.from}. Do not
+ * instantiate or extend it. Use {@code goog.soy.data.SanitizedHtml} instead.
+ *
+ * @constructor
+ * @extends {goog.soy.data.SanitizedHtml}
+ * @abstract
+ */
+soydata.SanitizedHtml = function() {
+ soydata.SanitizedHtml.base(this, 'constructor'); // Throws an exception.
+};
+goog.inherits(soydata.SanitizedHtml, goog.soy.data.SanitizedHtml);
+
+/**
+ * Returns a SanitizedHtml object for a particular value. The content direction
+ * is preserved.
+ *
+ * This HTML-escapes the value unless it is already SanitizedHtml or SafeHtml.
+ *
+ * @param {*} value The value to convert. If it is already a SanitizedHtml
+ * object, it is left alone.
+ * @return {!goog.soy.data.SanitizedHtml} A SanitizedHtml object derived from
+ * the stringified value. It is escaped unless the input is SanitizedHtml or
+ * SafeHtml.
+ */
+soydata.SanitizedHtml.from = function(value) {
+ // The check is soydata.isContentKind_() inlined for performance.
+ if (value != null &&
+ value.contentKind === goog.soy.data.SanitizedContentKind.HTML) {
+ goog.asserts.assert(value.constructor === goog.soy.data.SanitizedHtml);
+ return /** @type {!goog.soy.data.SanitizedHtml} */ (value);
+ }
+ if (value instanceof goog.html.SafeHtml) {
+ return soydata.VERY_UNSAFE.ordainSanitizedHtml(
+ goog.html.SafeHtml.unwrap(value), value.getDirection());
+ }
+ return soydata.VERY_UNSAFE.ordainSanitizedHtml(
+ soy.esc.$$escapeHtmlHelper(String(value)), soydata.getContentDir(value));
+};
+
+
+/**
+ * Empty string, used as a type in Soy templates.
+ * @enum {string}
+ * @private
+ */
+soydata.$$EMPTY_STRING_ = {
+ VALUE: ''
+};
+
+
+/**
+ * Creates a factory for SanitizedContent types.
+ *
+ * This is a hack so that the soydata.VERY_UNSAFE.ordainSanitized* can
+ * instantiate Sanitized* classes, without making the Sanitized* constructors
+ * publicly usable. Requiring all construction to use the VERY_UNSAFE names
+ * helps callers and their reviewers easily tell that creating SanitizedContent
+ * is not always safe and calls for careful review.
+ *
+ * @param {function(new: T)} ctor A constructor.
+ * @return {!function(*, ?goog.i18n.bidi.Dir=): T} A factory that takes
+ * content and an optional content direction and returns a new instance. If
+ * the content direction is undefined, ctor.prototype.contentDir is used.
+ * @template T
+ * @private
+ */
+soydata.$$makeSanitizedContentFactory_ = function(ctor) {
+ /**
+ * @param {string} content
+ * @constructor
+ * @extends {goog.soy.data.SanitizedContent}
+ */
+ function InstantiableCtor(content) {
+ /** @override */
+ this.content = content;
+ }
+ InstantiableCtor.prototype = ctor.prototype;
+ /**
+ * Creates a ctor-type SanitizedContent instance.
+ *
+ * @param {*} content The content to put in the instance.
+ * @param {?goog.i18n.bidi.Dir=} opt_contentDir The content direction. If
+ * undefined, ctor.prototype.contentDir is used.
+ * @return {!goog.soy.data.SanitizedContent} The new instance. It is actually
+ * of type T above (ctor's type, a descendant of SanitizedContent), but
+ * there is no way to express that here.
+ */
+ function sanitizedContentFactory(content, opt_contentDir) {
+ var result = new InstantiableCtor(String(content));
+ if (opt_contentDir !== undefined) {
+ result.contentDir = opt_contentDir;
+ }
+ return result;
+ }
+ return sanitizedContentFactory;
+};
+
+
+/**
+ * Creates a factory for SanitizedContent types that should always have their
+ * default directionality.
+ *
+ * This is a hack so that the soydata.VERY_UNSAFE.ordainSanitized* can
+ * instantiate Sanitized* classes, without making the Sanitized* constructors
+ * publicly usable. Requiring all construction to use the VERY_UNSAFE names
+ * helps callers and their reviewers easily tell that creating SanitizedContent
+ * is not always safe and calls for careful review.
+ *
+ * @param {function(new: T, string)} ctor A constructor.
+ * @return {!function(*): T} A factory that takes content and returns a new
+ * instance (with default directionality, i.e. ctor.prototype.contentDir).
+ * @template T
+ * @private
+ */
+soydata.$$makeSanitizedContentFactoryWithDefaultDirOnly_ = function(ctor) {
+ /**
+ * @param {string} content
+ * @constructor
+ * @extends {goog.soy.data.SanitizedContent}
+ */
+ function InstantiableCtor(content) {
+ /** @override */
+ this.content = content;
+ }
+ InstantiableCtor.prototype = ctor.prototype;
+ /**
+ * Creates a ctor-type SanitizedContent instance.
+ *
+ * @param {*} content The content to put in the instance.
+ * @return {!goog.soy.data.SanitizedContent} The new instance. It is actually
+ * of type T above (ctor's type, a descendant of SanitizedContent), but
+ * there is no way to express that here.
+ */
+ function sanitizedContentFactory(content) {
+ var result = new InstantiableCtor(String(content));
+ return result;
+ }
+ return sanitizedContentFactory;
+};
+
+
+// -----------------------------------------------------------------------------
+// Sanitized content ordainers. Please use these with extreme caution (with the
+// exception of markUnsanitizedText). A good recommendation is to limit usage
+// of these to just a handful of files in your source tree where usages can be
+// carefully audited.
+
+
+/**
+ * Protects a string from being used in an noAutoescaped context.
+ *
+ * This is useful for content where there is significant risk of accidental
+ * unescaped usage in a Soy template. A great case is for user-controlled
+ * data that has historically been a source of vulernabilities.
+ *
+ * @param {*} content Text to protect.
+ * @param {?goog.i18n.bidi.Dir=} opt_contentDir The content direction; null if
+ * unknown and thus to be estimated when necessary. Default: null.
+ * @return {!goog.soy.data.UnsanitizedText} A wrapper that is rejected by the
+ * Soy noAutoescape print directive.
+ */
+soydata.markUnsanitizedText = function(content, opt_contentDir) {
+ return new goog.soy.data.UnsanitizedText(content, opt_contentDir);
+};
+
+
+/**
+ * Takes a leap of faith that the provided content is "safe" HTML.
+ *
+ * @param {*} content A string of HTML that can safely be embedded in
+ * a PCDATA context in your app. If you would be surprised to find that an
+ * HTML sanitizer produced {@code s} (e.g. it runs code or fetches bad URLs)
+ * and you wouldn't write a template that produces {@code s} on security or
+ * privacy grounds, then don't pass {@code s} here.
+ * @param {?goog.i18n.bidi.Dir=} opt_contentDir The content direction; null if
+ * unknown and thus to be estimated when necessary. Default: null.
+ * @return {!goog.soy.data.SanitizedHtml} Sanitized content wrapper that
+ * indicates to Soy not to escape when printed as HTML.
+ */
+soydata.VERY_UNSAFE.ordainSanitizedHtml =
+ soydata.$$makeSanitizedContentFactory_(goog.soy.data.SanitizedHtml);
+
+
+/**
+ * Takes a leap of faith that the provided content is "safe" (non-attacker-
+ * controlled, XSS-free) Javascript.
+ *
+ * @param {*} content Javascript source that when evaluated does not
+ * execute any attacker-controlled scripts.
+ * @return {!goog.soy.data.SanitizedJs} Sanitized content wrapper that indicates
+ * to Soy not to escape when printed as Javascript source.
+ */
+soydata.VERY_UNSAFE.ordainSanitizedJs =
+ soydata.$$makeSanitizedContentFactoryWithDefaultDirOnly_(
+ goog.soy.data.SanitizedJs);
+
+
+/**
+ * Takes a leap of faith that the provided content is "safe" to use as a URI
+ * in a Soy template.
+ *
+ * This creates a Soy SanitizedContent object which indicates to Soy there is
+ * no need to escape it when printed as a URI (e.g. in an href or src
+ * attribute), such as if it's already been encoded or if it's a Javascript:
+ * URI.
+ *
+ * @param {*} content A chunk of URI that the caller knows is safe to
+ * emit in a template.
+ * @return {!goog.soy.data.SanitizedUri} Sanitized content wrapper that
+ * indicates to Soy not to escape or filter when printed in URI context.
+ */
+soydata.VERY_UNSAFE.ordainSanitizedUri =
+ soydata.$$makeSanitizedContentFactoryWithDefaultDirOnly_(
+ goog.soy.data.SanitizedUri);
+
+
+/**
+ * Takes a leap of faith that the provided content is "safe" to use as a
+ * TrustedResourceUri in a Soy template.
+ *
+ * This creates a Soy SanitizedContent object which indicates to Soy there is
+ * no need to filter it when printed as a TrustedResourceUri.
+ *
+ * @param {*} content A chunk of TrustedResourceUri such as that the caller
+ * knows is safe to emit in a template.
+ * @return {!goog.soy.data.SanitizedTrustedResourceUri} Sanitized content
+ * wrapper that indicates to Soy not to escape or filter when printed in
+ * TrustedResourceUri context.
+ */
+soydata.VERY_UNSAFE.ordainSanitizedTrustedResourceUri =
+ soydata.$$makeSanitizedContentFactoryWithDefaultDirOnly_(
+ goog.soy.data.SanitizedTrustedResourceUri);
+
+
+/**
+ * Takes a leap of faith that the provided content is "safe" to use as an
+ * HTML attribute.
+ *
+ * @param {*} content An attribute name and value, such as
+ * {@code dir="ltr"}.
+ * @return {!goog.soy.data.SanitizedHtmlAttribute} Sanitized content wrapper
+ * that indicates to Soy not to escape when printed as an HTML attribute.
+ */
+soydata.VERY_UNSAFE.ordainSanitizedHtmlAttribute =
+ soydata.$$makeSanitizedContentFactoryWithDefaultDirOnly_(
+ goog.soy.data.SanitizedHtmlAttribute);
+
+
+/**
+ * Takes a leap of faith that the provided content is "safe" to use as STYLE
+ * in a style attribute.
+ *
+ * @param {*} content CSS, such as {@code color:#c3d9ff}.
+ * @return {!goog.soy.data.SanitizedStyle} Sanitized style wrapper that
+ * indicates to Soy there is no need to escape or filter when printed in CSS
+ * context.
+ */
+soydata.VERY_UNSAFE.ordainSanitizedStyle =
+ soydata.$$makeSanitizedContentFactoryWithDefaultDirOnly_(
+ goog.soy.data.SanitizedStyle);
+
+
+/**
+ * Takes a leap of faith that the provided content is "safe" to use as CSS
+ * in a style block.
+ *
+ * @param {*} content CSS, such as {@code color:#c3d9ff}.
+ * @return {!goog.soy.data.SanitizedCss} Sanitized CSS wrapper that indicates to
+ * Soy there is no need to escape or filter when printed in CSS context.
+ */
+soydata.VERY_UNSAFE.ordainSanitizedCss =
+ soydata.$$makeSanitizedContentFactoryWithDefaultDirOnly_(
+ goog.soy.data.SanitizedCss);
+
+
+// -----------------------------------------------------------------------------
+// Soy-generated utilities in the soy namespace. Contains implementations for
+// common soyfunctions (e.g. keys()) and escaping/print directives.
+
+
+/**
+ * Whether the locale is right-to-left.
+ *
+ * @type {boolean}
+ */
+soy.$$IS_LOCALE_RTL = goog.i18n.bidi.IS_RTL;
+
+
+/**
+ * Builds an augmented map. The returned map will contain mappings from both
+ * the base map and the additional map. If the same key appears in both, then
+ * the value from the additional map will be visible, while the value from the
+ * base map will be hidden. The base map will be used, but not modified.
+ *
+ * @param {!Object} baseMap The original map to augment.
+ * @param {!Object} additionalMap A map containing the additional mappings.
+ * @return {!Object} An augmented map containing both the original and
+ * additional mappings.
+ */
+soy.$$augmentMap = function(baseMap, additionalMap) {
+ return soy.$$assignDefaults(soy.$$assignDefaults({}, additionalMap), baseMap);
+};
+
+
+/**
+ * Copies extra properties into an object if they do not already exist. The
+ * destination object is mutated in the process.
+ *
+ * @param {!Object} obj The destination object to update.
+ * @param {!Object} defaults An object with default properties to apply.
+ * @return {!Object} The destination object for convenience.
+ */
+soy.$$assignDefaults = function(obj, defaults) {
+ for (var key in defaults) {
+ if (!(key in obj)) {
+ obj[key] = defaults[key];
+ }
+ }
+
+ return obj;
+};
+
+
+/**
+ * Checks that the given map key is a string.
+ * @param {*} key Key to check.
+ * @return {string} The given key.
+ */
+soy.$$checkMapKey = function(key) {
+ // TODO: Support map literal with nonstring key.
+ if ((typeof key) != 'string') {
+ throw Error(
+ 'Map literal\'s key expression must evaluate to string' +
+ ' (encountered type "' + (typeof key) + '").');
+ }
+ return key;
+};
+
+
+/**
+ * Gets the keys in a map as an array. There are no guarantees on the order.
+ * @param {Object} map The map to get the keys of.
+ * @return {!Array<string>} The array of keys in the given map.
+ */
+soy.$$getMapKeys = function(map) {
+ var mapKeys = [];
+ for (var key in map) {
+ mapKeys.push(key);
+ }
+ return mapKeys;
+};
+
+
+/**
+ * Returns the argument if it is not null.
+ *
+ * @param {T} val The value to check
+ * @return {T} val if is isn't null
+ * @template T
+ */
+soy.$$checkNotNull = function(val) {
+ if (val == null) {
+ throw Error('unexpected null value');
+ }
+ return val;
+};
+
+
+/**
+ * Parses the given string into a base 10 integer. Returns null if parse is
+ * unsuccessful.
+ * @param {string} str The string to parse
+ * @return {?number} The string parsed as a base 10 integer, or null if
+ * unsuccessful
+ */
+soy.$$parseInt = function(str) {
+ var parsed = parseInt(str, 10);
+ return isNaN(parsed) ? null : parsed;
+};
+
+
+/**
+ * Parses the given string into a float. Returns null if parse is unsuccessful.
+ * @param {string} str The string to parse
+ * @return {?number} The string parsed as a float, or null if unsuccessful.
+ */
+soy.$$parseFloat = function(str) {
+ var parsed = parseFloat(str);
+ return isNaN(parsed) ? null : parsed;
+};
+
+
+/**
+ * Gets a consistent unique id for the given delegate template name. Two calls
+ * to this function will return the same id if and only if the input names are
+ * the same.
+ *
+ * <p> Important: This function must always be called with a string constant.
+ *
+ * <p> If Closure Compiler is not being used, then this is just this identity
+ * function. If Closure Compiler is being used, then each call to this function
+ * will be replaced with a short string constant, which will be consistent per
+ * input name.
+ *
+ * @param {string} delTemplateName The delegate template name for which to get a
+ * consistent unique id.
+ * @return {string} A unique id that is consistent per input name.
+ *
+ * @idGenerator {consistent}
+ */
+soy.$$getDelTemplateId = function(delTemplateName) {
+ return delTemplateName;
+};
+
+
+/**
+ * Map from registered delegate template key to the priority of the
+ * implementation.
+ * @type {Object}
+ * @private
+ */
+soy.$$DELEGATE_REGISTRY_PRIORITIES_ = {};
+
+/**
+ * Map from registered delegate template key to the implementation function.
+ * @type {Object}
+ * @private
+ */
+soy.$$DELEGATE_REGISTRY_FUNCTIONS_ = {};
+
+
+/**
+ * Registers a delegate implementation. If the same delegate template key (id
+ * and variant) has been registered previously, then priority values are
+ * compared and only the higher priority implementation is stored (if
+ * priorities are equal, an error is thrown).
+ *
+ * @param {string} delTemplateId The delegate template id.
+ * @param {string} delTemplateVariant The delegate template variant (can be
+ * empty string).
+ * @param {number} delPriority The implementation's priority value.
+ * @param {Function} delFn The implementation function.
+ */
+soy.$$registerDelegateFn = function(
+ delTemplateId, delTemplateVariant, delPriority, delFn) {
+
+ var mapKey = 'key_' + delTemplateId + ':' + delTemplateVariant;
+ var currPriority = soy.$$DELEGATE_REGISTRY_PRIORITIES_[mapKey];
+ if (currPriority === undefined || delPriority > currPriority) {
+ // Registering new or higher-priority function: replace registry entry.
+ soy.$$DELEGATE_REGISTRY_PRIORITIES_[mapKey] = delPriority;
+ soy.$$DELEGATE_REGISTRY_FUNCTIONS_[mapKey] = delFn;
+ } else if (delPriority == currPriority) {
+ // Registering same-priority function: error.
+ throw Error(
+ 'Encountered two active delegates with the same priority ("' +
+ delTemplateId + ':' + delTemplateVariant + '").');
+ } else {
+ // Registering lower-priority function: do nothing.
+ }
+};
+
+
+/**
+ * Retrieves the (highest-priority) implementation that has been registered for
+ * a given delegate template key (id and variant). If no implementation has
+ * been registered for the key, then the fallback is the same id with empty
+ * variant. If the fallback is also not registered, and allowsEmptyDefault is
+ * true, then returns an implementation that is equivalent to an empty template
+ * (i.e. rendered output would be empty string).
+ *
+ * @param {string} delTemplateId The delegate template id.
+ * @param {string} delTemplateVariant The delegate template variant (can be
+ * empty string).
+ * @param {boolean} allowsEmptyDefault Whether to default to the empty template
+ * function if there's no active implementation.
+ * @return {Function} The retrieved implementation function.
+ */
+soy.$$getDelegateFn = function(
+ delTemplateId, delTemplateVariant, allowsEmptyDefault) {
+
+ var delFn = soy.$$DELEGATE_REGISTRY_FUNCTIONS_[
+ 'key_' + delTemplateId + ':' + delTemplateVariant];
+ if (! delFn && delTemplateVariant != '') {
+ // Fallback to empty variant.
+ delFn = soy.$$DELEGATE_REGISTRY_FUNCTIONS_['key_' + delTemplateId + ':'];
+ }
+
+ if (delFn) {
+ return delFn;
+ } else if (allowsEmptyDefault) {
+ return soy.$$EMPTY_TEMPLATE_FN_;
+ } else {
+ throw Error(
+ 'Found no active impl for delegate call to "' + delTemplateId + ':' +
+ delTemplateVariant + '" (and not allowemptydefault="true").');
+ }
+};
+
+
+/**
+ * Private helper soy.$$getDelegateFn(). This is the empty template function
+ * that is returned whenever there's no delegate implementation found.
+ *
+ * @param {Object<string, *>=} opt_data
+ * @param {Object<string, *>=} opt_ijData
+ * @param {Object<string, *>=} opt_ijData_deprecated TODO(b/36644846): remove
+ * @return {string}
+ * @private
+ */
+soy.$$EMPTY_TEMPLATE_FN_ = function(
+ opt_data, opt_ijData, opt_ijData_deprecated) {
+ return '';
+};
+
+
+// -----------------------------------------------------------------------------
+// Internal sanitized content wrappers.
+
+
+/**
+ * Creates a SanitizedContent factory for SanitizedContent types for internal
+ * Soy let and param blocks.
+ *
+ * This is a hack within Soy so that SanitizedContent objects created via let
+ * and param blocks will truth-test as false if they are empty string.
+ * Tricking the Javascript runtime to treat empty SanitizedContent as falsey is
+ * not possible, and changing the Soy compiler to wrap every boolean statement
+ * for just this purpose is impractical. Instead, we just avoid wrapping empty
+ * string as SanitizedContent, since it's a no-op for empty strings anyways.
+ *
+ * @param {function(new: T)} ctor A constructor.
+ * @return {!function(*, ?goog.i18n.bidi.Dir=): (T|soydata.$$EMPTY_STRING_)}
+ * A factory that takes content and an optional content direction and
+ * returns a new instance, or an empty string. If the content direction is
+ * undefined, ctor.prototype.contentDir is used.
+ * @template T
+ * @private
+ */
+soydata.$$makeSanitizedContentFactoryForInternalBlocks_ = function(ctor) {
+ /**
+ * @param {string} content
+ * @constructor
+ * @extends {goog.soy.data.SanitizedContent}
+ */
+ function InstantiableCtor(content) {
+ /** @override */
+ this.content = content;
+ }
+ InstantiableCtor.prototype = ctor.prototype;
+ /**
+ * Creates a ctor-type SanitizedContent instance.
+ *
+ * @param {*} content The content to put in the instance.
+ * @param {?goog.i18n.bidi.Dir=} opt_contentDir The content direction. If
+ * undefined, ctor.prototype.contentDir is used.
+ * @return {!goog.soy.data.SanitizedContent|soydata.$$EMPTY_STRING_} The new
+ * instance, or an empty string. A new instance is actually of type T
+ * above (ctor's type, a descendant of SanitizedContent), but there's no
+ * way to express that here.
+ */
+ function sanitizedContentFactory(content, opt_contentDir) {
+ var contentString = String(content);
+ if (!contentString) {
+ return soydata.$$EMPTY_STRING_.VALUE;
+ }
+ var result = new InstantiableCtor(contentString);
+ if (opt_contentDir !== undefined) {
+ result.contentDir = opt_contentDir;
+ }
+ return result;
+ }
+ return sanitizedContentFactory;
+};
+
+
+/**
+ * Creates a SanitizedContent factory for SanitizedContent types that should
+ * always have their default directionality for internal Soy let and param
+ * blocks.
+ *
+ * This is a hack within Soy so that SanitizedContent objects created via let
+ * and param blocks will truth-test as false if they are empty string.
+ * Tricking the Javascript runtime to treat empty SanitizedContent as falsey is
+ * not possible, and changing the Soy compiler to wrap every boolean statement
+ * for just this purpose is impractical. Instead, we just avoid wrapping empty
+ * string as SanitizedContent, since it's a no-op for empty strings anyways.
+ *
+ * @param {function(new: T)} ctor A constructor.
+ * @return {!function(*): (T|soydata.$$EMPTY_STRING_)} A
+ * factory that takes content and returns a
+ * new instance (with default directionality, i.e.
+ * ctor.prototype.contentDir), or an empty string.
+ * @template T
+ * @private
+ */
+soydata.$$makeSanitizedContentFactoryWithDefaultDirOnlyForInternalBlocks_ =
+ function(ctor) {
+ /**
+ * @param {string} content
+ * @constructor
+ * @extends {goog.soy.data.SanitizedContent}
+ */
+ function InstantiableCtor(content) {
+ /** @override */
+ this.content = content;
+ }
+ InstantiableCtor.prototype = ctor.prototype;
+ /**
+ * Creates a ctor-type SanitizedContent instance.
+ *
+ * @param {*} content The content to put in the instance.
+ * @return {!goog.soy.data.SanitizedContent|soydata.$$EMPTY_STRING_} The new
+ * instance, or an empty string. A new instance is actually of type T
+ * above (ctor's type, a descendant of SanitizedContent), but there's no
+ * way to express that here.
+ */
+ function sanitizedContentFactory(content) {
+ var contentString = String(content);
+ if (!contentString) {
+ return soydata.$$EMPTY_STRING_.VALUE;
+ }
+ var result = new InstantiableCtor(contentString);
+ return result;
+ }
+ return sanitizedContentFactory;
+};
+
+
+/**
+ * Creates kind="text" block contents (internal use only).
+ *
+ * @param {*} content Text.
+ * @param {?goog.i18n.bidi.Dir=} opt_contentDir The content direction; null if
+ * unknown and thus to be estimated when necessary. Default: null.
+ * @return {!goog.soy.data.UnsanitizedText|soydata.$$EMPTY_STRING_} Wrapped result.
+ */
+soydata.$$markUnsanitizedTextForInternalBlocks = function(
+ content, opt_contentDir) {
+ var contentString = String(content);
+ if (!contentString) {
+ return soydata.$$EMPTY_STRING_.VALUE;
+ }
+ return new goog.soy.data.UnsanitizedText(contentString, opt_contentDir);
+};
+
+
+/**
+ * Creates kind="html" block contents (internal use only).
+ *
+ * @param {*} content Text.
+ * @param {?goog.i18n.bidi.Dir=} opt_contentDir The content direction; null if
+ * unknown and thus to be estimated when necessary. Default: null.
+ * @return {!goog.soy.data.SanitizedHtml|soydata.$$EMPTY_STRING_} Wrapped
+ * result.
+ */
+soydata.VERY_UNSAFE.$$ordainSanitizedHtmlForInternalBlocks =
+ soydata.$$makeSanitizedContentFactoryForInternalBlocks_(
+ goog.soy.data.SanitizedHtml);
+
+
+/**
+ * Creates kind="js" block contents (internal use only).
+ *
+ * @param {*} content Text.
+ * @return {!goog.soy.data.SanitizedJs|soydata.$$EMPTY_STRING_} Wrapped result.
+ */
+soydata.VERY_UNSAFE.$$ordainSanitizedJsForInternalBlocks =
+ soydata.$$makeSanitizedContentFactoryWithDefaultDirOnlyForInternalBlocks_(
+ goog.soy.data.SanitizedJs);
+
+
+/**
+ * Creates kind="trustedResourceUri" block contents (internal use only).
+ *
+ * @param {*} content Text.
+ * @return {goog.soy.data.SanitizedTrustedResourceUri|soydata.$$EMPTY_STRING_}
+ * Wrapped result.
+ */
+soydata.VERY_UNSAFE.$$ordainSanitizedTrustedResourceUriForInternalBlocks =
+ soydata.$$makeSanitizedContentFactoryWithDefaultDirOnlyForInternalBlocks_(
+ goog.soy.data.SanitizedTrustedResourceUri);
+
+
+/**
+ * Creates kind="uri" block contents (internal use only).
+ *
+ * @param {*} content Text.
+ * @return {goog.soy.data.SanitizedUri|soydata.$$EMPTY_STRING_} Wrapped result.
+ */
+soydata.VERY_UNSAFE.$$ordainSanitizedUriForInternalBlocks =
+ soydata.$$makeSanitizedContentFactoryWithDefaultDirOnlyForInternalBlocks_(
+ goog.soy.data.SanitizedUri);
+
+
+/**
+ * Creates kind="attributes" block contents (internal use only).
+ *
+ * @param {*} content Text.
+ * @return {goog.soy.data.SanitizedHtmlAttribute|soydata.$$EMPTY_STRING_}
+ * Wrapped result.
+ */
+soydata.VERY_UNSAFE.$$ordainSanitizedAttributesForInternalBlocks =
+ soydata.$$makeSanitizedContentFactoryWithDefaultDirOnlyForInternalBlocks_(
+ goog.soy.data.SanitizedHtmlAttribute);
+
+
+/**
+ * Creates kind="style" block contents (internal use only).
+ *
+ * @param {*} content Text.
+ * @return {goog.soy.data.SanitizedStyle|soydata.$$EMPTY_STRING_} Wrapped
+ * result.
+ */
+soydata.VERY_UNSAFE.$$ordainSanitizedStyleForInternalBlocks =
+ soydata.$$makeSanitizedContentFactoryWithDefaultDirOnlyForInternalBlocks_(
+ goog.soy.data.SanitizedStyle);
+
+
+/**
+ * Creates kind="css" block contents (internal use only).
+ *
+ * @param {*} content Text.
+ * @return {goog.soy.data.SanitizedCss|soydata.$$EMPTY_STRING_} Wrapped result.
+ */
+soydata.VERY_UNSAFE.$$ordainSanitizedCssForInternalBlocks =
+ soydata.$$makeSanitizedContentFactoryWithDefaultDirOnlyForInternalBlocks_(
+ goog.soy.data.SanitizedCss);
+
+
+// -----------------------------------------------------------------------------
+// Escape/filter/normalize.
+
+
+/**
+ * Returns a SanitizedHtml object for a particular value. The content direction
+ * is preserved.
+ *
+ * This HTML-escapes the value unless it is already SanitizedHtml. Escapes
+ * double quote '"' in addition to '&', '<', and '>' so that a string can be
+ * included in an HTML tag attribute value within double quotes.
+ *
+ * @param {*} value The value to convert. If it is already a SanitizedHtml
+ * object, it is left alone.
+ * @return {!goog.soy.data.SanitizedHtml} An escaped version of value.
+ */
+soy.$$escapeHtml = function(value) {
+ return soydata.SanitizedHtml.from(value);
+};
+
+
+/**
+ * Strips unsafe tags to convert a string of untrusted HTML into HTML that
+ * is safe to embed. The content direction is preserved.
+ *
+ * @param {?} value The string-like value to be escaped. May not be a string,
+ * but the value will be coerced to a string.
+ * @param {Array<string>=} opt_safeTags Additional tag names to whitelist.
+ * @return {!goog.soy.data.SanitizedHtml} A sanitized and normalized version of
+ * value.
+ */
+soy.$$cleanHtml = function(value, opt_safeTags) {
+ if (soydata.isContentKind_(value, goog.soy.data.SanitizedContentKind.HTML)) {
+ goog.asserts.assert(value.constructor === goog.soy.data.SanitizedHtml);
+ return /** @type {!goog.soy.data.SanitizedHtml} */ (value);
+ }
+ var tagWhitelist;
+ if (opt_safeTags) {
+ tagWhitelist = goog.object.createSet(opt_safeTags);
+ goog.object.extend(tagWhitelist, soy.esc.$$SAFE_TAG_WHITELIST_);
+ } else {
+ tagWhitelist = soy.esc.$$SAFE_TAG_WHITELIST_;
+ }
+ return soydata.VERY_UNSAFE.ordainSanitizedHtml(
+ soy.$$stripHtmlTags(value, tagWhitelist), soydata.getContentDir(value));
+};
+
+
+/**
+ * Escapes HTML, except preserves entities.
+ *
+ * Used mainly internally for escaping message strings in attribute and rcdata
+ * context, where we explicitly want to preserve any existing entities.
+ *
+ * @param {*} value Value to normalize.
+ * @return {string} A value safe to insert in HTML without any quotes or angle
+ * brackets.
+ */
+soy.$$normalizeHtml = function(value) {
+ return soy.esc.$$normalizeHtmlHelper(value);
+};
+
+
+/**
+ * Escapes HTML special characters in a string so that it can be embedded in
+ * RCDATA.
+ * <p>
+ * Escapes HTML special characters so that the value will not prematurely end
+ * the body of a tag like {@code <textarea>} or {@code <title>}. RCDATA tags
+ * cannot contain other HTML entities, so it is not strictly necessary to escape
+ * HTML special characters except when part of that text looks like an HTML
+ * entity or like a close tag : {@code </textarea>}.
+ * <p>
+ * Will normalize known safe HTML to make sure that sanitized HTML (which could
+ * contain an innocuous {@code </textarea>} don't prematurely end an RCDATA
+ * element.
+ *
+ * @param {?} value The string-like value to be escaped. May not be a string,
+ * but the value will be coerced to a string.
+ * @return {string} An escaped version of value.
+ */
+soy.$$escapeHtmlRcdata = function(value) {
+ if (soydata.isContentKind_(value, goog.soy.data.SanitizedContentKind.HTML)) {
+ goog.asserts.assert(value.constructor === goog.soy.data.SanitizedHtml);
+ return soy.esc.$$normalizeHtmlHelper(value.getContent());
+ }
+ return soy.esc.$$escapeHtmlHelper(value);
+};
+
+
+/**
+ * Matches any/only HTML5 void elements' start tags.
+ * See http://www.w3.org/TR/html-markup/syntax.html#syntax-elements
+ * @type {RegExp}
+ * @private
+ */
+soy.$$HTML5_VOID_ELEMENTS_ = new RegExp(
+ '^<(?:area|base|br|col|command|embed|hr|img|input' +
+ '|keygen|link|meta|param|source|track|wbr)\\b');
+
+
+/**
+ * Removes HTML tags from a string of known safe HTML.
+ * If opt_tagWhitelist is not specified or is empty, then
+ * the result can be used as an attribute value.
+ *
+ * @param {*} value The HTML to be escaped. May not be a string, but the
+ * value will be coerced to a string.
+ * @param {Object<string, boolean>=} opt_tagWhitelist Has an own property whose
+ * name is a lower-case tag name and whose value is {@code 1} for
+ * each element that is allowed in the output.
+ * @return {string} A representation of value without disallowed tags,
+ * HTML comments, or other non-text content.
+ */
+soy.$$stripHtmlTags = function(value, opt_tagWhitelist) {
+ if (!opt_tagWhitelist) {
+ // If we have no white-list, then use a fast track which elides all tags.
+ return String(value).replace(soy.esc.$$HTML_TAG_REGEX_, '')
+ // This is just paranoia since callers should normalize the result
+ // anyway, but if they didn't, it would be necessary to ensure that
+ // after the first replace non-tag uses of < do not recombine into
+ // tags as in "<<foo>script>alert(1337)</<foo>script>".
+ .replace(soy.esc.$$LT_REGEX_, '&lt;');
+ }
+
+ // Escapes '[' so that we can use [123] below to mark places where tags
+ // have been removed.
+ var html = String(value).replace(/\[/g, '&#91;');
+
+ // Consider all uses of '<' and replace whitelisted tags with markers like
+ // [1] which are indices into a list of approved tag names.
+ // Replace all other uses of < and > with entities.
+ var tags = [];
+ var attrs = [];
+ html = html.replace(
+ soy.esc.$$HTML_TAG_REGEX_,
+ function(tok, tagName) {
+ if (tagName) {
+ tagName = tagName.toLowerCase();
+ if (opt_tagWhitelist.hasOwnProperty(tagName) &&
+ opt_tagWhitelist[tagName]) {
+ var isClose = tok.charAt(1) == '/';
+ var index = tags.length;
+ var start = '</';
+ var attributes = '';
+ if (!isClose) {
+ start = '<';
+ var match;
+ while ((match = soy.esc.$$HTML_ATTRIBUTE_REGEX_.exec(tok))) {
+ if (match[1] && match[1].toLowerCase() == 'dir') {
+ var dir = match[2];
+ if (dir) {
+ if (dir.charAt(0) == '\'' || dir.charAt(0) == '"') {
+ dir = dir.substr(1, dir.length - 2);
+ }
+ dir = dir.toLowerCase();
+ if (dir == 'ltr' || dir == 'rtl' || dir == 'auto') {
+ attributes = ' dir="' + dir + '"';
+ }
+ }
+ break;
+ }
+ }
+ soy.esc.$$HTML_ATTRIBUTE_REGEX_.lastIndex = 0;
+ }
+ tags[index] = start + tagName + '>';
+ attrs[index] = attributes;
+ return '[' + index + ']';
+ }
+ }
+ return '';
+ });
+
+ // Escape HTML special characters. Now there are no '<' in html that could
+ // start a tag.
+ html = soy.esc.$$normalizeHtmlHelper(html);
+
+ var finalCloseTags = soy.$$balanceTags_(tags);
+
+ // Now html contains no tags or less-than characters that could become
+ // part of a tag via a replacement operation and tags only contains
+ // approved tags.
+ // Reinsert the white-listed tags.
+ html = html.replace(/\[(\d+)\]/g, function(_, index) {
+ if (attrs[index] && tags[index]) {
+ return tags[index].substr(0, tags[index].length - 1) + attrs[index] + '>';
+ }
+ return tags[index];
+ });
+
+ // Close any still open tags.
+ // This prevents unclosed formatting elements like <ol> and <table> from
+ // breaking the layout of containing HTML.
+ return html + finalCloseTags;
+};
+
+
+/**
+ * Make sure that tag boundaries are not broken by Safe CSS when embedded in a
+ * {@code <style>} element.
+ * @param {string} css
+ * @return {string}
+ * @private
+ */
+soy.$$embedCssIntoHtml_ = function(css) {
+ // Port of a method of the same name in
+ // com.google.template.soy.shared.restricted.Sanitizers
+ return css.replace(/<\//g, '<\\/').replace(/\]\]>/g, ']]\\>');
+};
+
+
+/**
+ * Throw out any close tags that don't correspond to start tags.
+ * If {@code <table>} is used for formatting, embedded HTML shouldn't be able
+ * to use a mismatched {@code </table>} to break page layout.
+ *
+ * @param {Array<string>} tags Array of open/close tags (e.g. '<p>', '</p>')
+ * that will be modified in place to be either an open tag, one or more close
+ * tags concatenated, or the empty string.
+ * @return {string} zero or more closed tags that close all elements that are
+ * opened in tags but not closed.
+ * @private
+ */
+soy.$$balanceTags_ = function(tags) {
+ var open = [];
+ for (var i = 0, n = tags.length; i < n; ++i) {
+ var tag = tags[i];
+ if (tag.charAt(1) == '/') {
+ var openTagIndex = goog.array.lastIndexOf(open, tag);
+ if (openTagIndex < 0) {
+ tags[i] = ''; // Drop close tag with no corresponding open tag.
+ } else {
+ tags[i] = open.slice(openTagIndex).reverse().join('');
+ open.length = openTagIndex;
+ }
+ } else if (tag == '<li>' &&
+ goog.array.lastIndexOf(open, '</ol>') < 0 &&
+ goog.array.lastIndexOf(open, '</ul>') < 0) {
+ // Drop <li> if it isn't nested in a parent <ol> or <ul>.
+ tags[i] = '';
+ } else if (!soy.$$HTML5_VOID_ELEMENTS_.test(tag)) {
+ open.push('</' + tag.substring(1));
+ }
+ }
+ return open.reverse().join('');
+};
+
+
+/**
+ * Escapes HTML special characters in an HTML attribute value.
+ *
+ * @param {?} value The HTML to be escaped. May not be a string, but the
+ * value will be coerced to a string.
+ * @return {string} An escaped version of value.
+ */
+soy.$$escapeHtmlAttribute = function(value) {
+ // NOTE: We don't accept ATTRIBUTES here because ATTRIBUTES is actually not
+ // the attribute value context, but instead k/v pairs.
+ if (soydata.isContentKind_(value, goog.soy.data.SanitizedContentKind.HTML)) {
+ // NOTE: After removing tags, we also escape quotes ("normalize") so that
+ // the HTML can be embedded in attribute context.
+ goog.asserts.assert(value.constructor === goog.soy.data.SanitizedHtml);
+ return soy.esc.$$normalizeHtmlHelper(
+ soy.$$stripHtmlTags(value.getContent()));
+ }
+ return soy.esc.$$escapeHtmlHelper(value);
+};
+
+
+/**
+ * Escapes HTML special characters in a string including space and other
+ * characters that can end an unquoted HTML attribute value.
+ *
+ * @param {?} value The HTML to be escaped. May not be a string, but the
+ * value will be coerced to a string.
+ * @return {string} An escaped version of value.
+ */
+soy.$$escapeHtmlAttributeNospace = function(value) {
+ if (soydata.isContentKind_(value, goog.soy.data.SanitizedContentKind.HTML)) {
+ goog.asserts.assert(value.constructor === goog.soy.data.SanitizedHtml);
+ return soy.esc.$$normalizeHtmlNospaceHelper(
+ soy.$$stripHtmlTags(value.getContent()));
+ }
+ return soy.esc.$$escapeHtmlNospaceHelper(value);
+};
+
+
+/**
+ * Filters out strings that cannot be a substring of a valid HTML attribute.
+ *
+ * Note the input is expected to be key=value pairs.
+ *
+ * @param {?} value The value to escape. May not be a string, but the value
+ * will be coerced to a string.
+ * @return {string} A valid HTML attribute name part or name/value pair.
+ * {@code "zSoyz"} if the input is invalid.
+ */
+soy.$$filterHtmlAttributes = function(value) {
+ // NOTE: Explicitly no support for SanitizedContentKind.HTML, since that is
+ // meaningless in this context, which is generally *between* html attributes.
+ if (soydata.isContentKind_(
+ value, goog.soy.data.SanitizedContentKind.ATTRIBUTES)) {
+ goog.asserts.assert(
+ value.constructor === goog.soy.data.SanitizedHtmlAttribute);
+ // Add a space at the end to ensure this won't get merged into following
+ // attributes, unless the interpretation is unambiguous (ending with quotes
+ // or a space).
+ return value.getContent().replace(/([^"'\s])$/, '$1 ');
+ }
+ // TODO: Dynamically inserting attributes that aren't marked as trusted is
+ // probably unnecessary. Any filtering done here will either be inadequate
+ // for security or not flexible enough. Having clients use kind="attributes"
+ // in parameters seems like a wiser idea.
+ return soy.esc.$$filterHtmlAttributesHelper(value);
+};
+
+
+/**
+ * Filters out strings that cannot be a substring of a valid HTML element name.
+ *
+ * @param {*} value The value to escape. May not be a string, but the value
+ * will be coerced to a string.
+ * @return {string} A valid HTML element name part.
+ * {@code "zSoyz"} if the input is invalid.
+ */
+soy.$$filterHtmlElementName = function(value) {
+ // NOTE: We don't accept any SanitizedContent here. HTML indicates valid
+ // PCDATA, not tag names. A sloppy developer shouldn't be able to cause an
+ // exploit:
+ // ... {let userInput}script src=http://evil.com/evil.js{/let} ...
+ // ... {param tagName kind="html"}{$userInput}{/param} ...
+ // ... <{$tagName}>Hello World</{$tagName}>
+ return soy.esc.$$filterHtmlElementNameHelper(value);
+};
+
+
+/**
+ * Escapes characters in the value to make it valid content for a JS string
+ * literal.
+ *
+ * @param {*} value The value to escape. May not be a string, but the value
+ * will be coerced to a string.
+ * @return {string} An escaped version of value.
+ */
+soy.$$escapeJsString = function(value) {
+ return soy.esc.$$escapeJsStringHelper(value);
+};
+
+
+/**
+ * Encodes a value as a JavaScript literal.
+ *
+ * @param {*} value The value to escape. May not be a string, but the value
+ * will be coerced to a string.
+ * @return {string} A JavaScript code representation of the input.
+ */
+soy.$$escapeJsValue = function(value) {
+ // We surround values with spaces so that they can't be interpolated into
+ // identifiers by accident.
+ // We could use parentheses but those might be interpreted as a function call.
+ if (value == null) { // Intentionally matches undefined.
+ // Java returns null from maps where there is no corresponding key while
+ // JS returns undefined.
+ // We always output null for compatibility with Java which does not have a
+ // distinct undefined value.
+ return ' null ';
+ }
+ if (soydata.isContentKind_(value, goog.soy.data.SanitizedContentKind.JS)) {
+ goog.asserts.assert(value.constructor === goog.soy.data.SanitizedJs);
+ return value.getContent();
+ }
+ if (value instanceof goog.html.SafeScript) {
+ return goog.html.SafeScript.unwrap(value);
+ }
+ switch (typeof value) {
+ case 'boolean': case 'number':
+ return ' ' + value + ' ';
+ default:
+ return "'" + soy.esc.$$escapeJsStringHelper(String(value)) + "'";
+ }
+};
+
+
+/**
+ * Escapes characters in the string to make it valid content for a JS regular
+ * expression literal.
+ *
+ * @param {*} value The value to escape. May not be a string, but the value
+ * will be coerced to a string.
+ * @return {string} An escaped version of value.
+ */
+soy.$$escapeJsRegex = function(value) {
+ return soy.esc.$$escapeJsRegexHelper(value);
+};
+
+
+/**
+ * Matches all URI mark characters that conflict with HTML attribute delimiters
+ * or that cannot appear in a CSS uri.
+ * From <a href="http://www.w3.org/TR/CSS2/grammar.html">G.2: CSS grammar</a>
+ * <pre>
+ * url ([!#$%&*-~]|{nonascii}|{escape})*
+ * </pre>
+ *
+ * @type {RegExp}
+ * @private
+ */
+soy.$$problematicUriMarks_ = /['()]/g;
+
+/**
+ * @param {string} ch A single character in {@link soy.$$problematicUriMarks_}.
+ * @return {string}
+ * @private
+ */
+soy.$$pctEncode_ = function(ch) {
+ return '%' + ch.charCodeAt(0).toString(16);
+};
+
+/**
+ * Escapes a string so that it can be safely included in a URI.
+ *
+ * @param {*} value The value to escape. May not be a string, but the value
+ * will be coerced to a string.
+ * @return {string} An escaped version of value.
+ */
+soy.$$escapeUri = function(value) {
+ // NOTE: We don't check for SanitizedUri or SafeUri, because just because
+ // something is already a valid complete URL doesn't mean we don't want to
+ // encode it as a component. For example, it would be bad if
+ // ?redirect={$url} didn't escape ampersands, because in that template, the
+ // continue URL should be treated as a single unit.
+
+ // Apostophes and parentheses are not matched by encodeURIComponent.
+ // They are technically special in URIs, but only appear in the obsolete mark
+ // production in Appendix D.2 of RFC 3986, so can be encoded without changing
+ // semantics.
+ var encoded = soy.esc.$$escapeUriHelper(value);
+ soy.$$problematicUriMarks_.lastIndex = 0;
+ if (soy.$$problematicUriMarks_.test(encoded)) {
+ return encoded.replace(soy.$$problematicUriMarks_, soy.$$pctEncode_);
+ }
+ return encoded;
+};
+
+
+/**
+ * Removes rough edges from a URI by escaping any raw HTML/JS string delimiters.
+ *
+ * @param {*} value The value to escape. May not be a string, but the value
+ * will be coerced to a string.
+ * @return {string} An escaped version of value.
+ */
+soy.$$normalizeUri = function(value) {
+ return soy.esc.$$normalizeUriHelper(value);
+};
+
+
+/**
+ * Vets a URI's protocol and removes rough edges from a URI by escaping
+ * any raw HTML/JS string delimiters.
+ *
+ * @param {?} value The value to escape. May not be a string, but the value
+ * will be coerced to a string.
+ * @return {string} An escaped version of value.
+ */
+soy.$$filterNormalizeUri = function(value) {
+ if (soydata.isContentKind_(value, goog.soy.data.SanitizedContentKind.URI)) {
+ goog.asserts.assert(value.constructor === goog.soy.data.SanitizedUri);
+ return soy.$$normalizeUri(value);
+ }
+ if (soydata.isContentKind_(value,
+ goog.soy.data.SanitizedContentKind.TRUSTED_RESOURCE_URI)) {
+ goog.asserts.assert(
+ value.constructor === goog.soy.data.SanitizedTrustedResourceUri);
+ return soy.$$normalizeUri(value);
+ }
+ if (value instanceof goog.html.SafeUrl) {
+ return soy.$$normalizeUri(goog.html.SafeUrl.unwrap(value));
+ }
+ if (value instanceof goog.html.TrustedResourceUrl) {
+ return soy.$$normalizeUri(goog.html.TrustedResourceUrl.unwrap(value));
+ }
+ return soy.esc.$$filterNormalizeUriHelper(value);
+};
+
+
+/**
+ * Vets a URI for usage as an image source.
+ *
+ * @param {?} value The value to filter. Might not be a string, but the value
+ * will be coerced to a string.
+ * @return {string} An escaped version of value.
+ */
+soy.$$filterNormalizeMediaUri = function(value) {
+ // Image URIs are filtered strictly more loosely than other types of URIs.
+ // TODO(shwetakarwa): Add tests for this in soyutils_test_helper while adding
+ // tests for filterTrustedResourceUri.
+ if (soydata.isContentKind_(value, goog.soy.data.SanitizedContentKind.URI)) {
+ goog.asserts.assert(value.constructor === goog.soy.data.SanitizedUri);
+ return soy.$$normalizeUri(value);
+ }
+ if (soydata.isContentKind_(value,
+ goog.soy.data.SanitizedContentKind.TRUSTED_RESOURCE_URI)) {
+ goog.asserts.assert(
+ value.constructor === goog.soy.data.SanitizedTrustedResourceUri);
+ return soy.$$normalizeUri(value);
+ }
+ if (value instanceof goog.html.SafeUrl) {
+ return soy.$$normalizeUri(goog.html.SafeUrl.unwrap(value));
+ }
+ if (value instanceof goog.html.TrustedResourceUrl) {
+ return soy.$$normalizeUri(goog.html.TrustedResourceUrl.unwrap(value));
+ }
+ return soy.esc.$$filterNormalizeMediaUriHelper(value);
+};
+
+
+/**
+ * Vets a URI for usage as a resource. Makes sure the input value is a compile
+ * time constant or a TrustedResouce not in attacker's control.
+ *
+ * @param {?} value The value to filter.
+ * @return {string} The value content.
+ */
+soy.$$filterTrustedResourceUri = function(value) {
+ if (soydata.isContentKind_(value,
+ goog.soy.data.SanitizedContentKind.TRUSTED_RESOURCE_URI)) {
+ goog.asserts.assert(
+ value.constructor === goog.soy.data.SanitizedTrustedResourceUri);
+ return value.getContent();
+ }
+ if (value instanceof goog.html.TrustedResourceUrl) {
+ return goog.html.TrustedResourceUrl.unwrap(value);
+ }
+ goog.asserts.fail('Bad value `%s` for |filterTrustedResourceUri',
+ [String(value)]);
+ return 'about:invalid#zSoyz';
+};
+
+
+/**
+ * For any resource string/variable which has
+ * |blessStringAsTrustedResuorceUrlForLegacy directive return the value as is.
+ *
+ * @param {*} value The value to be blessed. Might not be a string
+ * @return {*} value Return current value.
+ */
+soy.$$blessStringAsTrustedResourceUrlForLegacy = function(value) {
+ return value;
+};
+
+
+/**
+ * Allows only data-protocol image URI's.
+ *
+ * @param {*} value The value to process. May not be a string, but the value
+ * will be coerced to a string.
+ * @return {!goog.soy.data.SanitizedUri} An escaped version of value.
+ */
+soy.$$filterImageDataUri = function(value) {
+ // NOTE: Even if it's a SanitizedUri, we will still filter it.
+ return soydata.VERY_UNSAFE.ordainSanitizedUri(
+ soy.esc.$$filterImageDataUriHelper(value));
+};
+
+
+/**
+ * Allows only tel URIs.
+ *
+ * @param {*} value The value to process. May not be a string, but the value
+ * will be coerced to a string.
+ * @return {!goog.soy.data.SanitizedUri} An escaped version of value.
+ */
+soy.$$filterTelUri = function(value) {
+ // NOTE: Even if it's a SanitizedUri, we will still filter it.
+ return soydata.VERY_UNSAFE.ordainSanitizedUri(
+ soy.esc.$$filterTelUriHelper(value));
+};
+
+
+/**
+ * Escapes a string so it can safely be included inside a quoted CSS string.
+ *
+ * @param {*} value The value to escape. May not be a string, but the value
+ * will be coerced to a string.
+ * @return {string} An escaped version of value.
+ */
+soy.$$escapeCssString = function(value) {
+ return soy.esc.$$escapeCssStringHelper(value);
+};
+
+
+/**
+ * Encodes a value as a CSS identifier part, keyword, or quantity.
+ *
+ * @param {?} value The value to escape. May not be a string, but the value
+ * will be coerced to a string.
+ * @return {string} A safe CSS identifier part, keyword, or quanitity.
+ */
+soy.$$filterCssValue = function(value) {
+ if (soydata.isContentKind_(value, goog.soy.data.SanitizedContentKind.CSS)) {
+ goog.asserts.assertInstanceof(value, goog.soy.data.SanitizedCss);
+ return soy.$$embedCssIntoHtml_(value.getContent());
+ }
+ // Uses == to intentionally match null and undefined for Java compatibility.
+ if (value == null) {
+ return '';
+ }
+ if (value instanceof goog.html.SafeStyle) {
+ return soy.$$embedCssIntoHtml_(goog.html.SafeStyle.unwrap(value));
+ }
+ // Note: SoyToJsSrcCompiler uses soy.$$filterCssValue both for the contents of
+ // <style> (list of rules) and for the contents of style="" (one set of
+ // declarations). We support SafeStyleSheet here to be used inside <style> but
+ // it also wrongly allows it inside style="". We should instead change
+ // SoyToJsSrcCompiler to use a different function inside <style>.
+ if (value instanceof goog.html.SafeStyleSheet) {
+ return soy.$$embedCssIntoHtml_(goog.html.SafeStyleSheet.unwrap(value));
+ }
+ return soy.esc.$$filterCssValueHelper(value);
+};
+
+
+/**
+ * Sanity-checks noAutoescape input for explicitly tainted content.
+ *
+ * SanitizedContentKind.TEXT is used to explicitly mark input that was never
+ * meant to be used unescaped.
+ *
+ * @param {?} value The value to filter.
+ * @return {*} The value, that we dearly hope will not cause an attack.
+ */
+soy.$$filterNoAutoescape = function(value) {
+ if (soydata.isContentKind_(value, goog.soy.data.SanitizedContentKind.TEXT)) {
+ // Fail in development mode.
+ goog.asserts.fail(
+ 'Tainted SanitizedContentKind.TEXT for |noAutoescape: `%s`',
+ [value.getContent()]);
+ // Return innocuous data in production.
+ return 'zSoyz';
+ }
+
+ return value;
+};
+
+
+// -----------------------------------------------------------------------------
+// Basic directives/functions.
+
+
+/**
+ * Converts \r\n, \r, and \n to <br>s
+ * @param {*} value The string in which to convert newlines.
+ * @return {string|!goog.soy.data.SanitizedHtml} A copy of {@code value} with
+ * converted newlines. If {@code value} is SanitizedHtml, the return value
+ * is also SanitizedHtml, of the same known directionality.
+ */
+soy.$$changeNewlineToBr = function(value) {
+ var result = goog.string.newLineToBr(String(value), false);
+ if (soydata.isContentKind_(value, goog.soy.data.SanitizedContentKind.HTML)) {
+ return soydata.VERY_UNSAFE.ordainSanitizedHtml(
+ result, soydata.getContentDir(value));
+ }
+ return result;
+};
+
+
+/**
+ * Inserts word breaks ('wbr' tags) into a HTML string at a given interval. The
+ * counter is reset if a space is encountered. Word breaks aren't inserted into
+ * HTML tags or entities. Entites count towards the character count; HTML tags
+ * do not.
+ *
+ * @param {*} value The HTML string to insert word breaks into. Can be other
+ * types, but the value will be coerced to a string.
+ * @param {number} maxCharsBetweenWordBreaks Maximum number of non-space
+ * characters to allow before adding a word break.
+ * @return {string|!goog.soy.data.SanitizedHtml} The string including word
+ * breaks. If {@code value} is SanitizedHtml, the return value
+ * is also SanitizedHtml, of the same known directionality.
+ * @deprecated The |insertWordBreaks directive is deprecated.
+ * Prefer wrapping with CSS white-space: break-word.
+ */
+soy.$$insertWordBreaks = function(value, maxCharsBetweenWordBreaks) {
+ var result = goog.format.insertWordBreaks(
+ String(value), maxCharsBetweenWordBreaks);
+ if (soydata.isContentKind_(value, goog.soy.data.SanitizedContentKind.HTML)) {
+ return soydata.VERY_UNSAFE.ordainSanitizedHtml(
+ result, soydata.getContentDir(value));
+ }
+ return result;
+};
+
+
+/**
+ * Truncates a string to a given max length (if it's currently longer),
+ * optionally adding ellipsis at the end.
+ *
+ * @param {*} str The string to truncate. Can be other types, but the value will
+ * be coerced to a string.
+ * @param {number} maxLen The maximum length of the string after truncation
+ * (including ellipsis, if applicable).
+ * @param {boolean} doAddEllipsis Whether to add ellipsis if the string needs
+ * truncation.
+ * @return {string} The string after truncation.
+ */
+soy.$$truncate = function(str, maxLen, doAddEllipsis) {
+
+ str = String(str);
+ if (str.length <= maxLen) {
+ return str; // no need to truncate
+ }
+
+ // If doAddEllipsis, either reduce maxLen to compensate, or else if maxLen is
+ // too small, just turn off doAddEllipsis.
+ if (doAddEllipsis) {
+ if (maxLen > 3) {
+ maxLen -= 3;
+ } else {
+ doAddEllipsis = false;
+ }
+ }
+
+ // Make sure truncating at maxLen doesn't cut up a unicode surrogate pair.
+ if (soy.$$isHighSurrogate_(str.charCodeAt(maxLen - 1)) &&
+ soy.$$isLowSurrogate_(str.charCodeAt(maxLen))) {
+ maxLen -= 1;
+ }
+
+ // Truncate.
+ str = str.substring(0, maxLen);
+
+ // Add ellipsis.
+ if (doAddEllipsis) {
+ str += '...';
+ }
+
+ return str;
+};
+
+/**
+ * Private helper for $$truncate() to check whether a char is a high surrogate.
+ * @param {number} cc The codepoint to check.
+ * @return {boolean} Whether the given codepoint is a unicode high surrogate.
+ * @private
+ */
+soy.$$isHighSurrogate_ = function(cc) {
+ return 0xD800 <= cc && cc <= 0xDBFF;
+};
+
+/**
+ * Private helper for $$truncate() to check whether a char is a low surrogate.
+ * @param {number} cc The codepoint to check.
+ * @return {boolean} Whether the given codepoint is a unicode low surrogate.
+ * @private
+ */
+soy.$$isLowSurrogate_ = function(cc) {
+ return 0xDC00 <= cc && cc <= 0xDFFF;
+};
+
+
+// -----------------------------------------------------------------------------
+// Bidi directives/functions.
+
+
+/**
+ * Cache of bidi formatter by context directionality, so we don't keep on
+ * creating new objects.
+ * @type {!Object<!goog.i18n.BidiFormatter>}
+ * @private
+ */
+soy.$$bidiFormatterCache_ = {};
+
+
+/**
+ * Returns cached bidi formatter for bidiGlobalDir, or creates a new one.
+ * @param {number} bidiGlobalDir The global directionality context: 1 if ltr, -1
+ * if rtl, 0 if unknown.
+ * @return {!goog.i18n.BidiFormatter} A formatter for bidiGlobalDir.
+ * @private
+ */
+soy.$$getBidiFormatterInstance_ = function(bidiGlobalDir) {
+ return soy.$$bidiFormatterCache_[bidiGlobalDir] ||
+ (soy.$$bidiFormatterCache_[bidiGlobalDir] =
+ new goog.i18n.BidiFormatter(bidiGlobalDir));
+};
+
+
+/**
+ * Estimate the overall directionality of text. If opt_isHtml, makes sure to
+ * ignore the LTR nature of the mark-up and escapes in text, making the logic
+ * suitable for HTML and HTML-escaped text.
+ * If text has a goog.i18n.bidi.Dir-valued contentDir, this is used instead of
+ * estimating the directionality.
+ *
+ * @param {*} text The content whose directionality is to be estimated.
+ * @param {boolean=} opt_isHtml Whether text is HTML/HTML-escaped.
+ * Default: false.
+ * @return {number} 1 if text is LTR, -1 if it is RTL, and 0 if it is neutral.
+ */
+soy.$$bidiTextDir = function(text, opt_isHtml) {
+ var contentDir = soydata.getContentDir(text);
+ if (contentDir != null) {
+ return contentDir;
+ }
+ var isHtml = opt_isHtml ||
+ soydata.isContentKind_(text, goog.soy.data.SanitizedContentKind.HTML);
+ return goog.i18n.bidi.estimateDirection(text + '', isHtml);
+};
+
+
+/**
+ * Returns 'dir="ltr"' or 'dir="rtl"', depending on text's estimated
+ * directionality, if it is not the same as bidiGlobalDir.
+ * Otherwise, returns the empty string.
+ * If opt_isHtml, makes sure to ignore the LTR nature of the mark-up and escapes
+ * in text, making the logic suitable for HTML and HTML-escaped text.
+ * If text has a goog.i18n.bidi.Dir-valued contentDir, this is used instead of
+ * estimating the directionality.
+ *
+ * @param {number} bidiGlobalDir The global directionality context: 1 if ltr, -1
+ * if rtl, 0 if unknown.
+ * @param {*} text The content whose directionality is to be estimated.
+ * @param {boolean=} opt_isHtml Whether text is HTML/HTML-escaped.
+ * Default: false.
+ * @return {!goog.soy.data.SanitizedHtmlAttribute} 'dir="rtl"' for RTL text in
+ * non-RTL context; 'dir="ltr"' for LTR text in non-LTR context;
+ * else, the empty string.
+ */
+soy.$$bidiDirAttr = function(bidiGlobalDir, text, opt_isHtml) {
+ var formatter = soy.$$getBidiFormatterInstance_(bidiGlobalDir);
+ var contentDir = soydata.getContentDir(text);
+ if (contentDir == null) {
+ var isHtml = opt_isHtml ||
+ soydata.isContentKind_(text, goog.soy.data.SanitizedContentKind.HTML);
+ contentDir = goog.i18n.bidi.estimateDirection(text + '', isHtml);
+ }
+ return soydata.VERY_UNSAFE.ordainSanitizedHtmlAttribute(
+ formatter.knownDirAttr(contentDir));
+};
+
+
+/**
+ * Returns a Unicode BiDi mark matching bidiGlobalDir (LRM or RLM) if the
+ * directionality or the exit directionality of text are opposite to
+ * bidiGlobalDir. Otherwise returns the empty string.
+ * If opt_isHtml, makes sure to ignore the LTR nature of the mark-up and escapes
+ * in text, making the logic suitable for HTML and HTML-escaped text.
+ * If text has a goog.i18n.bidi.Dir-valued contentDir, this is used instead of
+ * estimating the directionality.
+ *
+ * @param {number} bidiGlobalDir The global directionality context: 1 if ltr, -1
+ * if rtl, 0 if unknown.
+ * @param {*} text The content whose directionality is to be estimated.
+ * @param {boolean=} opt_isHtml Whether text is HTML/HTML-escaped.
+ * Default: false.
+ * @return {string} A Unicode bidi mark matching bidiGlobalDir, or the empty
+ * string when text's overall and exit directionalities both match
+ * bidiGlobalDir, or bidiGlobalDir is 0 (unknown).
+ */
+soy.$$bidiMarkAfter = function(bidiGlobalDir, text, opt_isHtml) {
+ var formatter = soy.$$getBidiFormatterInstance_(bidiGlobalDir);
+ var isHtml = opt_isHtml ||
+ soydata.isContentKind_(text, goog.soy.data.SanitizedContentKind.HTML);
+ return formatter.markAfterKnownDir(soydata.getContentDir(text), text + '',
+ isHtml);
+};
+
+
+/**
+ * Returns text wrapped in a <span dir="ltr|rtl"> according to its
+ * directionality - but only if that is neither neutral nor the same as the
+ * global context. Otherwise, returns text unchanged.
+ * Always treats text as HTML/HTML-escaped, i.e. ignores mark-up and escapes
+ * when estimating text's directionality.
+ * If text has a goog.i18n.bidi.Dir-valued contentDir, this is used instead of
+ * estimating the directionality.
+ *
+ * @param {number} bidiGlobalDir The global directionality context: 1 if ltr, -1
+ * if rtl, 0 if unknown.
+ * @param {*} text The string to be wrapped. Can be other types, but the value
+ * will be coerced to a string.
+ * @return {!goog.soy.data.SanitizedContent|string} The wrapped text.
+ */
+soy.$$bidiSpanWrap = function(bidiGlobalDir, text) {
+ var formatter = soy.$$getBidiFormatterInstance_(bidiGlobalDir);
+
+ // We always treat the value as HTML, because span-wrapping is only useful
+ // when its output will be treated as HTML (without escaping), and because
+ // |bidiSpanWrap is not itself specified to do HTML escaping in Soy. (Both
+ // explicit and automatic HTML escaping, if any, is done before calling
+ // |bidiSpanWrap because the BidiSpanWrapDirective Java class implements
+ // SanitizedContentOperator, but this does not mean that the input has to be
+ // HTML SanitizedContent. In legacy usage, a string that is not
+ // SanitizedContent is often printed in an autoescape="false" template or by
+ // a print with a |noAutoescape, in which case our input is just SoyData.) If
+ // the output will be treated as HTML, the input had better be safe
+ // HTML/HTML-escaped (even if it isn't HTML SanitizedData), or we have an XSS
+ // opportunity and a much bigger problem than bidi garbling.
+ var html = goog.html.uncheckedconversions.
+ safeHtmlFromStringKnownToSatisfyTypeContract(
+ goog.string.Const.from(
+ 'Soy |bidiSpanWrap is applied on an autoescaped text.'),
+ String(text));
+ var wrappedHtml = formatter.spanWrapSafeHtmlWithKnownDir(
+ soydata.getContentDir(text), html);
+
+ // Like other directives whose Java class implements SanitizedContentOperator,
+ // |bidiSpanWrap is called after the escaping (if any) has already been done,
+ // and thus there is no need for it to produce actual SanitizedContent.
+ return goog.html.SafeHtml.unwrap(wrappedHtml);
+};
+
+
+/**
+ * Returns text wrapped in Unicode BiDi formatting characters according to its
+ * directionality, i.e. either LRE or RLE at the beginning and PDF at the end -
+ * but only if text's directionality is neither neutral nor the same as the
+ * global context. Otherwise, returns text unchanged.
+ * Only treats SanitizedHtml as HTML/HTML-escaped, i.e. ignores mark-up
+ * and escapes when estimating text's directionality.
+ * If text has a goog.i18n.bidi.Dir-valued contentDir, this is used instead of
+ * estimating the directionality.
+ *
+ * @param {number} bidiGlobalDir The global directionality context: 1 if ltr, -1
+ * if rtl, 0 if unknown.
+ * @param {*} text The string to be wrapped. Can be other types, but the value
+ * will be coerced to a string.
+ * @return {!goog.soy.data.SanitizedContent|string} The wrapped string.
+ */
+soy.$$bidiUnicodeWrap = function(bidiGlobalDir, text) {
+ var formatter = soy.$$getBidiFormatterInstance_(bidiGlobalDir);
+
+ // We treat the value as HTML if and only if it says it's HTML, even though in
+ // legacy usage, we sometimes have an HTML string (not SanitizedContent) that
+ // is passed to an autoescape="false" template or a {print $foo|noAutoescape},
+ // with the output going into an HTML context without escaping. We simply have
+ // no way of knowing if this is what is happening when we get
+ // non-SanitizedContent input, and most of the time it isn't.
+ var isHtml =
+ soydata.isContentKind_(text, goog.soy.data.SanitizedContentKind.HTML);
+ var wrappedText = formatter.unicodeWrapWithKnownDir(
+ soydata.getContentDir(text), text + '', isHtml);
+
+ // Bidi-wrapping a value converts it to the context directionality. Since it
+ // does not cost us anything, we will indicate this known direction in the
+ // output SanitizedContent, even though the intended consumer of that
+ // information - a bidi wrapping directive - has already been run.
+ var wrappedTextDir = formatter.getContextDir();
+
+ // Unicode-wrapping UnsanitizedText gives UnsanitizedText.
+ // Unicode-wrapping safe HTML or JS string data gives valid, safe HTML or JS
+ // string data.
+ // ATTENTION: Do these need to be ...ForInternalBlocks()?
+ if (soydata.isContentKind_(text, goog.soy.data.SanitizedContentKind.TEXT)) {
+ return new goog.soy.data.UnsanitizedText(wrappedText, wrappedTextDir);
+ }
+ if (isHtml) {
+ return soydata.VERY_UNSAFE.ordainSanitizedHtml(wrappedText, wrappedTextDir);
+ }
+
+ // Unicode-wrapping does not conform to the syntax of the other types of
+ // content. For lack of anything better to do, we we do not declare a content
+ // kind at all by falling through to the non-SanitizedContent case below.
+ // TODO(aharon): Consider throwing a runtime error on receipt of
+ // SanitizedContent other than TEXT, HTML, or JS_STR_CHARS.
+
+ // The input was not SanitizedContent, so our output isn't SanitizedContent
+ // either.
+ return wrappedText;
+};
+
+// -----------------------------------------------------------------------------
+// Assertion methods used by runtime.
+
+/**
+ * Checks if the type assertion is true if goog.asserts.ENABLE_ASSERTS is
+ * true. Report errors on runtime types if goog.DEBUG is true.
+ * @param {boolean} condition The type check condition.
+ * @param {string} paramName The Soy name of the parameter.
+ * @param {?} param The JS object for the parameter.
+ * @param {!string} jsDocTypeStr SoyDoc type str.
+ * @return {?} the param value
+ * @throws {goog.asserts.AssertionError} When the condition evaluates to false.
+ */
+soy.asserts.assertType = function(condition, paramName, param, jsDocTypeStr) {
+ if (goog.asserts.ENABLE_ASSERTS && !condition) {
+ var msg = 'expected param ' + paramName + ' of type ' + jsDocTypeStr +
+ (goog.DEBUG ? (', but got ' + goog.debug.runtimeType(param)) : '') +
+ '.';
+ goog.asserts.fail(msg);
+ }
+ return param;
+};
+
+// -----------------------------------------------------------------------------
+// Used for inspecting Soy template information from rendered pages.
+
+/**
+ * Whether we should generate additional HTML comments.
+ * @type {boolean}
+ */
+soy.$$debugSoyTemplateInfo = false;
+
+if (goog.DEBUG) {
+ /**
+ * Configures whether we should generate additional HTML comments for
+ * inspecting Soy template information from rendered pages.
+ * @param {boolean} debugSoyTemplateInfo
+ */
+ soy.setDebugSoyTemplateInfo = function(debugSoyTemplateInfo) {
+ soy.$$debugSoyTemplateInfo = debugSoyTemplateInfo;
+ };
+}
+
+// -----------------------------------------------------------------------------
+// Generated code.
+
+
+// START GENERATED CODE FOR ESCAPERS.
+
+/**
+ * @type {function (*) : string}
+ */
+soy.esc.$$escapeHtmlHelper = function(v) {
+ return goog.string.htmlEscape(String(v));
+};
+
+/**
+ * @type {function (*) : string}
+ */
+soy.esc.$$escapeUriHelper = function(v) {
+ return goog.string.urlEncode(String(v));
+};
+
+/**
+ * Maps characters to the escaped versions for the named escape directives.
+ * @private {!Object<string, string>}
+ */
+soy.esc.$$ESCAPE_MAP_FOR_NORMALIZE_HTML__AND__ESCAPE_HTML_NOSPACE__AND__NORMALIZE_HTML_NOSPACE_ = {
+ '\x00': '\x26#0;',
+ '\x09': '\x26#9;',
+ '\x0a': '\x26#10;',
+ '\x0b': '\x26#11;',
+ '\x0c': '\x26#12;',
+ '\x0d': '\x26#13;',
+ ' ': '\x26#32;',
+ '\x22': '\x26quot;',
+ '\x26': '\x26amp;',
+ '\x27': '\x26#39;',
+ '-': '\x26#45;',
+ '\/': '\x26#47;',
+ '\x3c': '\x26lt;',
+ '\x3d': '\x26#61;',
+ '\x3e': '\x26gt;',
+ '`': '\x26#96;',
+ '\x85': '\x26#133;',
+ '\xa0': '\x26#160;',
+ '\u2028': '\x26#8232;',
+ '\u2029': '\x26#8233;'
+};
+
+/**
+ * A function that can be used with String.replace.
+ * @param {string} ch A single character matched by a compatible matcher.
+ * @return {string} A token in the output language.
+ * @private
+ */
+soy.esc.$$REPLACER_FOR_NORMALIZE_HTML__AND__ESCAPE_HTML_NOSPACE__AND__NORMALIZE_HTML_NOSPACE_ = function(ch) {
+ return soy.esc.$$ESCAPE_MAP_FOR_NORMALIZE_HTML__AND__ESCAPE_HTML_NOSPACE__AND__NORMALIZE_HTML_NOSPACE_[ch];
+};
+
+/**
+ * Maps characters to the escaped versions for the named escape directives.
+ * @private {!Object<string, string>}
+ */
+soy.esc.$$ESCAPE_MAP_FOR_ESCAPE_JS_STRING__AND__ESCAPE_JS_REGEX_ = {
+ '\x00': '\\x00',
+ '\x08': '\\x08',
+ '\x09': '\\t',
+ '\x0a': '\\n',
+ '\x0b': '\\x0b',
+ '\x0c': '\\f',
+ '\x0d': '\\r',
+ '\x22': '\\x22',
+ '$': '\\x24',
+ '\x26': '\\x26',
+ '\x27': '\\x27',
+ '(': '\\x28',
+ ')': '\\x29',
+ '*': '\\x2a',
+ '+': '\\x2b',
+ ',': '\\x2c',
+ '-': '\\x2d',
+ '.': '\\x2e',
+ '\/': '\\\/',
+ ':': '\\x3a',
+ '\x3c': '\\x3c',
+ '\x3d': '\\x3d',
+ '\x3e': '\\x3e',
+ '?': '\\x3f',
+ '\x5b': '\\x5b',
+ '\\': '\\\\',
+ '\x5d': '\\x5d',
+ '^': '\\x5e',
+ '\x7b': '\\x7b',
+ '|': '\\x7c',
+ '\x7d': '\\x7d',
+ '\x85': '\\x85',
+ '\u2028': '\\u2028',
+ '\u2029': '\\u2029'
+};
+
+/**
+ * A function that can be used with String.replace.
+ * @param {string} ch A single character matched by a compatible matcher.
+ * @return {string} A token in the output language.
+ * @private
+ */
+soy.esc.$$REPLACER_FOR_ESCAPE_JS_STRING__AND__ESCAPE_JS_REGEX_ = function(ch) {
+ return soy.esc.$$ESCAPE_MAP_FOR_ESCAPE_JS_STRING__AND__ESCAPE_JS_REGEX_[ch];
+};
+
+/**
+ * Maps characters to the escaped versions for the named escape directives.
+ * @private {!Object<string, string>}
+ */
+soy.esc.$$ESCAPE_MAP_FOR_ESCAPE_CSS_STRING_ = {
+ '\x00': '\\0 ',
+ '\x08': '\\8 ',
+ '\x09': '\\9 ',
+ '\x0a': '\\a ',
+ '\x0b': '\\b ',
+ '\x0c': '\\c ',
+ '\x0d': '\\d ',
+ '\x22': '\\22 ',
+ '\x26': '\\26 ',
+ '\x27': '\\27 ',
+ '(': '\\28 ',
+ ')': '\\29 ',
+ '*': '\\2a ',
+ '\/': '\\2f ',
+ ':': '\\3a ',
+ ';': '\\3b ',
+ '\x3c': '\\3c ',
+ '\x3d': '\\3d ',
+ '\x3e': '\\3e ',
+ '@': '\\40 ',
+ '\\': '\\5c ',
+ '\x7b': '\\7b ',
+ '\x7d': '\\7d ',
+ '\x85': '\\85 ',
+ '\xa0': '\\a0 ',
+ '\u2028': '\\2028 ',
+ '\u2029': '\\2029 '
+};
+
+/**
+ * A function that can be used with String.replace.
+ * @param {string} ch A single character matched by a compatible matcher.
+ * @return {string} A token in the output language.
+ * @private
+ */
+soy.esc.$$REPLACER_FOR_ESCAPE_CSS_STRING_ = function(ch) {
+ return soy.esc.$$ESCAPE_MAP_FOR_ESCAPE_CSS_STRING_[ch];
+};
+
+/**
+ * Maps characters to the escaped versions for the named escape directives.
+ * @private {!Object<string, string>}
+ */
+soy.esc.$$ESCAPE_MAP_FOR_NORMALIZE_URI__AND__FILTER_NORMALIZE_URI__AND__FILTER_NORMALIZE_MEDIA_URI_ = {
+ '\x00': '%00',
+ '\x01': '%01',
+ '\x02': '%02',
+ '\x03': '%03',
+ '\x04': '%04',
+ '\x05': '%05',
+ '\x06': '%06',
+ '\x07': '%07',
+ '\x08': '%08',
+ '\x09': '%09',
+ '\x0a': '%0A',
+ '\x0b': '%0B',
+ '\x0c': '%0C',
+ '\x0d': '%0D',
+ '\x0e': '%0E',
+ '\x0f': '%0F',
+ '\x10': '%10',
+ '\x11': '%11',
+ '\x12': '%12',
+ '\x13': '%13',
+ '\x14': '%14',
+ '\x15': '%15',
+ '\x16': '%16',
+ '\x17': '%17',
+ '\x18': '%18',
+ '\x19': '%19',
+ '\x1a': '%1A',
+ '\x1b': '%1B',
+ '\x1c': '%1C',
+ '\x1d': '%1D',
+ '\x1e': '%1E',
+ '\x1f': '%1F',
+ ' ': '%20',
+ '\x22': '%22',
+ '\x27': '%27',
+ '(': '%28',
+ ')': '%29',
+ '\x3c': '%3C',
+ '\x3e': '%3E',
+ '\\': '%5C',
+ '\x7b': '%7B',
+ '\x7d': '%7D',
+ '\x7f': '%7F',
+ '\x85': '%C2%85',
+ '\xa0': '%C2%A0',
+ '\u2028': '%E2%80%A8',
+ '\u2029': '%E2%80%A9',
+ '\uff01': '%EF%BC%81',
+ '\uff03': '%EF%BC%83',
+ '\uff04': '%EF%BC%84',
+ '\uff06': '%EF%BC%86',
+ '\uff07': '%EF%BC%87',
+ '\uff08': '%EF%BC%88',
+ '\uff09': '%EF%BC%89',
+ '\uff0a': '%EF%BC%8A',
+ '\uff0b': '%EF%BC%8B',
+ '\uff0c': '%EF%BC%8C',
+ '\uff0f': '%EF%BC%8F',
+ '\uff1a': '%EF%BC%9A',
+ '\uff1b': '%EF%BC%9B',
+ '\uff1d': '%EF%BC%9D',
+ '\uff1f': '%EF%BC%9F',
+ '\uff20': '%EF%BC%A0',
+ '\uff3b': '%EF%BC%BB',
+ '\uff3d': '%EF%BC%BD'
+};
+
+/**
+ * A function that can be used with String.replace.
+ * @param {string} ch A single character matched by a compatible matcher.
+ * @return {string} A token in the output language.
+ * @private
+ */
+soy.esc.$$REPLACER_FOR_NORMALIZE_URI__AND__FILTER_NORMALIZE_URI__AND__FILTER_NORMALIZE_MEDIA_URI_ = function(ch) {
+ return soy.esc.$$ESCAPE_MAP_FOR_NORMALIZE_URI__AND__FILTER_NORMALIZE_URI__AND__FILTER_NORMALIZE_MEDIA_URI_[ch];
+};
+
+/**
+ * Matches characters that need to be escaped for the named directives.
+ * @private {!RegExp}
+ */
+soy.esc.$$MATCHER_FOR_NORMALIZE_HTML_ = /[\x00\x22\x27\x3c\x3e]/g;
+
+/**
+ * Matches characters that need to be escaped for the named directives.
+ * @private {!RegExp}
+ */
+soy.esc.$$MATCHER_FOR_ESCAPE_HTML_NOSPACE_ = /[\x00\x09-\x0d \x22\x26\x27\x2d\/\x3c-\x3e`\x85\xa0\u2028\u2029]/g;
+
+/**
+ * Matches characters that need to be escaped for the named directives.
+ * @private {!RegExp}
+ */
+soy.esc.$$MATCHER_FOR_NORMALIZE_HTML_NOSPACE_ = /[\x00\x09-\x0d \x22\x27\x2d\/\x3c-\x3e`\x85\xa0\u2028\u2029]/g;
+
+/**
+ * Matches characters that need to be escaped for the named directives.
+ * @private {!RegExp}
+ */
+soy.esc.$$MATCHER_FOR_ESCAPE_JS_STRING_ = /[\x00\x08-\x0d\x22\x26\x27\/\x3c-\x3e\x5b-\x5d\x7b\x7d\x85\u2028\u2029]/g;
+
+/**
+ * Matches characters that need to be escaped for the named directives.
+ * @private {!RegExp}
+ */
+soy.esc.$$MATCHER_FOR_ESCAPE_JS_REGEX_ = /[\x00\x08-\x0d\x22\x24\x26-\/\x3a\x3c-\x3f\x5b-\x5e\x7b-\x7d\x85\u2028\u2029]/g;
+
+/**
+ * Matches characters that need to be escaped for the named directives.
+ * @private {!RegExp}
+ */
+soy.esc.$$MATCHER_FOR_ESCAPE_CSS_STRING_ = /[\x00\x08-\x0d\x22\x26-\x2a\/\x3a-\x3e@\\\x7b\x7d\x85\xa0\u2028\u2029]/g;
+
+/**
+ * Matches characters that need to be escaped for the named directives.
+ * @private {!RegExp}
+ */
+soy.esc.$$MATCHER_FOR_NORMALIZE_URI__AND__FILTER_NORMALIZE_URI__AND__FILTER_NORMALIZE_MEDIA_URI_ = /[\x00- \x22\x27-\x29\x3c\x3e\\\x7b\x7d\x7f\x85\xa0\u2028\u2029\uff01\uff03\uff04\uff06-\uff0c\uff0f\uff1a\uff1b\uff1d\uff1f\uff20\uff3b\uff3d]/g;
+
+/**
+ * A pattern that vets values produced by the named directives.
+ * @private {!RegExp}
+ */
+soy.esc.$$FILTER_FOR_FILTER_CSS_VALUE_ = /^(?!-*(?:expression|(?:moz-)?binding))(?!\s+)(?:[.#]?-?(?:[_a-z0-9-]+)(?:-[_a-z0-9-]+)*-?|(?:rgb|hsl)a?\([0-9.%,\u0020]+\)|-?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[a-z]{1,2}|%)?|!important|\s+)*$/i;
+
+/**
+ * A pattern that vets values produced by the named directives.
+ * @private {!RegExp}
+ */
+soy.esc.$$FILTER_FOR_FILTER_NORMALIZE_URI_ = /^(?![^#?]*\/(?:\.|%2E){2}(?:[\/?#]|$))(?:(?:https?|mailto):|[^&:\/?#]*(?:[\/?#]|$))/i;
+
+/**
+ * A pattern that vets values produced by the named directives.
+ * @private {!RegExp}
+ */
+soy.esc.$$FILTER_FOR_FILTER_NORMALIZE_MEDIA_URI_ = /^[^&:\/?#]*(?:[\/?#]|$)|^https?:|^data:image\/[a-z0-9+]+;base64,[a-z0-9+\/]+=*$|^blob:/i;
+
+/**
+ * A pattern that vets values produced by the named directives.
+ * @private {!RegExp}
+ */
+soy.esc.$$FILTER_FOR_FILTER_IMAGE_DATA_URI_ = /^data:image\/(?:bmp|gif|jpe?g|png|tiff|webp);base64,[a-z0-9+\/]+=*$/i;
+
+/**
+ * A pattern that vets values produced by the named directives.
+ * @private {!RegExp}
+ */
+soy.esc.$$FILTER_FOR_FILTER_TEL_URI_ = /^tel:[0-9a-z;=\-+._!~*'\u0020\/():&$#?@,]+$/i;
+
+/**
+ * A pattern that vets values produced by the named directives.
+ * @private {!RegExp}
+ */
+soy.esc.$$FILTER_FOR_FILTER_HTML_ATTRIBUTES_ = /^(?!on|src|(?:style|action|archive|background|cite|classid|codebase|data|dsync|href|longdesc|usemap)\s*$)(?:[a-z0-9_$:-]*)$/i;
+
+/**
+ * A pattern that vets values produced by the named directives.
+ * @private {!RegExp}
+ */
+soy.esc.$$FILTER_FOR_FILTER_HTML_ELEMENT_NAME_ = /^(?!script|style|title|textarea|xmp|no)[a-z0-9_$:-]*$/i;
+
+/**
+ * A helper for the Soy directive |normalizeHtml
+ * @param {*} value Can be of any type but will be coerced to a string.
+ * @return {string} The escaped text.
+ */
+soy.esc.$$normalizeHtmlHelper = function(value) {
+ var str = String(value);
+ return str.replace(
+ soy.esc.$$MATCHER_FOR_NORMALIZE_HTML_,
+ soy.esc.$$REPLACER_FOR_NORMALIZE_HTML__AND__ESCAPE_HTML_NOSPACE__AND__NORMALIZE_HTML_NOSPACE_);
+};
+
+/**
+ * A helper for the Soy directive |escapeHtmlNospace
+ * @param {*} value Can be of any type but will be coerced to a string.
+ * @return {string} The escaped text.
+ */
+soy.esc.$$escapeHtmlNospaceHelper = function(value) {
+ var str = String(value);
+ return str.replace(
+ soy.esc.$$MATCHER_FOR_ESCAPE_HTML_NOSPACE_,
+ soy.esc.$$REPLACER_FOR_NORMALIZE_HTML__AND__ESCAPE_HTML_NOSPACE__AND__NORMALIZE_HTML_NOSPACE_);
+};
+
+/**
+ * A helper for the Soy directive |normalizeHtmlNospace
+ * @param {*} value Can be of any type but will be coerced to a string.
+ * @return {string} The escaped text.
+ */
+soy.esc.$$normalizeHtmlNospaceHelper = function(value) {
+ var str = String(value);
+ return str.replace(
+ soy.esc.$$MATCHER_FOR_NORMALIZE_HTML_NOSPACE_,
+ soy.esc.$$REPLACER_FOR_NORMALIZE_HTML__AND__ESCAPE_HTML_NOSPACE__AND__NORMALIZE_HTML_NOSPACE_);
+};
+
+/**
+ * A helper for the Soy directive |escapeJsString
+ * @param {*} value Can be of any type but will be coerced to a string.
+ * @return {string} The escaped text.
+ */
+soy.esc.$$escapeJsStringHelper = function(value) {
+ var str = String(value);
+ return str.replace(
+ soy.esc.$$MATCHER_FOR_ESCAPE_JS_STRING_,
+ soy.esc.$$REPLACER_FOR_ESCAPE_JS_STRING__AND__ESCAPE_JS_REGEX_);
+};
+
+/**
+ * A helper for the Soy directive |escapeJsRegex
+ * @param {*} value Can be of any type but will be coerced to a string.
+ * @return {string} The escaped text.
+ */
+soy.esc.$$escapeJsRegexHelper = function(value) {
+ var str = String(value);
+ return str.replace(
+ soy.esc.$$MATCHER_FOR_ESCAPE_JS_REGEX_,
+ soy.esc.$$REPLACER_FOR_ESCAPE_JS_STRING__AND__ESCAPE_JS_REGEX_);
+};
+
+/**
+ * A helper for the Soy directive |escapeCssString
+ * @param {*} value Can be of any type but will be coerced to a string.
+ * @return {string} The escaped text.
+ */
+soy.esc.$$escapeCssStringHelper = function(value) {
+ var str = String(value);
+ return str.replace(
+ soy.esc.$$MATCHER_FOR_ESCAPE_CSS_STRING_,
+ soy.esc.$$REPLACER_FOR_ESCAPE_CSS_STRING_);
+};
+
+/**
+ * A helper for the Soy directive |filterCssValue
+ * @param {*} value Can be of any type but will be coerced to a string.
+ * @return {string} The escaped text.
+ */
+soy.esc.$$filterCssValueHelper = function(value) {
+ var str = String(value);
+ if (!soy.esc.$$FILTER_FOR_FILTER_CSS_VALUE_.test(str)) {
+ goog.asserts.fail('Bad value `%s` for |filterCssValue', [str]);
+ return 'zSoyz';
+ }
+ return str;
+};
+
+/**
+ * A helper for the Soy directive |normalizeUri
+ * @param {*} value Can be of any type but will be coerced to a string.
+ * @return {string} The escaped text.
+ */
+soy.esc.$$normalizeUriHelper = function(value) {
+ var str = String(value);
+ return str.replace(
+ soy.esc.$$MATCHER_FOR_NORMALIZE_URI__AND__FILTER_NORMALIZE_URI__AND__FILTER_NORMALIZE_MEDIA_URI_,
+ soy.esc.$$REPLACER_FOR_NORMALIZE_URI__AND__FILTER_NORMALIZE_URI__AND__FILTER_NORMALIZE_MEDIA_URI_);
+};
+
+/**
+ * A helper for the Soy directive |filterNormalizeUri
+ * @param {*} value Can be of any type but will be coerced to a string.
+ * @return {string} The escaped text.
+ */
+soy.esc.$$filterNormalizeUriHelper = function(value) {
+ var str = String(value);
+ if (!soy.esc.$$FILTER_FOR_FILTER_NORMALIZE_URI_.test(str)) {
+ goog.asserts.fail('Bad value `%s` for |filterNormalizeUri', [str]);
+ return 'about:invalid#zSoyz';
+ }
+ return str.replace(
+ soy.esc.$$MATCHER_FOR_NORMALIZE_URI__AND__FILTER_NORMALIZE_URI__AND__FILTER_NORMALIZE_MEDIA_URI_,
+ soy.esc.$$REPLACER_FOR_NORMALIZE_URI__AND__FILTER_NORMALIZE_URI__AND__FILTER_NORMALIZE_MEDIA_URI_);
+};
+
+/**
+ * A helper for the Soy directive |filterNormalizeMediaUri
+ * @param {*} value Can be of any type but will be coerced to a string.
+ * @return {string} The escaped text.
+ */
+soy.esc.$$filterNormalizeMediaUriHelper = function(value) {
+ var str = String(value);
+ if (!soy.esc.$$FILTER_FOR_FILTER_NORMALIZE_MEDIA_URI_.test(str)) {
+ goog.asserts.fail('Bad value `%s` for |filterNormalizeMediaUri', [str]);
+ return 'about:invalid#zSoyz';
+ }
+ return str.replace(
+ soy.esc.$$MATCHER_FOR_NORMALIZE_URI__AND__FILTER_NORMALIZE_URI__AND__FILTER_NORMALIZE_MEDIA_URI_,
+ soy.esc.$$REPLACER_FOR_NORMALIZE_URI__AND__FILTER_NORMALIZE_URI__AND__FILTER_NORMALIZE_MEDIA_URI_);
+};
+
+/**
+ * A helper for the Soy directive |filterImageDataUri
+ * @param {*} value Can be of any type but will be coerced to a string.
+ * @return {string} The escaped text.
+ */
+soy.esc.$$filterImageDataUriHelper = function(value) {
+ var str = String(value);
+ if (!soy.esc.$$FILTER_FOR_FILTER_IMAGE_DATA_URI_.test(str)) {
+ goog.asserts.fail('Bad value `%s` for |filterImageDataUri', [str]);
+ return 'data:image/gif;base64,zSoyz';
+ }
+ return str;
+};
+
+/**
+ * A helper for the Soy directive |filterTelUri
+ * @param {*} value Can be of any type but will be coerced to a string.
+ * @return {string} The escaped text.
+ */
+soy.esc.$$filterTelUriHelper = function(value) {
+ var str = String(value);
+ if (!soy.esc.$$FILTER_FOR_FILTER_TEL_URI_.test(str)) {
+ goog.asserts.fail('Bad value `%s` for |filterTelUri', [str]);
+ return 'about:invalid#zSoyz';
+ }
+ return str;
+};
+
+/**
+ * A helper for the Soy directive |filterHtmlAttributes
+ * @param {*} value Can be of any type but will be coerced to a string.
+ * @return {string} The escaped text.
+ */
+soy.esc.$$filterHtmlAttributesHelper = function(value) {
+ var str = String(value);
+ if (!soy.esc.$$FILTER_FOR_FILTER_HTML_ATTRIBUTES_.test(str)) {
+ goog.asserts.fail('Bad value `%s` for |filterHtmlAttributes', [str]);
+ return 'zSoyz';
+ }
+ return str;
+};
+
+/**
+ * A helper for the Soy directive |filterHtmlElementName
+ * @param {*} value Can be of any type but will be coerced to a string.
+ * @return {string} The escaped text.
+ */
+soy.esc.$$filterHtmlElementNameHelper = function(value) {
+ var str = String(value);
+ if (!soy.esc.$$FILTER_FOR_FILTER_HTML_ELEMENT_NAME_.test(str)) {
+ goog.asserts.fail('Bad value `%s` for |filterHtmlElementName', [str]);
+ return 'zSoyz';
+ }
+ return str;
+};
+
+/**
+ * Matches all tags, HTML comments, and DOCTYPEs in tag soup HTML.
+ * By removing these, and replacing any '<' or '>' characters with
+ * entities we guarantee that the result can be embedded into a
+ * an attribute without introducing a tag boundary.
+ *
+ * @private {!RegExp}
+ */
+soy.esc.$$HTML_TAG_REGEX_ = /<(?:!|\/?([a-zA-Z][a-zA-Z0-9:\-]*))(?:[^>'"]|"[^"]*"|'[^']*')*>/g;
+
+/**
+ * Matches all occurrences of '<'.
+ *
+ * @private {!RegExp}
+ */
+soy.esc.$$LT_REGEX_ = /</g;
+
+/**
+ * Maps lower-case names of innocuous tags to true.
+ *
+ * @private {!Object<string, boolean>}
+ */
+soy.esc.$$SAFE_TAG_WHITELIST_ = {'b': true, 'br': true, 'em': true, 'i': true, 's': true, 'sub': true, 'sup': true, 'u': true};
+
+/**
+ * Pattern for matching attribute name and value, where value is single-quoted
+ * or double-quoted.
+ * See http://www.w3.org/TR/2011/WD-html5-20110525/syntax.html#attributes-0
+ *
+ * @private {!RegExp}
+ */
+soy.esc.$$HTML_ATTRIBUTE_REGEX_ = /([a-zA-Z][a-zA-Z0-9:\-]*)[\t\n\r\u0020]*=[\t\n\r\u0020]*("[^"]*"|'[^']*')/g;
+
+// END GENERATED CODE
diff --git a/chromium/third_party/ink/wireserializer.js b/chromium/third_party/ink/wireserializer.js
new file mode 100644
index 00000000000..b88dcedf023
--- /dev/null
+++ b/chromium/third_party/ink/wireserializer.js
@@ -0,0 +1,845 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+/**
+ * @fileoverview Protocol Buffer 2 Serializer which serializes and deserializes
+ * messages using the wire format. Note that this serializer requires protocol
+ * buffer reflection, which carries some overhead.
+ * @supported any browser with DataView implemented. For now Chrome9, FF15, IE10
+ *
+ * @see https://developers.google.com/protocol-buffers/docs/encoding
+ *
+ * TODO(feinberg): Replace goog.math.Long with mutable long representation that
+ * permits in-place arithmetic to avoid allocations.
+ */
+
+
+goog.provide('net.proto2.contrib.WireSerializer');
+
+goog.require('goog.array');
+goog.require('goog.asserts');
+goog.require('goog.math.Long');
+goog.require('goog.proto2.Message');
+goog.require('goog.proto2.Serializer');
+
+
+
+/**
+ * Wire format serializer.
+ *
+ * @constructor
+ * @extends {goog.proto2.Serializer}
+ */
+net.proto2.contrib.WireSerializer = function() {
+ /**
+ * This array is where proto bytes go during serialization.
+ * It must be reset for each serialization.
+ * @type {!Array.<number>}
+ * @private
+ */
+ this.buffer_ = [];
+
+ /**
+ * Scratch workspace to avoid allocations during serialization.
+ * @type {{value: number, length: number}}
+ * @private
+ */
+ this.scratchTag32_ = {value: 0, length: 0};
+
+ /**
+ * Scratch workspace to avoid allocations during serialization.
+ * @type {{value: !goog.math.Long, length: number}}
+ * @private
+ */
+ this.scratchTag64_ = {value: goog.math.Long.getZero(), length: 0};
+
+ /**
+ * Scratch data view for coding/decoding little-endian numbers.
+ * @type {!DataView}
+ * @private
+ */
+ this.dataView_ = new DataView(new ArrayBuffer(8));
+};
+goog.inherits(net.proto2.contrib.WireSerializer, goog.proto2.Serializer);
+
+
+/**
+ * @return {!Array.<number>} The serialized form of the message.
+ * @override
+ */
+net.proto2.contrib.WireSerializer.prototype.serialize = function(message) {
+ if (message == null) {
+ return [];
+ }
+
+ this.buffer_ = [];
+
+ var descriptor = message.getDescriptor();
+ var fields = descriptor.getFields();
+
+ // Add the known fields.
+ for (var i = 0; i < fields.length; i++) {
+ var field = fields[i];
+
+ if (!message.has(field)) {
+ continue;
+ }
+
+ if (field.isRepeated()) {
+ if (field.isPacked()) {
+ this.serializePackedField_(message, field);
+ } else {
+ for (var j = 0, n = message.countOf(field); j < n; j++) {
+ var val = message.get(field, j);
+ this.getSerializedValue(field, val);
+ }
+ }
+ } else {
+ this.getSerializedValue(field, message.get(field));
+ }
+ }
+
+ return this.buffer_;
+};
+
+
+/**
+ * Append the serialized packed field to our serialization buffer.
+ * @param {!goog.proto2.Message} message The message containing the field
+ * to serialize.
+ * @param {!goog.proto2.FieldDescriptor} field The field to serialize.
+ * @return {boolean} Whether the field tag was serialized.
+ * @private
+ */
+net.proto2.contrib.WireSerializer.prototype.serializePackedField_ =
+ function(message, field) {
+ var buf = this.buffer_;
+
+ var wireType = 2; // Per definition.
+
+ // Tag.
+ this.serializeVarint_((field.getTag() << 3) | wireType);
+
+ // Make note of the current buffer size. After serializing the repeated
+ // fields, splice the size header at the current position.
+ var savedBufferSize = buf.length;
+ for (var j = 0, n = message.countOf(field); j < n; j++) {
+ var val = message.get(field, j);
+ this.getSerializedValue(field, val, true /* omit tag */);
+ }
+ var serializedData = buf.splice(
+ savedBufferSize, buf.length - savedBufferSize);
+ this.serializeVarint_(serializedData.length);
+
+ var args = [buf.length, 0].concat(serializedData);
+ buf.splice.apply(buf, args);
+
+ return true;
+};
+
+
+/**
+ * Append the serialized field tag to our serialization buffer.
+ * @param {goog.proto2.FieldDescriptor} field The field to serialize.
+ * @return {boolean} Whether the field tag was serialized.
+ * @private
+ */
+net.proto2.contrib.WireSerializer.prototype.serializeFieldTag_ =
+ function(field) {
+ var wireType = 0;
+ switch (field.getFieldType()) {
+ default:
+ return false;
+ case goog.proto2.Message.FieldType.SINT32:
+ case goog.proto2.Message.FieldType.SINT64:
+ case goog.proto2.Message.FieldType.BOOL:
+ case goog.proto2.Message.FieldType.INT64:
+ case goog.proto2.Message.FieldType.ENUM:
+ case goog.proto2.Message.FieldType.INT32:
+ case goog.proto2.Message.FieldType.UINT32:
+ case goog.proto2.Message.FieldType.UINT64:
+ wireType = 0;
+ break;
+ case goog.proto2.Message.FieldType.FIXED64:
+ case goog.proto2.Message.FieldType.SFIXED64:
+ case goog.proto2.Message.FieldType.DOUBLE:
+ wireType = 1;
+ break;
+ case goog.proto2.Message.FieldType.STRING:
+ case goog.proto2.Message.FieldType.BYTES:
+ case goog.proto2.Message.FieldType.MESSAGE:
+ wireType = 2;
+ break;
+ case goog.proto2.Message.FieldType.GROUP:
+ wireType = 3;
+ break;
+ case goog.proto2.Message.FieldType.FIXED32:
+ case goog.proto2.Message.FieldType.SFIXED32:
+ case goog.proto2.Message.FieldType.FLOAT:
+ wireType = 5;
+ break;
+ }
+ this.serializeVarint_((field.getTag() << 3) | wireType);
+ return true;
+};
+
+
+/**
+ * Returns the serialized form of the given value for the given field if the
+ * field is a Message or Group and returns the value unchanged otherwise, except
+ * for Infinity, -Infinity and NaN numerical values which are converted to
+ * string representation.
+ *
+ * @param {goog.proto2.FieldDescriptor} field The field from which this
+ * value came.
+ * @param {*} value The value of the field.
+ * @param {boolean=} opt_omitTag If present and true, do not serialize a field
+ * tag.
+ *
+ * @return {*} The value.
+ * @protected
+ */
+net.proto2.contrib.WireSerializer.prototype.getSerializedValue =
+ function(field, value, opt_omitTag) {
+ if (!opt_omitTag) {
+ if (!this.serializeFieldTag_(field)) {
+ return false;
+ }
+ }
+
+ switch (field.getFieldType()) {
+ default:
+ throw new Error('Unknown field type ' + field.getFieldType());
+ case goog.proto2.Message.FieldType.SINT32:
+ this.serializeVarint_(this.zigZagEncode(/** @type {number} */ (value)));
+ break;
+ case goog.proto2.Message.FieldType.SINT64:
+ this.serializeVarint64_(this.zigZagEncode64_(
+ goog.math.Long.fromString(/** @type {string} */(value))));
+ break;
+ case goog.proto2.Message.FieldType.BOOL:
+ this.serializeVarint_(value ? 1 : 0);
+ break;
+ case goog.proto2.Message.FieldType.INT32:
+ var numericValue = /** @type {number} */ (value);
+ if (numericValue > 0) {
+ this.serializeVarint_(numericValue);
+ } else {
+ // Negative 32 bit quantities are always 10 bytes long.
+ this.serializeVarint64_(goog.math.Long.fromInt(numericValue));
+ }
+ break;
+ case goog.proto2.Message.FieldType.INT64:
+ case goog.proto2.Message.FieldType.UINT64:
+ this.serializeVarint64_(
+ goog.math.Long.fromString(/** @type {string} */(value)));
+ break;
+ case goog.proto2.Message.FieldType.ENUM:
+ case goog.proto2.Message.FieldType.UINT32:
+ this.serializeVarint_(/** @type {number} */ (value));
+ break;
+ case goog.proto2.Message.FieldType.FIXED64:
+ case goog.proto2.Message.FieldType.SFIXED64:
+ this.serializeFixed_(
+ goog.math.Long.fromString(/** @type {string} */ (value)), 8);
+ break;
+ case goog.proto2.Message.FieldType.DOUBLE:
+ this.serializeDouble_(/** @type {number} */ (value));
+ break;
+ case goog.proto2.Message.FieldType.STRING:
+ this.serializeString(value);
+ break;
+ case goog.proto2.Message.FieldType.BYTES:
+ this.serializeBytes(value);
+ break;
+ case goog.proto2.Message.FieldType.GROUP:
+ var serialized = new net.proto2.contrib.WireSerializer().serialize(
+ /** @type {goog.proto2.Message} */ (value));
+ goog.array.extend(this.buffer_, serialized);
+ this.serializeVarint_((field.getTag() << 3) | 4);
+ break;
+ case goog.proto2.Message.FieldType.MESSAGE:
+ var serialized = new net.proto2.contrib.WireSerializer().serialize(
+ /** @type {goog.proto2.Message} */ (value));
+ this.serializeVarint_(serialized.length);
+ goog.array.extend(this.buffer_, serialized);
+ break;
+ case goog.proto2.Message.FieldType.FIXED32:
+ this.serializeFixed_(
+ goog.math.Long.fromNumber(/** @type {number} */ (value)), 4);
+ break;
+ case goog.proto2.Message.FieldType.SFIXED32:
+ this.serializeFixed_(
+ goog.math.Long.fromInt(/** @type {number} */ (value)), 4);
+ break;
+ case goog.proto2.Message.FieldType.FLOAT:
+ this.serializeFloat_(/** @type {number} */ (value));
+ break;
+ }
+ // To avoid allocations, this method serializes into a pre-existing buffer,
+ // rather than serializing into a new value object.
+ return null;
+};
+
+
+/** @override */
+net.proto2.contrib.WireSerializer.prototype.deserializeTo =
+ function(message, buffer) {
+ if (buffer == null) {
+ // Since value double-equals null, it may be either null or undefined.
+ // Ensure we return the same one, since they have different meanings.
+ return buffer;
+ }
+
+ if (buffer instanceof ArrayBuffer) {
+ buffer = new Uint8Array(buffer);
+ }
+
+ var descriptor = message.getDescriptor();
+ var offset = 0;
+ var size = buffer.length;
+ var view = function() {
+ return buffer.subarray(offset);
+ };
+ // Because subarray is broken on ie10, we can't simply advance our view of the
+ // buffer. Instead, we keep track of an offset.
+ while (offset < buffer.length) {
+ var tag = this.parseUnsignedVarInt_(view());
+ var tagValue = tag.value;
+ var tagLength = tag.length;
+ var index = tagValue >> 3;
+ var wireType = tagValue & 0x7; // Last 3 bits.
+
+ // Advance.
+ offset += tagLength;
+
+ var field = descriptor.findFieldByTag(index);
+ if (!field) {
+ // Unknown field; skip it.
+ offset += this.lengthForWireType_(wireType, view());
+ continue;
+ } else if (field.isPacked()) { // Packed repeated.
+ // Read byte length.
+ var v = this.parseUnsignedVarInt_(view());
+ var remaining = v.value;
+ offset += v.length;
+ while (remaining > 0 && offset < buffer.length) {
+ var packedValue =
+ this.getDeserializedValue(field, view());
+ if (!packedValue) {
+ throw new Error('Expected ' + field.getFieldType());
+ }
+ message.add(field, packedValue.value);
+ offset += packedValue.length;
+ remaining -= packedValue.length;
+ }
+ } else {
+ var value = this.getDeserializedValue(field, view());
+ if (!value) {
+ throw new Error('Expected ' + field.getFieldType());
+ }
+ offset += value.length;
+ if (field.isRepeated()) {
+ message.add(field, value.value);
+ } else {
+ message.set(field, value.value);
+ }
+ }
+ }
+};
+
+
+/**
+ * @param {number} wireType
+ * @param {*} buffer The data of the message.
+ * @return {number} Default length to use for a given fieldType.
+ * @private
+ */
+net.proto2.contrib.WireSerializer.prototype.lengthForWireType_ = function(
+ wireType, buffer) {
+ var length = 0;
+ switch (wireType) {
+ case 0: // int32, int64, uint32, uint64, sint32, sint64, bool, enum.
+ length = this.parseVarInt64_(buffer).length;
+ break;
+ case 1: // fixed64, sfixed64, double.
+ length = 8;
+ break;
+ case 2: // Length-delimited: string, bytes, messages, repeated fields.
+ var bufferLength = this.parseVarInt64_(buffer);
+ length = bufferLength.length + bufferLength.value.toInt();
+ break;
+ case 3: // "Start group". Not supported.
+ case 4: // "End group". Not supported.
+ goog.asserts.fail('Error deserializing group');
+ break;
+ case 5: // fixed32, sfixed32, float.
+ length = 4;
+ break;
+ }
+ return length;
+};
+
+
+/**
+ * Deserializes a message from the expected format and places the
+ * data in the message. The message must correspond to a group. Moreover
+ * the buffer must be positioned after the initial START_GROUP tag for the
+ * group. The message will be terminated by the first END_GROUP tag at the
+ * same nesting level. It is the responsibility of the caller to validate that
+ * its field index matches the one in the opening START_GROUP tag. Since groups
+ * are not length-delimited, this method returns the length of the parsed
+ * data excluding the END_GROUP tag.
+ *
+ * @param {goog.proto2.Message} message The message in which to
+ * place the information.
+ * @param {*} buffer The data of the message.
+ * @return {number} the length of the parsed message, excluding the closing tag.
+ * @protected
+ */
+ net.proto2.contrib.WireSerializer.prototype.deserializeGroupTo =
+ function(message, buffer) {
+ var descriptor = message.getDescriptor();
+ var parsedLength = 0;
+
+ while (true) {
+ var tag = this.parseUnsignedVarInt_(buffer);
+ var tagValue = tag.value;
+ var tagLength = tag.length;
+ var index = tagValue >> 3;
+ var wiretype = tagValue & 7;
+ if (wiretype == 4) {
+ // Got an end group.
+ break;
+ }
+ parsedLength += tagLength;
+ var value = {value: undefined, length: 0};
+ var field = descriptor.findFieldByTag(index);
+ if (field) {
+ value = this.getDeserializedValue(field, buffer.subarray(tagLength));
+ if (value && value.value !== null) {
+ if (field.isRepeated()) {
+ message.add(field, value.value);
+ } else {
+ message.set(field, value.value);
+ }
+ }
+ }
+ parsedLength += value.length;
+ if (buffer.length < tagLength + value.length) {
+ break;
+ }
+ buffer = buffer.subarray(tagLength + value.length);
+ }
+ return parsedLength;
+};
+
+
+/**
+ * @override
+ */
+net.proto2.contrib.WireSerializer.prototype.getDeserializedValue =
+ function(field, buffer) {
+ var value = null;
+ var t = field.getFieldType();
+ var varInt = this.parseVarInt64_(buffer);
+ var length = varInt.length;
+ switch (t) {
+ case goog.proto2.Message.FieldType.SINT32:
+ value = this.zigZagDecode_(varInt.value.toInt());
+ break;
+ case goog.proto2.Message.FieldType.SINT64:
+ value = this.zigZagDecode64_(varInt.value).toString();
+ break;
+ case goog.proto2.Message.FieldType.BOOL:
+ value = varInt.value.equals(goog.math.Long.getOne());
+ break;
+ case goog.proto2.Message.FieldType.INT64:
+ case goog.proto2.Message.FieldType.UINT64:
+ value = varInt.value.toString();
+ break;
+ case goog.proto2.Message.FieldType.INT32:
+ value = varInt.value.toInt();
+ break;
+ case goog.proto2.Message.FieldType.ENUM:
+ case goog.proto2.Message.FieldType.UINT32:
+ value = varInt.value.getLowBitsUnsigned();
+ break;
+ case goog.proto2.Message.FieldType.FIXED64:
+ case goog.proto2.Message.FieldType.SFIXED64:
+ value = this.parseFixed64_(buffer.subarray(0, 8)).toString();
+ length = 8;
+ break;
+ case goog.proto2.Message.FieldType.DOUBLE:
+ value = this.parseDouble_(buffer.subarray(0, 8));
+ length = 8;
+ break;
+ case goog.proto2.Message.FieldType.STRING:
+ var strBuffer =
+ buffer.subarray(varInt.length, varInt.length + varInt.value.toInt());
+ value = this.arrayBufferToUtf8String_(strBuffer);
+ length = varInt.length + varInt.value.toInt();
+ break;
+ case goog.proto2.Message.FieldType.BYTES:
+ var strBuffer =
+ buffer.subarray(varInt.length, varInt.length + varInt.value.toInt());
+ // Store the bytes using a String.
+ value = this.arrayBufferToString_(strBuffer);
+ length = varInt.length + varInt.value.toInt();
+ break;
+ case goog.proto2.Message.FieldType.GROUP:
+ value = field.getFieldMessageType().createMessageInstance();
+ var groupLength = this.deserializeGroupTo(value, buffer);
+ var next = buffer.subarray(groupLength);
+ var closingTag = this.parseVarInt64_(next);
+ var expected = (field.getTag() << 3) | 4;
+ goog.asserts.assert(closingTag.value.toInt() == expected,
+ 'Error deserializing group');
+ length = groupLength + closingTag.length;
+ break;
+ case goog.proto2.Message.FieldType.MESSAGE:
+ length = varInt.length + varInt.value.toInt();
+ var data = buffer.subarray(varInt.length, length);
+ value = field.getFieldMessageType().createMessageInstance();
+ this.deserializeTo(value, data);
+ break;
+ case goog.proto2.Message.FieldType.FIXED32:
+ case goog.proto2.Message.FieldType.SFIXED32:
+ value = this.parseFixed32_(
+ buffer.subarray(0, 4), t == goog.proto2.Message.FieldType.SFIXED32);
+ length = 4;
+ break;
+ case goog.proto2.Message.FieldType.FLOAT:
+ value = this.parseFloat_(buffer.subarray(0, 4));
+ length = 4;
+ break;
+ }
+ return {value: value, length: length};
+};
+
+
+/**
+ * @param {*} value Binary string that needs to be converted to bytes.
+ */
+net.proto2.contrib.WireSerializer.prototype.serializeBytes = function(value) {
+ if (goog.isDefAndNotNull(value)) {
+ var valueStr = /** @type {string} */ (value);
+ // Serialize the number of bytes, per spec of the wire format.
+ this.serializeVarint_(valueStr.length);
+ for (var i = 0; i < valueStr.length; i++) {
+ this.buffer_.push(valueStr.charCodeAt(i));
+ }
+ }
+};
+
+
+/**
+ * @param {*} value String (possibly utf-8) that needs to be converted to bytes.
+ */
+net.proto2.contrib.WireSerializer.prototype.serializeString = function(value) {
+ if (goog.isDefAndNotNull(value)) {
+ var valueStr = /** @type {string} */ (value);
+ // Inspired by:
+ // http://ecmanaut.blogspot.com/2006/07/encoding-decoding-utf8-in-javascript.html
+ var utf8 = unescape(encodeURIComponent(valueStr));
+ // Serialize the length of the encoded string: what we want is the number
+ // of bytes, not the number of characters, per spec of the wire format.
+ this.serializeVarint_(utf8.length);
+ for (var i = 0; i < utf8.length; i++) {
+ this.buffer_.push(utf8.charCodeAt(i));
+ }
+ }
+};
+
+
+/**
+ * @param {*} buffer to parse as String.
+ * @return {{value: string, length: number}}
+ */
+net.proto2.contrib.WireSerializer.prototype.parseString = function(buffer) {
+ var length = this.parseUnsignedVarInt_(buffer);
+ var strBuffer = buffer.subarray(length.length, length.length + length.value);
+ return {
+ value: this.arrayBufferToUtf8String_(strBuffer),
+ length: length.length + length.value
+ };
+};
+
+
+/**
+ * @param {number} number signed number that needs to be converted to unsigned.
+ * @return {number}
+ */
+net.proto2.contrib.WireSerializer.prototype.zigZagEncode =
+ function(number) {
+ var sign = number >>> 31;
+ return (number << 1) ^ -sign;
+};
+
+
+/**
+ * @param {number} number Unsigned number in zigzag format that needs
+ to be converted to signed.
+ * @return {number} signed.
+ * @private
+ */
+net.proto2.contrib.WireSerializer.prototype.zigZagDecode_ =
+ function(number) {
+ return (number >>> 1) ^ -(number & 1);
+};
+
+
+/**
+ * @param {!goog.math.Long} number signed number that needs to be converted to
+ * unsigned.
+ * @return {!goog.math.Long}
+ * @private
+ */
+net.proto2.contrib.WireSerializer.prototype.zigZagEncode64_ =
+ function(number) {
+ var sign = number.shiftRightUnsigned(63);
+ return number.shiftLeft(1).xor(sign.negate());
+};
+
+
+/**
+ * @param {!goog.math.Long} number Unsigned number in zigzag format that needs
+ to be converted to signed.
+ * @return {!goog.math.Long}
+ * @private
+ */
+net.proto2.contrib.WireSerializer.prototype.zigZagDecode64_ =
+ function(number) {
+ return number.shiftRightUnsigned(1).xor(
+ number.and(goog.math.Long.getOne()).negate());
+};
+
+
+/**
+ * Serialize the given number as a varint into our buffer.
+ * @param {number} number that needs to be converted to varint.
+ * @private
+ */
+net.proto2.contrib.WireSerializer.prototype.serializeVarint_ =
+ function(number) {
+ do {
+ var chunk = number & 0x7F;
+ number = number >>> 7;
+ if (number > 0) {
+ chunk = chunk | 0x80;
+ }
+ this.buffer_.push(chunk);
+ } while (number > 0);
+};
+
+
+/**
+ * Serialize the given 64-bit number as a varint into our buffer.
+ * @param {!goog.math.Long} number that needs to be encoded as varint.
+ * @private
+ */
+net.proto2.contrib.WireSerializer.prototype.serializeVarint64_ =
+ function(number) {
+ var mask = goog.math.Long.fromInt(0x7F);
+ do {
+ var chunk = number.and(mask).toInt();
+ number = number.shiftRightUnsigned(7);
+ if (number.greaterThan(goog.math.Long.getZero())) {
+ chunk = chunk | 0x80;
+ }
+ this.buffer_.push(chunk);
+ } while (number.greaterThan(goog.math.Long.getZero()));
+};
+
+
+/**
+ * @param {*} buffer from which field number and type needs to be extracted.
+ * @return {{value: !goog.math.Long, length: number}}
+ * @private
+ */
+net.proto2.contrib.WireSerializer.prototype.parseVarInt64_ = function(buffer) {
+ var valueInfo = this.scratchTag64_;
+ var number = goog.math.Long.fromNumber(0);
+ var i = 0;
+ for (; i < buffer.length; i++) {
+ var bits = goog.math.Long.fromInt(buffer[i] & 0x7F).shiftLeft(i * 7);
+ number = number.or(bits);
+ if ((buffer[i] & 0x80) == 0) {
+ break;
+ }
+ }
+ valueInfo.value = number;
+ valueInfo.length = i + 1;
+ return valueInfo;
+};
+
+
+/**
+ * A special case parser for unsigned 32-bit varints, which can fit comfortably
+ * in 32 bits during decoding.
+ * @param {*} buffer from which field number and type needs to be extracted.
+ * @return {{value: number, length: number}}
+ * @private
+ */
+net.proto2.contrib.WireSerializer.prototype.parseUnsignedVarInt_ =
+ function(buffer) {
+ var valueInfo = this.scratchTag32_;
+ var result = 0;
+ var i = 0;
+ for (; i < buffer.length; i++) {
+ result = result | ((buffer[i] & 0x7F) << (i * 7));
+ if ((buffer[i] & 0x80) == 0) {
+ break;
+ }
+ }
+ valueInfo.value = result;
+ valueInfo.length = i + 1;
+ return valueInfo;
+};
+
+
+/**
+ * @param {goog.math.Long} number that needs to be converted to little endian
+ * order.
+ * @param {number} size of the result array (4 = 32bit, 8 = 64bit).
+ * @private
+ */
+net.proto2.contrib.WireSerializer.prototype.serializeFixed_ =
+ function(number, size) {
+ var mask = goog.math.Long.fromInt(0xFF);
+ for (var i = 0; i < size; i++) {
+ var chunk = number.and(mask).toInt();
+ this.buffer_.push(chunk);
+ number = number.shiftRightUnsigned(8);
+ }
+};
+
+
+/**
+ * @param {*} buffer from which the fixed32 value needs to be extracted.
+ * @param {boolean} signed if the fixed32 value represents a signed value
+ * (i.e. sfixed32).
+ * @return {number}
+ * @private
+ */
+net.proto2.contrib.WireSerializer.prototype.parseFixed32_ = function(
+ buffer, signed) {
+ var number = 0;
+ for (var i = 0; i < buffer.length; i++) {
+ number = number | (buffer[i] << (i * 8));
+ }
+ if (!signed) {
+ // The bitwise operations above treat numbers as signed int32 values.
+ // Correct for this in the unsigned case by using >>> to coerce to unsigned.
+ number = number >>> 0;
+ }
+ return number;
+};
+
+
+/**
+ * @param {*} buffer from which the fixed64 value needs to be extracted.
+ * @return {!goog.math.Long}
+ * @private
+ */
+net.proto2.contrib.WireSerializer.prototype.parseFixed64_ = function(buffer) {
+ // Javascript numbers are only accurate up to 51 bits as they are stored as
+ // 64-bit floating points. We store the result in a goog.math.Long object to
+ // preserve full precision.
+ return new goog.math.Long(
+ this.parseFixed32_(buffer.subarray(0, 4), true),
+ this.parseFixed32_(buffer.subarray(4, 8), true));
+};
+
+
+/**
+ * @param {*} buffer from which double needs to be extracted.
+ * @return {number}
+ * @private
+ */
+net.proto2.contrib.WireSerializer.prototype.parseDouble_ = function(buffer) {
+ for (var i = 0; i < 8; i++) {
+ this.dataView_.setUint8(i, buffer[i]);
+ }
+ return this.dataView_.getFloat64(0, true); // little-endian
+};
+
+
+/**
+ * @param {*} buffer from which float needs to be extracted.
+ * @return {number}
+ * @private
+ */
+net.proto2.contrib.WireSerializer.prototype.parseFloat_ = function(buffer) {
+ for (var i = 0; i < 4; i++) {
+ this.dataView_.setUint8(i, buffer[i]);
+ }
+ return this.dataView_.getFloat32(0, true); // little-endian
+};
+
+
+/**
+ * @param {number} number to be serialized to 8 bytes.
+ * @private
+ */
+net.proto2.contrib.WireSerializer.prototype.serializeDouble_ =
+ function(number) {
+ this.dataView_.setFloat64(0, number, true); // little-endian
+ for (var i = 0; i < 8; i++) {
+ this.buffer_.push(this.dataView_.getUint8(i));
+ }
+};
+
+
+/**
+ * @param {number} number to be serialized to 4 bytes.
+ * @private
+ */
+net.proto2.contrib.WireSerializer.prototype.serializeFloat_ = function(number) {
+ this.dataView_.setFloat32(0, number, true); // little-endian
+ for (var i = 0; i < 4; i++) {
+ this.buffer_.push(this.dataView_.getUint8(i));
+ }
+};
+
+
+/**
+ * This method converts an ArrayBuffer into a string (with utf8 encoding).
+ *
+ * @param {ArrayBuffer} buffer The buffer to convert to a string
+ * @return {string}
+ * @private
+ */
+net.proto2.contrib.WireSerializer.prototype.arrayBufferToUtf8String_ = function(
+ buffer) {
+ var str = this.arrayBufferToString_(buffer);
+ // Inspired by:
+ // http://ecmanaut.blogspot.com/2006/07/encoding-decoding-utf8-in-javascript.html
+ return decodeURIComponent(escape(str));
+};
+
+
+/**
+ * This method converts an ArrayBuffer into a string (each index is 1 byte).
+ *
+ * The maximum stack size in chrome is ~125k. This means that using
+ * String.fromCharCode.apply will fail for strings larger than the maximum stack
+ * size. This method breaks up the calls to fromCharCode into ~64k chunks to
+ * work around this limitation.
+ *
+ * @param {ArrayBuffer} buffer The buffer to convert to a string
+ * @return {string}
+ * @private
+ */
+net.proto2.contrib.WireSerializer.prototype.arrayBufferToString_ = function(
+ buffer) {
+ var CHUNK_SIZE = 65536;
+ var str = '';
+ var view = new Uint16Array(buffer);
+ for (var offset = 0; offset < view.length; offset += CHUNK_SIZE) {
+ var len = Math.min(CHUNK_SIZE, view.length - offset);
+ var subview = view.subarray(offset, offset + len);
+ str += String.fromCharCode.apply(null, subview);
+ }
+ return str;
+};