aboutsummaryrefslogtreecommitdiff
path: root/contexts/data/lib/closure-library/closure/goog/labs
diff options
context:
space:
mode:
authorGravatar Benjamin Jones <bjones@galois.com>2012-09-27 12:49:34 -0700
committerGravatar Benjamin Jones <bjones@galois.com>2012-09-27 15:12:48 -0700
commit599b9ac75de5d4868b6ea390460d4dbe6784f06e (patch)
treec049deaae840a7365eacdeb90d3089d78ecb184c /contexts/data/lib/closure-library/closure/goog/labs
parentc1b9103d298842b30e0c7e0481a803b217c0e837 (diff)
updated Google closure library to 9-27-2012 SVN trunk
Diffstat (limited to 'contexts/data/lib/closure-library/closure/goog/labs')
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/.svn/all-wcprops5
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/.svn/entries31
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/mock/mock.js350
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/mock/mock_test.html115
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/net/.svn/all-wcprops17
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/net/.svn/entries96
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/net/.svn/prop-base/xhr.js.svn-base5
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/net/.svn/prop-base/xhr_test.html.svn-base5
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/net/.svn/text-base/xhr.js.svn-base423
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/net/.svn/text-base/xhr_test.html.svn-base424
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/net/image.js93
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/net/image_test.html21
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/net/image_test.js128
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/net/testdata/cleardot.gifbin0 -> 43 bytes
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/net/testdata/xhr_test_json.data2
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/net/testdata/xhr_test_text.data1
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/net/xhr.js673
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/net/xhr_test.html207
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/object/object.js46
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/object/object_test.html56
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/observe/notice.js63
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/observe/observable.js77
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/observe/observableset.js180
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/observe/observableset_test.html207
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/observe/observationset.js156
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/observe/observationset_test.html252
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/observe/observer.js100
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/observe/observer_test.html65
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/observe/simpleobservable.js129
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/observe/simpleobservable_test.html196
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/structs/map.js339
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/structs/map_perf.js201
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/structs/map_test.html434
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/structs/multimap.js279
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/structs/multimap_test.html332
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/testing/assertthat.js59
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/testing/assertthat_test.html73
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/testing/decoratormatcher.js94
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/testing/decoratormatcher_test.html43
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/testing/dictionarymatcher.js266
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/testing/dictionarymatcher_test.html70
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/testing/logicmatcher.js206
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/testing/logicmatcher_test.html59
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/testing/matcher.js51
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/testing/numbermatcher.js334
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/testing/numbermatcher_test.html79
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/testing/objectmatcher.js306
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/testing/objectmatcher_test.html102
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/testing/stringmatcher.js350
-rw-r--r--contexts/data/lib/closure-library/closure/goog/labs/testing/stringmatcher_test.html91
50 files changed, 6462 insertions, 1429 deletions
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/.svn/all-wcprops b/contexts/data/lib/closure-library/closure/goog/labs/.svn/all-wcprops
deleted file mode 100644
index 9418569..0000000
--- a/contexts/data/lib/closure-library/closure/goog/labs/.svn/all-wcprops
+++ /dev/null
@@ -1,5 +0,0 @@
-K 25
-svn:wc:ra_dav:version-url
-V 42
-/svn/!svn/ver/1453/trunk/closure/goog/labs
-END
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/.svn/entries b/contexts/data/lib/closure-library/closure/goog/labs/.svn/entries
deleted file mode 100644
index 3935db5..0000000
--- a/contexts/data/lib/closure-library/closure/goog/labs/.svn/entries
+++ /dev/null
@@ -1,31 +0,0 @@
-10
-
-dir
-1494
-http://closure-library.googlecode.com/svn/trunk/closure/goog/labs
-http://closure-library.googlecode.com/svn
-
-
-
-2011-12-07T18:41:01.000000Z
-1453
-pupius@google.com
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-0b95b8e8-c90f-11de-9d4f-f947ee5921c8
-
-net
-dir
-
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/mock/mock.js b/contexts/data/lib/closure-library/closure/goog/labs/mock/mock.js
new file mode 100644
index 0000000..fa6b86c
--- /dev/null
+++ b/contexts/data/lib/closure-library/closure/goog/labs/mock/mock.js
@@ -0,0 +1,350 @@
+// 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 Provides a mocking framework in Closure to make unit tests easy
+ * to write and understand. The methods provided here can be used to replace
+ * implementations of existing objects with 'mock' objects to abstract out
+ * external services and dependencies thereby isolating the code under test.
+ * Apart from mocking, methods are also provided to just monitor calls to an
+ * object (spying) and returning specific values for some or all the inputs to
+ * methods (stubbing).
+ *
+ */
+
+
+goog.provide('goog.labs.mock');
+
+goog.require('goog.array');
+goog.require('goog.debug.Error');
+goog.require('goog.functions');
+
+
+/**
+ * Mocks a given object or class.
+ *
+ * @param {!Object} objectOrClass An instance or a constructor of a class to be
+ * mocked.
+ *
+ * @return {!Object} The mocked object.
+ */
+goog.labs.mock = function(objectOrClass) {
+ // Go over properties of 'objectOrClass' and create a MockManager to
+ // be used for stubbing out calls to methods.
+ var mockedObject = new goog.labs.mock.MockManager_(objectOrClass);
+ return mockedObject.getMockedObject();
+};
+
+
+/**
+ * This array contains the name of the functions that are part of the base
+ * Object prototype.
+ * Basically a copy of goog.object.PROTOTYPE_FIELDS_.
+ * @const
+ * @type {!Array.<string>}
+ * @private
+ */
+goog.labs.mock.PROTOTYPE_FIELDS_ = [
+ 'constructor',
+ 'hasOwnProperty',
+ 'isPrototypeOf',
+ 'propertyIsEnumerable',
+ 'toLocaleString',
+ 'toString',
+ 'valueOf'
+];
+
+
+
+/**
+ * Sets up mock for the given object (or class), stubbing out all the defined
+ * methods. By default, all stubs return {@code undefined}, though stubs can be
+ * later defined using {@code goog.labs.mock.when}.
+ *
+ * @param {!Object|!Function} objOrClass The object to set up the mock for.
+ *
+ * @constructor
+ * @private
+ */
+goog.labs.mock.MockManager_ = function(objOrClass) {
+ /**
+ * Proxies the methods for the mocked object or class to execute the stubs.
+ * @type {!Object}
+ * @private
+ * TODO(user): make instanceof work.
+ */
+ this.mockedObject_ = {};
+
+ /**
+ * Holds the stub bindings established so far.
+ * @private
+ */
+ this.methodBindings_ = [];
+
+ /**
+ * Proxies the calls to establish the first step of the stub bindings (object
+ * and method name)
+ * @private
+ */
+ this.binderProxy_ = {};
+
+ var obj;
+ if (goog.isFunction(objOrClass)) {
+ // Create a temporary subclass with a no-op constructor so that we can
+ // create an instance and determine what methods it has.
+ /** @constructor */
+ function tempCtor() {};
+ goog.inherits(tempCtor, objOrClass);
+ obj = new tempCtor();
+ } else {
+ obj = objOrClass;
+ }
+
+ var enumerableProperties = goog.object.getKeys(obj);
+ // The non enumerable properties are added due to the fact that IE8 does not
+ // enumerate any of the prototype Object functions even when overriden and
+ // mocking these is sometimes needed.
+ for (var i = 0; i < goog.labs.mock.PROTOTYPE_FIELDS_.length; i++) {
+ var prop = goog.labs.mock.PROTOTYPE_FIELDS_[i];
+ if (!goog.array.contains(enumerableProperties, prop)) {
+ enumerableProperties.push(prop);
+ }
+ }
+
+ // Adds the properties to the mock, creating a proxy stub for each method on
+ // the instance.
+ for (var i = 0; i < enumerableProperties.length; i++) {
+ var prop = enumerableProperties[i];
+ if (goog.isFunction(obj[prop])) {
+ this.mockedObject_[prop] = goog.bind(this.executeStub_, this, prop);
+ this.binderProxy_[prop] = goog.bind(this.handleMockCall_, this, prop);
+ }
+ }
+
+ this.mockedObject_.$binderProxy = this.binderProxy_;
+};
+
+
+/**
+ * Handles the first step in creating a stub, returning a stub-binder that
+ * is later used to bind a stub for a method.
+ *
+ * @param {string} methodName The name of the method being bound.
+ * @param {...} var_args The arguments to the method.
+ *
+ * @return {!goog.labs.mock.StubBinder_} The stub binder.
+ * @private
+ */
+goog.labs.mock.MockManager_.prototype.handleMockCall_ =
+ function(methodName, var_args) {
+ var args = goog.array.slice(arguments, 1);
+ return new goog.labs.mock.StubBinder_(this, methodName, args);
+};
+
+
+/**
+ * Returns the mock object. This should have a stubbed method for each method
+ * on the object being mocked.
+ *
+ * @return {!Object} The mock object.
+ */
+goog.labs.mock.MockManager_.prototype.getMockedObject = function() {
+ return this.mockedObject_;
+};
+
+
+/**
+ * Adds a binding for the method name and arguments to be stubbed.
+ *
+ * @param {string} methodName The name of the stubbed method.
+ * @param {!Array} args The arguments passed to the method.
+ * @param {!Function} func The stub function.
+ *
+ */
+goog.labs.mock.MockManager_.prototype.addBinding =
+ function(methodName, args, func) {
+ var binding = new goog.labs.mock.MethodBinding_(methodName, args, func);
+ this.methodBindings_.push(binding);
+};
+
+
+/**
+ * Returns a stub, if defined, for the method and arguments passed in as
+ * parameters.
+ *
+ * @param {string} methodName The name of the stubbed method.
+ * @param {Array} args The arguments passed to the method.
+ *
+ * @return {!Function|undefined} The stub function or undefined.
+ * @private
+ */
+goog.labs.mock.MockManager_.prototype.findBinding_ =
+ function(methodName, args) {
+ var stub = goog.array.find(this.methodBindings_, function(binding) {
+ return binding.matches(methodName, args);
+ });
+ return stub && stub.getStub();
+};
+
+
+/**
+ * Looks up the list of stubs defined on the mock object and executes the
+ * function associated with that stub.
+ *
+ * @param {string} methodName The name of the method to execute.
+ * @param {...} var_args The arguments passed to the method.
+ *
+ * @return {*} Value returned by the stub function.
+ * @private
+ */
+goog.labs.mock.MockManager_.prototype.executeStub_ =
+ function(methodName, var_args) {
+ var args = goog.array.slice(arguments, 1);
+ var func = this.findBinding_(methodName, args);
+ if (func) {
+ return func.apply(null, args);
+ }
+};
+
+
+
+/**
+ * The stub binder is the object that helps define the stubs by binding
+ * method name to the stub method.
+ *
+ * @param {!goog.labs.mock.MockManager_} mockManager The mock manager.
+ * @param {string} name The method name.
+ * @param {!Array} args The other arguments to the method.
+ *
+ * @constructor
+ * @private
+ */
+goog.labs.mock.StubBinder_ = function(mockManager, name, args) {
+ /**
+ * The mock manager instance.
+ * @type {!goog.labs.mock.MockManager_}
+ * @private
+ */
+ this.mockManager_ = mockManager;
+
+ /**
+ * Holds the name of the method to be bound.
+ * @type {string}
+ * @private
+ */
+ this.name_ = name;
+
+ /**
+ * Holds the arguments for the method.
+ * @type {!Array}
+ * @private
+ */
+ this.args_ = args;
+};
+
+
+/**
+ * Defines the stub to be called for the method name and arguments bound
+ * earlier.
+ * TODO(user): Add support for the 'Answer' interface.
+ *
+ * @param {!Function} func The stub.
+ */
+goog.labs.mock.StubBinder_.prototype.then = function(func) {
+ this.mockManager_.addBinding(this.name_, this.args_, func);
+};
+
+
+/**
+ * Defines the stub to return a specific value for a method name and arguments.
+ *
+ * @param {*} value The value to return.
+ */
+goog.labs.mock.StubBinder_.prototype.thenReturn = function(value) {
+ this.mockManager_.addBinding(this.name_, this.args_,
+ goog.functions.constant(value));
+};
+
+
+/**
+ * Facilitates (and is the first step in) setting up stubs. Obtains an object
+ * on which, the method to be mocked is called to create a stub. Sample usage:
+ *
+ * var mockObj = goog.labs.mock(objectBeingMocked);
+ * goog.labs.mock.when(mockObj).getFoo(3).thenReturn(4);
+ *
+ * @param {!Object} mockObject The mocked object.
+ *
+ * @return {!goog.labs.mock.StubBinder_} The property binder.
+ */
+goog.labs.mock.when = function(mockObject) {
+ return mockObject.$binderProxy;
+};
+
+
+
+/**
+ * Represents a binding between a method name, args and a stub.
+ *
+ * @param {string} methodName The name of the method being stubbed.
+ * @param {!Array} args The arguments passed to the method.
+ * @param {!Function} stub The stub function to be called for the given method.
+ * @constructor
+ * @private
+ */
+goog.labs.mock.MethodBinding_ = function(methodName, args, stub) {
+ /**
+ * The name of the method being stubbed.
+ * @type {string}
+ * @private
+ */
+ this.methodName_ = methodName;
+
+ /**
+ * The arguments for the method being stubbed.
+ * @type {!Array}
+ * @private
+ */
+ this.args_ = args;
+
+ /**
+ * The stub function.
+ * @type {!Function}
+ * @private
+ */
+ this.stub_ = stub;
+};
+
+
+/**
+ * @return {!Function} The stub to be executed.
+ */
+goog.labs.mock.MethodBinding_.prototype.getStub = function() {
+ return this.stub_;
+};
+
+
+/**
+ * Determines whether the given args match the stored args_. Used to determine
+ * which stub to invoke for a method.
+ *
+ * @param {string} methodName The name of the method being stubbed.
+ * @param {!Array} args An array of arguments.
+ * @return {boolean} If it matches the stored arguments.
+ */
+goog.labs.mock.MethodBinding_.prototype.matches = function(methodName, args) {
+ //TODO(user): More elaborate argument matching.
+ return this.methodName_ == methodName &&
+ goog.array.equals(args, this.args_);
+};
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/mock/mock_test.html b/contexts/data/lib/closure-library/closure/goog/labs/mock/mock_test.html
new file mode 100644
index 0000000..93a3984
--- /dev/null
+++ b/contexts/data/lib/closure-library/closure/goog/labs/mock/mock_test.html
@@ -0,0 +1,115 @@
+<!DOCTYPE html>
+<html>
+<!--
+Copyright 2012 The Closure Library Authors. All Rights Reserved.
+
+Use of this source code is governed by the Apache License, Version 2.0.
+See the COPYING file for details.
+-->
+<head>
+<title>Closure Unit Tests - goog.labs.mock</title>
+<script src="../../base.js"></script>
+<script>
+
+goog.require('goog.labs.mock');
+goog.require('goog.testing.jsunit');
+
+</script>
+</head>
+<body>
+<script>
+
+var ParentClass = function() {};
+ParentClass.prototype.method1 = function() {};
+ParentClass.prototype.x = 1;
+
+var ChildClass = function() {};
+goog.inherits(ChildClass, ParentClass);
+ChildClass.prototype.method2 = function() {};
+ChildClass.prototype.y = 2;
+
+function testParentClass() {
+ var parentMock = goog.labs.mock(ParentClass);
+
+ assertNotUndefined(parentMock.method1);
+ assertUndefined(parentMock.method1());
+ assertUndefined(parentMock.method2);
+ assertUndefined(parentMock.x);
+ assertUndefined(parentMock.y);
+}
+
+function testChildClass() {
+ var childMock = goog.labs.mock(ChildClass);
+
+ assertNotUndefined(childMock.method1);
+ assertUndefined(childMock.method1());
+ assertNotUndefined(childMock.method2);
+ assertUndefined(childMock.method2());
+ assertUndefined(childMock.x);
+ assertUndefined(childMock.y);
+}
+
+function testParentClassInstance() {
+ var parentMock = goog.labs.mock(new ParentClass());
+
+ assertNotUndefined(parentMock.method1);
+ assertUndefined(parentMock.method1());
+ assertUndefined(parentMock.method2);
+ assertUndefined(parentMock.x);
+ assertUndefined(parentMock.y);
+}
+
+function testChildClassInstance() {
+ var childMock = goog.labs.mock(new ChildClass());
+
+ assertNotUndefined(childMock.method1);
+ assertUndefined(childMock.method1());
+ assertNotUndefined(childMock.method2);
+ assertUndefined(childMock.method2());
+ assertUndefined(childMock.x);
+ assertUndefined(childMock.y);
+}
+
+function testNonEnumerableProperties() {
+ var mockObject = goog.labs.mock({});
+ assertNotUndefined(mockObject.toString);
+ goog.labs.mock.when(mockObject).toString().then(function() {
+ return 'toString';
+ });
+ assertEquals('toString', mockObject.toString());
+}
+
+function testBasicStubbing() {
+ var obj = {
+ method1: function(i) {
+ return 2 * i;
+ },
+ method2: function(i, str) {
+ return str;
+ },
+ method3: function(x) {
+ return x;
+ }
+ };
+
+ var mockObj = goog.labs.mock(obj);
+ goog.labs.mock.when(mockObj).method1(2).then(function(i) {return i;});
+
+ assertEquals(4, obj.method1(2));
+ assertEquals(2, mockObj.method1(2));
+ assertUndefined(mockObj.method1(4));
+
+ goog.labs.mock.when(mockObj).method2(1, 'hi').then(function(i) {return 'oh'});
+ assertEquals('hi', obj.method2(1, 'hi'));
+ assertEquals('oh', mockObj.method2(1, 'hi'));
+ assertUndefined(mockObj.method2(3, 'foo'));
+
+ goog.labs.mock.when(mockObj).method3(4).thenReturn(10);
+ assertEquals(4, obj.method3(4));
+ assertEquals(10, mockObj.method3(4));
+ assertUndefined(mockObj.method3(5));
+}
+
+</script>
+</body>
+</html>
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/net/.svn/all-wcprops b/contexts/data/lib/closure-library/closure/goog/labs/net/.svn/all-wcprops
deleted file mode 100644
index 2b312b0..0000000
--- a/contexts/data/lib/closure-library/closure/goog/labs/net/.svn/all-wcprops
+++ /dev/null
@@ -1,17 +0,0 @@
-K 25
-svn:wc:ra_dav:version-url
-V 46
-/svn/!svn/ver/1453/trunk/closure/goog/labs/net
-END
-xhr_test.html
-K 25
-svn:wc:ra_dav:version-url
-V 60
-/svn/!svn/ver/1453/trunk/closure/goog/labs/net/xhr_test.html
-END
-xhr.js
-K 25
-svn:wc:ra_dav:version-url
-V 53
-/svn/!svn/ver/1453/trunk/closure/goog/labs/net/xhr.js
-END
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/net/.svn/entries b/contexts/data/lib/closure-library/closure/goog/labs/net/.svn/entries
deleted file mode 100644
index 671edc4..0000000
--- a/contexts/data/lib/closure-library/closure/goog/labs/net/.svn/entries
+++ /dev/null
@@ -1,96 +0,0 @@
-10
-
-dir
-1494
-http://closure-library.googlecode.com/svn/trunk/closure/goog/labs/net
-http://closure-library.googlecode.com/svn
-
-
-
-2011-12-07T18:41:01.000000Z
-1453
-pupius@google.com
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-0b95b8e8-c90f-11de-9d4f-f947ee5921c8
-
-xhr_test.html
-file
-
-
-
-
-2011-12-23T22:42:29.432342Z
-08f19869b6b3067562fcb9bc509c0eb8
-2011-12-07T18:41:01.000000Z
-1453
-pupius@google.com
-has-props
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-12561
-
-xhr.js
-file
-
-
-
-
-2011-12-23T22:42:29.432342Z
-77170937aa4671abae08f58b0efd3239
-2011-12-07T18:41:01.000000Z
-1453
-pupius@google.com
-has-props
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-13736
-
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/net/.svn/prop-base/xhr.js.svn-base b/contexts/data/lib/closure-library/closure/goog/labs/net/.svn/prop-base/xhr.js.svn-base
deleted file mode 100644
index 530636b..0000000
--- a/contexts/data/lib/closure-library/closure/goog/labs/net/.svn/prop-base/xhr.js.svn-base
+++ /dev/null
@@ -1,5 +0,0 @@
-K 13
-svn:mime-type
-V 15
-text/javascript
-END
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/net/.svn/prop-base/xhr_test.html.svn-base b/contexts/data/lib/closure-library/closure/goog/labs/net/.svn/prop-base/xhr_test.html.svn-base
deleted file mode 100644
index d356868..0000000
--- a/contexts/data/lib/closure-library/closure/goog/labs/net/.svn/prop-base/xhr_test.html.svn-base
+++ /dev/null
@@ -1,5 +0,0 @@
-K 13
-svn:mime-type
-V 9
-text/html
-END
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/net/.svn/text-base/xhr.js.svn-base b/contexts/data/lib/closure-library/closure/goog/labs/net/.svn/text-base/xhr.js.svn-base
deleted file mode 100644
index e2135d2..0000000
--- a/contexts/data/lib/closure-library/closure/goog/labs/net/.svn/text-base/xhr.js.svn-base
+++ /dev/null
@@ -1,423 +0,0 @@
-// 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 Offered as an alternative to XhrIo as a way for making requests
- * via XMLHttpRequest. Instead of mirroring the XHR interface and exposing
- * events, deferreds are used as a way to pass a "promise" of the response to
- * interested parties.
- *
- */
-
-goog.provide('goog.labs.net.xhr');
-goog.provide('goog.labs.net.xhr.Error');
-goog.provide('goog.labs.net.xhr.HttpError');
-goog.provide('goog.labs.net.xhr.TimeoutError');
-
-goog.require('goog.async.Deferred');
-goog.require('goog.debug.Error');
-goog.require('goog.json');
-goog.require('goog.net.HttpStatus');
-goog.require('goog.net.XmlHttp');
-goog.require('goog.string');
-goog.require('goog.uri.utils');
-
-
-
-goog.scope(function() {
- var _ = goog.labs.net.xhr;
- var Deferred = goog.async.Deferred;
- var HttpStatus = goog.net.HttpStatus;
-
-
- /**
- * Configuration options for an XMLHttpRequest.
- * - headers: map of header key/value pairs.
- * - timeoutMs: number of milliseconds after which the request will be timed
- * out by the client. Default is to allow the browser to handle timeouts.
- * - withCredentials: whether user credentials are to be included in a
- * cross-origin request. See:
- * http://dev.w3.org/2006/webapi/XMLHttpRequest-2/#the-withcredentials-attribute
- * - mimeType: allows the caller to override the content-type and charset for
- * the request, which is useful when requesting binary data. See:
- * http://dev.w3.org/2006/webapi/XMLHttpRequest-2/#dom-xmlhttprequest-overridemimetype
- * - xssiPrefix: Prefix used for protecting against XSSI attacks, which should
- * be removed before parsing the response as JSON.
- *
- * @typedef {{
- * headers: (Object.<string>|undefined),
- * timeoutMs: (number|undefined),
- * withCredentials: (boolean|undefined),
- * mimeType: (string|undefined),
- * xssiPrefix: (string|undefined)
- * }}
- */
- _.Options;
-
-
- /**
- * Defines the types that are allowed as post data.
- * @typedef {(ArrayBuffer|Blob|Document|FormData|null|string|undefined)}
- */
- _.PostData;
-
-
- /**
- * The Content-Type HTTP header name.
- * @type {string}
- */
- _.CONTENT_TYPE_HEADER = 'Content-Type';
-
-
- /**
- * The Content-Type HTTP header value for a url-encoded form.
- * @type {string}
- */
- _.FORM_CONTENT_TYPE = 'application/x-www-form-urlencoded;charset=utf-8';
-
-
- /**
- * Sends a get request, returning a deferred which will be called back with
- * the response text once the request completes.
- *
- * @param {string} url The URL to request.
- * @param {_.Options=} opt_options Configuration options for the request.
- * @return {!Deferred} A deferred object that will be called back
- * with the response text once the request finishes.
- */
- _.get = function(url, opt_options) {
- return _.send('GET', url, null, opt_options).
- addCallback(_.getResponseText_);
- };
-
-
- /**
- * Sends a post request, returning a deferred which will be called back with
- * the response text once the request completes.
- *
- * @param {string} url The URL to request.
- * @param {_.PostData} data The body of the post request.
- * @param {_.Options=} opt_options Configuration options for the request.
- * @return {!Deferred} A deferred object that will be called back
- * with the response text once the request finishes.
- */
- _.post = function(url, data, opt_options) {
- return _.send('POST', url, data, opt_options).
- addCallback(_.getResponseText_);
- };
-
-
- /**
- * Sends a get request, returning a deferred which will be called back with
- * the parsed response text once the request completes.
- *
- * @param {string} url The URL to request.
- * @param {_.Options=} opt_options Configuration options for the request.
- * @return {!Deferred} A deferred object that will be called back
- * with the response JSON once the request finishes.
- */
- _.getJson = function(url, opt_options) {
- var d = _.send('GET', url, null, opt_options);
- _.addJsonParsingCallbacks_(d, opt_options);
- return d;
- };
-
-
- /**
- * Sends a post request, returning a deferred which will be called back with
- * the parsed response text once the request completes.
- *
- * @param {string} url The URL to request.
- * @param {_.PostData} data The body of the post request.
- * @param {_.Options=} opt_options Configuration options for the request.
- * @return {!Deferred} A deferred object that will be called back
- * with the response JSON once the request finishes.
- */
- _.postJson = function(url, data, opt_options) {
- var d = _.send('POST', url, data, opt_options);
- _.addJsonParsingCallbacks_(d, opt_options);
- return d;
- };
-
-
- /**
- * Sends a request using XMLHttpRequest and returns a deferred.
- *
- * @param {string} method The HTTP method for the request.
- * @param {string} url The URL to request.
- * @param {_.PostData} data The body of the post request.
- * @param {_.Options=} opt_options Configuration options for the request.
- * @return {!Deferred} A deferred object that will be called back
- * with the XHR object when the request finishes.
- */
- _.send = function(method, url, data, opt_options) {
-
- // When the deferred is cancelled, we abort the XHR. We want to make sure
- // the readystatechange event still fires, so it can do the timeout
- // cleanup, however we don't want the callback or errback to be called
- // again. Thus the slight ugliness here. If deferreds were pushed into
- // makeRequest, this could become a lot cleaner but we want an option for
- // people not to include goog.async.Deferred.
-
- function cancel() {
- cancelled = true;
- xhr.abort();
- xhr.onreadystatechange = goog.nullFunction;
- // TODO(user): We could return an AbortedError if we wanted to.
- }
-
- function callback(data) {
- if (!cancelled) {
- d.callback(data);
- }
- }
-
- function errback(err) {
- if (!cancelled) {
- d.errback(err);
- }
- }
-
- var cancelled = false;
- var d = new Deferred(cancel);
- var xhr = _.makeRequest(method, url, data, opt_options, callback, errback);
-
- return d;
- };
-
-
- /**
- * Creates a new XMLHttpRequest and initiates a request.
- *
- * @param {string} method The HTTP method for the request.
- * @param {string} url The URL to request.
- * @param {_.PostData} data The body of the post request, unless the content
- * type is explicitly set in the Options, then it will default to form
- * urlencoded.
- * @param {_.Options=} opt_options Configuration options for the request.
- * @param {function(XMLHttpRequest)=} opt_callback Optional callback to call
- * when the request completes.
- * @param {function(Error)=} opt_errback Optional callback to call
- * when there is an error.
- * @return {!XMLHttpRequest} The new XMLHttpRequest.
- */
- _.makeRequest = function(
- method, url, data, opt_options, opt_callback, opt_errback) {
- var options = opt_options || {};
- var callback = opt_callback || goog.nullFunction;
- var errback = opt_errback || goog.nullFunction;
- var timer;
-
- var xhr = /** @type {!XMLHttpRequest} */ (goog.net.XmlHttp());
- try {
- xhr.open(method, url, true);
- } catch (e) {
- // XMLHttpRequest.open may throw when 'open' is called, for example, IE7
- // throws "Access Denied" for cross-origin requests.
- errback(new _.Error('Error opening XHR: ' + e.message, url, xhr));
- return xhr;
- }
-
- // So sad that IE doesn't support onload and onerror.
- xhr.onreadystatechange = function() {
- if (xhr.readyState == goog.net.XmlHttp.ReadyState.COMPLETE) {
- window.clearTimeout(timer);
- if (HttpStatus.isSuccess(xhr.status) ||
- xhr.status === 0 && !_.isEffectiveSchemeHttp_(url)) {
- callback(xhr);
- } else {
- errback(new _.HttpError(xhr.status, url, xhr));
- }
- }
- };
-
- // Set the headers.
- var contentTypeIsSet = false;
- if (options.headers) {
- for (var key in options.headers) {
- xhr.setRequestHeader(key, options.headers[key]);
- }
- contentTypeIsSet = _.CONTENT_TYPE_HEADER in options.headers;
- }
-
- // If a content type hasn't been set, default to form-urlencoded/UTF8 for
- // POSTs. This is because some proxies have been known to reject posts
- // without a content-type.
- if (method == 'POST' && !contentTypeIsSet) {
- xhr.setRequestHeader(_.CONTENT_TYPE_HEADER, _.FORM_CONTENT_TYPE);
- }
-
- // Set whether to pass cookies on cross-domain requests (if applicable).
- // @see http://dev.w3.org/2006/webapi/XMLHttpRequest-2/#the-withcredentials-attribute
- if (options.withCredentials) {
- xhr.withCredentials = options.withCredentials;
- }
-
- // Allow the request to override the mime type, useful for getting binary
- // data from the server. e.g. 'text/plain; charset=x-user-defined'.
- // @see http://dev.w3.org/2006/webapi/XMLHttpRequest-2/#dom-xmlhttprequest-overridemimetype
- if (options.mimeType) {
- xhr.overrideMimeType(options.mimeType);
- }
-
- // Handle timeouts, if requested.
- if (options.timeoutMs > 0) {
- timer = window.setTimeout(function() {
- // Clear event listener before aborting so the errback will not be
- // called twice.
- xhr.onreadystatechange = goog.nullFunction;
- xhr.abort();
- errback(new _.TimeoutError(url, xhr));
- }, options.timeoutMs);
- }
-
- // Trigger the send.
- try {
- xhr.send(data);
- } catch (e) {
- // XMLHttpRequest.send is known to throw on some versions of FF, for example
- // if a cross-origin request is disallowed.
- errback(new _.Error('Error sending XHR: ' + e.message, url, xhr));
- }
-
- return xhr;
- };
-
-
- /**
- * @param {string} url The URL to test.
- * @return {boolean} Whether the effective scheme is HTTP or HTTPs.
- * @private
- */
- _.isEffectiveSchemeHttp_ = function(url) {
- var scheme = goog.uri.utils.getEffectiveScheme(url);
- // NOTE(user): Empty-string is for the case under FF3.5 when the location
- // is not defined inside a web worker.
- return scheme == 'http' || scheme == 'https' || scheme == '';
- };
-
-
- /**
- * Returns the response text of an XHR object. Intended for use as a callback
- * on a deferred.
- *
- * @param {!XMLHttpRequest} xhr The XHR object.
- * @return {string} The response text.
- * @private
- */
- _.getResponseText_ = function(xhr) {
- return xhr.responseText;
- };
-
-
- /**
- * Adds the callbacks required for parsing a response as JSON. The chain will
- * expect an XMLHttpRequest and will result in a JS object. Parse errors will
- * trigger the errback chain.
- *
- * @param {!Deferred} d The deferred to add callbacks to.
- * @param {_.Options|undefined} options The options object.
- * @private
- */
- _.addJsonParsingCallbacks_ = function(d, options) {
- d.addCallback(_.getResponseText_);
- if (options && options.xssiPrefix) {
- d.addCallback(goog.partial(_.stripXssiPrefix_, options.xssiPrefix));
- }
- d.addCallback(goog.json.parse);
- };
-
-
- /**
- * Strips the XSSI prefix from the input string.
- *
- * @param {string} prefix The XSSI prefix.
- * @param {string} string The string to strip the prefix from.
- * @return {string} The input string without the prefix.
- * @private
- */
- _.stripXssiPrefix_ = function(prefix, string) {
- if (goog.string.startsWith(string, prefix)) {
- string = string.substring(prefix.length);
- }
- return string;
- };
-
-
-
- /**
- * Generic error that may occur during a request.
- *
- * @param {string} message The error message.
- * @param {string} url The URL that was being requested.
- * @param {!XMLHttpRequest} xhr The XMLHttpRequest that failed.
- * @extends {goog.debug.Error}
- * @constructor
- */
- _.Error = function(message, url, xhr) {
- goog.base(this, message + ', url=' + url);
-
- /**
- * The URL that was requested.
- * @type {string}
- */
- this.url = url;
-
- /**
- * The XMLHttpRequest corresponding with the failed request.
- * @type {!XMLHttpRequest}
- */
- this.xhr = xhr;
- };
- goog.inherits(_.Error, goog.debug.Error);
-
-
-
- /**
- * Class for HTTP errors.
- *
- * @param {number} status The HTTP status code of the response.
- * @param {string} url The URL that was being requested.
- * @param {!XMLHttpRequest} xhr The XMLHttpRequest that failed.
- * @extends {_.Error}
- * @constructor
- */
- _.HttpError = function(status, url, xhr) {
- goog.base(this, 'Request Failed, status=' + status, url, xhr);
-
- /**
- * The HTTP status code for the error.
- * @type {number}
- */
- this.status = status;
- };
- goog.inherits(_.HttpError, _.Error);
-
-
-
- /**
- * Class for Timeout errors.
- *
- * @param {string} url The URL that timed out.
- * @param {!XMLHttpRequest} xhr The XMLHttpRequest that failed.
- * @extends {_.Error}
- * @constructor
- */
- _.TimeoutError = function(url, xhr) {
- goog.base(this, 'Request timed out', url, xhr);
- };
- goog.inherits(_.TimeoutError, _.Error);
-
-});
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/net/.svn/text-base/xhr_test.html.svn-base b/contexts/data/lib/closure-library/closure/goog/labs/net/.svn/text-base/xhr_test.html.svn-base
deleted file mode 100644
index 601e02d..0000000
--- a/contexts/data/lib/closure-library/closure/goog/labs/net/.svn/text-base/xhr_test.html.svn-base
+++ /dev/null
@@ -1,424 +0,0 @@
-<!DOCTYPE html>
-<html>
-<!--
-Copyright 2011 The Closure Library Authors. All Rights Reserved.
-
-Use of this source code is governed by the Apache License, Version 2.0.
-See the COPYING file for details.
--->
-<!--
--->
-<head>
-<meta http-equiv="X-UA-Compatible" content="IE=edge">
-<title>Closure Unit Tests - goog.labs.net.xhr</title>
-<script src="../../base.js"></script>
-<script>
- goog.require('goog.labs.net.xhr');
- goog.require('goog.string');
- goog.require('goog.testing.AsyncTestCase');
- goog.require('goog.testing.MockClock');
- goog.require('goog.testing.jsunit');
- goog.require('goog.userAgent');
-</script>
-</head>
-<body>
-<script>
-
- function setupStubXMLHttpRequest() {
-
- mockClock = new goog.testing.MockClock(true);
-
- var stubXhr = {
- sent: false,
- aborted: false,
- status: 0,
- headers: {},
- open: function(method, url, async) {
- this.method = method;
- this.url = url;
- this.async = async;
- },
- setRequestHeader: function(key, value) {
- this.headers[key] = value;
- },
- overrideMimeType: function(mimeType) {
- this.mimeType = mimeType;
- },
- abort: function() {
- this.aborted = true;
- this.load(0);
- },
- send: function(data) {
- this.data = data;
- this.sent = true;
- },
- load: function(status) {
- this.status = status;
- this.readyState = 4;
- if (this.onreadystatechange) this.onreadystatechange();
- }
- };
-
- goog.net.XmlHttp = function() {
- return stubXhr;
- };
- for (var x in originalXmlHttp) {
- goog.net.XmlHttp[x] = originalXmlHttp[x];
- }
-
- return stubXhr;
- }
-
-
- var xhr = goog.labs.net.xhr;
- var originalXmlHttp = goog.net.XmlHttp;
- var mockClock;
-
- var testCase = new goog.testing.AsyncTestCase(document.title);
- testCase.stepTimeout = 5 * 1000;
-
- testCase.setUpPage = function() {
- this.autoDiscoverTests();
- };
-
- testCase.tearDown = function() {
- if (mockClock) {
- mockClock.dispose();
- mockClock = null;
- }
- goog.net.XmlHttp = originalXmlHttp;
- };
-
- G_testRunner.initialize(testCase);
-
- // Many tests don't work on the local file system due to cross-origin
- // restrictions in Chrome without --allow-file-access-from-files.
- // They will run on the farm or on a Closure Test server.
- var shouldRunLocally = goog.userAgent.IE || goog.userAgent.GECKO ||
- goog.string.startsWith(document.location.href, 'file://');
-
- function testSimpleRequest() {
- if (shouldRunLocally) return;
-
- testCase.waitForAsync('simpleRequest');
- xhr.send('GET', 'testdata/xhr_test_text.data').
- addCallback(function(xhr) {
- assertEquals('Just some data.', xhr.responseText);
- assertEquals(200, xhr.status);
- testCase.continueTesting();
- }).
- addErrback(fail);
- }
-
- function testGetText() {
- if (shouldRunLocally) return;
-
- testCase.waitForAsync('getText');
- xhr.get('testdata/xhr_test_text.data').
- addCallback(function(content) {
- assertEquals('Just some data.', content);
- testCase.continueTesting();
- }).
- addErrback(fail);
- }
-
- function testGetTextWithJson() {
- if (shouldRunLocally) return;
-
- testCase.waitForAsync('getTextWithJson');
- xhr.get('testdata/xhr_test_json.data').
- addCallback(function(content) {
- assertEquals('while(1);\n{"stat":"ok","count":12345}\n', content);
- testCase.continueTesting();
- }).
- addErrback(fail);
- }
-
- function testPostText() {
- if (shouldRunLocally) return;
-
- testCase.waitForAsync('postText');
- xhr.post('testdata/xhr_test_text.data', 'post-data').
- addCallback(function(content) {
- // No good way to test post-data gets transported.
- assertEquals('Just some data.', content);
- testCase.continueTesting();
- }).
- addErrback(fail);
- }
-
- function testGetJson() {
- if (shouldRunLocally) return;
-
- testCase.waitForAsync('getJson');
- xhr.getJson('testdata/xhr_test_json.data', {xssiPrefix: 'while(1);\n'}).
- addCallback(function(content) {
- assertEquals('ok', content['stat']);
- assertEquals(12345, content['count']);
- testCase.continueTesting();
- }).
- addErrback(fail);
- }
-
- function testSerialRequests() {
- if (shouldRunLocally) return;
-
- var d = xhr.get('testdata/xhr_test_text.data').
- addCallback(function(txt) {
- return xhr.getJson(
- 'testdata/xhr_test_json.data', {xssiPrefix: 'while(1);\n'});
- });
-
- // Data that comes through to callbacks should be from the 2nd request.
- d.addCallback(function(json) {
- assertEquals('ok', json['stat']);
- assertEquals(12345, json['count']);
- testCase.continueTesting();
- });
- d.addErrback(fail);
- }
-
- function testParallelRequest() {
- if (shouldRunLocally) return;
-
- var textData, jsonData;
- function loadText(response) {
- textData = response;
- }
- function loadJson(response) {
- jsonData = response;
- }
-
- var d = goog.async.Deferred.succeed();
- d.awaitDeferred(
- xhr.get('testdata/xhr_test_text.data').addCallback(loadText));
- d.awaitDeferred(
- xhr.getJson('testdata/xhr_test_json.data',
- {xssiPrefix: 'while(1);\n'}).addCallback(loadJson));
- d.addCallback(function() {
- assertEquals('Just some data.', textData);
- assertEquals('ok', jsonData['stat']);
- testCase.continueTesting();
- });
- d.addErrback(fail);
- }
-
- function testBadUrlDetectedAsError() {
- if (shouldRunLocally) return;
-
- testCase.waitForAsync('badUrl');
- xhr.getJson('unknown-file.dat').
- addCallback(fail).
- addErrback(function(err) {
- assertTrue('Error should be an HTTP error',
- err instanceof xhr.HttpError);
- assertEquals(404, err.status);
- assertNotNull(err.xhr);
- testCase.continueTesting();
- });
- }
-
- function testBadOriginTriggersOnErrorHandler() {
- testCase.waitForAsync('badOrigin');
- xhr.get('http://www.google.com').
- addCallback(fail).
- addErrback(function(err) {
- // In IE this will be a goog.labs.net.xhr.Error since it is thrown
- // when calling xhr.open(), other browsers will raise an HttpError.
- assertTrue('Error should be an xhr error',
- err instanceof xhr.Error);
- assertNotNull(err.xhr);
- testCase.continueTesting();
- });
- }
-
- function testAbortRequest() {
- if (shouldRunLocally) return;
-
- var err;
- var d = xhr.send('GET', 'test-url', null).
- addCallback(fail).
- addErrback(function(e) {
- err = e;
- });
- d.cancel();
- assertTrue(err instanceof goog.async.Deferred.CancelledError);
- }
-
- //============================================================================
- // The following tests are synchronous and use a stubbed out XMLHttpRequest.
- //============================================================================
-
- function testSendNoOptions() {
- var stubXhr = setupStubXMLHttpRequest();
- var called = false;
- xhr.send('GET', 'test-url', null).
- addCallback(function(xhr) {
- called = true;
- assertEquals('Objects should be equal', xhr, stubXhr);
- }).
- addErrback(fail);
-
- assertTrue('XHR should have been sent', stubXhr.sent);
- assertFalse('Callback should not yet have been called', called);
-
- stubXhr.load(200);
-
- assertTrue('Callback should have been called', called);
-
- assertEquals('GET', stubXhr.method);
- assertEquals('test-url', stubXhr.url);
- }
-
- function testSendPostSetsDefaultHeader() {
- var stubXhr = setupStubXMLHttpRequest();
- xhr.send('POST', 'test-url', null).addErrback(fail);
-
- stubXhr.load(200);
-
- assertEquals('POST', stubXhr.method);
- assertEquals('test-url', stubXhr.url);
- assertEquals('application/x-www-form-urlencoded;charset=utf-8',
- stubXhr.headers['Content-Type']);
- }
-
- function testSendPostHeaders() {
- var stubXhr = setupStubXMLHttpRequest();
- xhr.send('POST', 'test-url', null, {
- headers: {'Content-Type': 'text/plain', 'X-Made-Up': 'FooBar'}
- }).addErrback(fail);
-
- stubXhr.load(200);
-
- assertEquals('POST', stubXhr.method);
- assertEquals('test-url', stubXhr.url);
- assertEquals('text/plain', stubXhr.headers['Content-Type']);
- assertEquals('FooBar', stubXhr.headers['X-Made-Up']);
- }
-
- function testSendWithCredentials() {
- var stubXhr = setupStubXMLHttpRequest();
- xhr.send('POST', 'test-url', null, {withCredentials: true}).
- addErrback(fail);
- stubXhr.load(200);
- assertTrue('XHR should have been sent', stubXhr.sent);
- assertTrue(stubXhr.withCredentials);
- }
-
- function testSendWithMimeType() {
- var stubXhr = setupStubXMLHttpRequest();
- xhr.send('POST', 'test-url', null, {mimeType: 'text/plain'}).
- addErrback(fail);
-
- stubXhr.load(200);
- assertTrue('XHR should have been sent', stubXhr.sent);
- assertEquals('text/plain', stubXhr.mimeType);
- }
-
- function testSendWithHttpError() {
- var stubXhr = setupStubXMLHttpRequest();
- var err;
- xhr.send('POST', 'test-url', null).
- addCallback(fail).
- addErrback(function(e) { err = e; });
-
- stubXhr.load(500);
-
- assertTrue('XHR should have been sent', stubXhr.sent);
- assertTrue(err instanceof xhr.HttpError);
- assertEquals(500, err.status);
- }
-
- function testSendWithTimeoutNotHit() {
- // TODO(user): This test fails in safari if it is run as part of a batch
- // but passes when run on its own. Something strange is going on to do
- // with the references to window.clearTimeout inside onreadystatechange and
- // the mockclock overrides.
- if (goog.userAgent.SAFARI) return;
-
- var stubXhr = setupStubXMLHttpRequest();
- var err;
- xhr.send('POST', 'test-url', null, {timeoutMs: 1500}).addErrback(fail);
- assertTrue(mockClock.getTimeoutsMade() > 0);
- mockClock.tick(1400);
- stubXhr.load(200);
- mockClock.tick(200);
- assertTrue('XHR should have been sent', stubXhr.sent);
- assertFalse('XHR should not have been aborted', stubXhr.aborted);
- }
-
- function testSendWithTimeoutHit() {
- var stubXhr = setupStubXMLHttpRequest();
- var err;
- xhr.send('POST', 'test-url', null, {timeoutMs: 50})
- .addCallback(fail)
- .addErrback(function(e) { err = e; });
- assertTrue(mockClock.getTimeoutsMade() > 0);
- mockClock.tick(50);
- assertTrue('XHR should have been sent', stubXhr.sent);
- assertTrue('XHR should have been aborted', stubXhr.aborted);
- assertTrue(err instanceof xhr.TimeoutError);
- }
-
- function testSendWithTimeoutWithoutDeferreds() {
- var stubXhr = setupStubXMLHttpRequest();
- var err;
- xhr.makeRequest('POST', 'test-url', null, {timeoutMs: 50}, fail,
- function(e) { err = e; });
- assertTrue(mockClock.getTimeoutsMade() > 0);
- mockClock.tick(50);
- assertTrue('XHR should have been sent', stubXhr.sent);
- assertTrue('XHR should have been aborted', stubXhr.aborted);
- assertTrue(err instanceof xhr.TimeoutError);
- }
-
- function testCancelRequest() {
- var stubXhr = setupStubXMLHttpRequest();
- var err;
- var d = xhr.send('GET', 'test-url', null, {timeoutMs: 50}).
- addCallback(fail).
- addErrback(function(e) {
- err = e;
- });
- d.cancel();
- stubXhr.load(0); // Call load anyway, shoudn't make a difference.
- mockClock.tick(100); // Timeout should never be called.
-
- assertTrue('XHR should have been sent', stubXhr.sent);
- assertTrue('XHR should have been aborted', stubXhr.aborted);
- assertTrue(err instanceof goog.async.Deferred.CancelledError);
- }
-
- function testGetJson() {
- var stubXhr = setupStubXMLHttpRequest();
- var responseData;
- xhr.getJson('test-url').
- addCallback(function(data) {
- responseData = data;
- }).
- addErrback(fail);
-
- stubXhr.responseText = '{"a": 1, "b": 2}';
- stubXhr.load(200);
-
- assertObjectEquals({a: 1, b: 2}, responseData);
- }
-
- function testGetJsonWithXssiPrefix() {
- var stubXhr = setupStubXMLHttpRequest();
- var responseData;
- xhr.getJson('test-url', {xssiPrefix: 'while(1);\n'}).
- addCallback(function(data) {
- responseData = data;
- }).
- addErrback(fail);
-
- stubXhr.responseText = 'while(1);\n{"a": 1, "b": 2}';
- stubXhr.load(200);
-
- assertObjectEquals({a: 1, b: 2}, responseData);
- }
-
-</script>
-</body>
-</html>
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/net/image.js b/contexts/data/lib/closure-library/closure/goog/labs/net/image.js
new file mode 100644
index 0000000..1f1973a
--- /dev/null
+++ b/contexts/data/lib/closure-library/closure/goog/labs/net/image.js
@@ -0,0 +1,93 @@
+// 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 Simple image loader, used for preloading.
+ * @author nnaze@google.com (Nathan Naze)
+ */
+
+goog.provide('goog.labs.net.image');
+
+goog.require('goog.events.EventHandler');
+goog.require('goog.events.EventType');
+goog.require('goog.net.EventType');
+goog.require('goog.result.SimpleResult');
+goog.require('goog.userAgent');
+
+
+/**
+ * Loads a single image. Useful for preloading images. May be combined with
+ * goog.result.combine to preload many images.
+ *
+ * @param {string} uri URI of the image.
+ * @param {(Image|function(): !Image)=} opt_image If present, instead of
+ * creating a new Image instance the function will use the passed Image
+ * instance or the result of calling the Image factory respectively. This
+ * can be used to control exactly how Image instances are created, for
+ * example if they should be created in a particular document element, or
+ * have fields that will trigger CORS image fetches.
+ * @return {!goog.result.Result} An asyncronous result that will succeed
+ * if the image successfully loads or error if the image load fails.
+ */
+goog.labs.net.image.load = function(uri, opt_image) {
+ var image;
+ if (!goog.isDef(opt_image)) {
+ image = new Image();
+ } else if (goog.isFunction(opt_image)) {
+ image = opt_image();
+ } else {
+ image = opt_image;
+ }
+
+ // IE's load event on images can be buggy. Instead, we wait for
+ // readystatechange events and check if readyState is 'complete'.
+ // See:
+ // http://msdn.microsoft.com/en-us/library/ie/ms536957(v=vs.85).aspx
+ // http://msdn.microsoft.com/en-us/library/ie/ms534359(v=vs.85).aspx
+ var loadEvent = goog.userAgent.IE ? goog.net.EventType.READY_STATE_CHANGE :
+ goog.events.EventType.LOAD;
+
+ var result = new goog.result.SimpleResult();
+
+ var handler = new goog.events.EventHandler();
+ handler.listen(
+ image,
+ [loadEvent, goog.net.EventType.ABORT, goog.net.EventType.ERROR],
+ function(e) {
+
+ // We only registered listeners for READY_STATE_CHANGE for IE.
+ // If readyState is now COMPLETE, the image has loaded.
+ // See related comment above.
+ if (e.type == goog.net.EventType.READY_STATE_CHANGE &&
+ image.readyState != goog.net.EventType.COMPLETE) {
+ return;
+ }
+
+ // At this point, we know whether the image load was successful
+ // and no longer care about image events.
+ goog.dispose(handler);
+
+ // Whether the image successfully loaded.
+ if (e.type == loadEvent) {
+ result.setValue(image);
+ } else {
+ result.setError();
+ }
+ });
+
+ // Initiate the image request.
+ image.src = uri;
+
+ return result;
+};
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/net/image_test.html b/contexts/data/lib/closure-library/closure/goog/labs/net/image_test.html
new file mode 100644
index 0000000..32d529a
--- /dev/null
+++ b/contexts/data/lib/closure-library/closure/goog/labs/net/image_test.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<!--
+Copyright 2012 The Closure Library Authors. All Rights Reserved.
+
+Use of this source code is governed by the Apache License, Version 2.0.
+See the COPYING file for details.
+-->
+<!--
+ Author: nnaze@google.com (Nathan Naze)
+-->
+<head>
+<title>Closure Unit Tests - goog.labs.net.image</title>
+<script src="../../base.js"></script>
+</head>
+<body>
+<script>
+goog.require('goog.labs.net.imageTest');
+</script>
+</body>
+</html>
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/net/image_test.js b/contexts/data/lib/closure-library/closure/goog/labs/net/image_test.js
new file mode 100644
index 0000000..57ad109
--- /dev/null
+++ b/contexts/data/lib/closure-library/closure/goog/labs/net/image_test.js
@@ -0,0 +1,128 @@
+// 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 Unit tests for goog.labs.net.Image.
+ *
+ * @author nnaze@google.com (Nathan Naze)
+ */
+
+
+/** @suppress {extraRequire} */
+goog.provide('goog.labs.net.imageTest');
+
+goog.require('goog.events');
+goog.require('goog.labs.net.image');
+goog.require('goog.result');
+goog.require('goog.result.Result');
+goog.require('goog.string');
+goog.require('goog.testing.AsyncTestCase');
+goog.require('goog.testing.jsunit');
+goog.require('goog.testing.recordFunction');
+
+goog.setTestOnly('goog.labs.net.ImageTest');
+
+var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall();
+
+function testValidImage() {
+ var url = 'testdata/cleardot.gif';
+
+ asyncTestCase.waitForAsync('image load');
+
+ assertEquals(0, goog.events.getTotalListenerCount());
+
+ var result = goog.labs.net.image.load(url);
+
+ goog.result.waitOnSuccess(result, function(value) {
+
+ assertEquals(goog.result.Result.State.SUCCESS, result.getState());
+
+ assertEquals('IMG', value.tagName);
+ assertTrue(goog.string.endsWith(value.src, url));
+ assertUndefined(result.getError());
+
+ assertEquals('Listeners should have been cleaned up.',
+ 0, goog.events.getTotalListenerCount());
+
+ asyncTestCase.continueTesting();
+ });
+}
+
+function testInvalidImage() {
+
+ var url = 'testdata/invalid.gif'; // This file does not exist.
+
+ asyncTestCase.waitForAsync('image load');
+
+ assertEquals(0, goog.events.getTotalListenerCount());
+
+ var result = goog.labs.net.image.load(url);
+
+ goog.result.wait(result, function(result) {
+
+ assertEquals(goog.result.Result.State.ERROR, result.getState());
+ assertUndefined(result.getValue());
+ assertUndefined(result.getError());
+
+ assertEquals('Listeners should have been cleaned up.',
+ 0, goog.events.getTotalListenerCount());
+
+ asyncTestCase.continueTesting();
+ });
+}
+
+function testImageFactory() {
+ var returnedImage = new Image();
+ var factory = function() {
+ return returnedImage;
+ }
+ var countedFactory = goog.testing.recordFunction(factory);
+
+ var url = 'testdata/cleardot.gif';
+
+ asyncTestCase.waitForAsync('image load');
+ assertEquals(0, goog.events.getTotalListenerCount());
+ var result = goog.labs.net.image.load(url, countedFactory);
+
+ goog.result.waitOnSuccess(result, function(value) {
+ assertEquals(goog.result.Result.State.SUCCESS, result.getState());
+ assertEquals(returnedImage, value);
+ assertEquals(1, countedFactory.getCallCount());
+ assertUndefined(result.getError());
+
+ assertEquals('Listeners should have been cleaned up.',
+ 0, goog.events.getTotalListenerCount());
+ asyncTestCase.continueTesting();
+ });
+}
+
+function testExistingImage() {
+ var image = new Image();
+
+ var url = 'testdata/cleardot.gif';
+
+ asyncTestCase.waitForAsync('image load');
+ assertEquals(0, goog.events.getTotalListenerCount());
+ var result = goog.labs.net.image.load(url, image);
+
+ goog.result.waitOnSuccess(result, function(value) {
+ assertEquals(goog.result.Result.State.SUCCESS, result.getState());
+ assertEquals(image, value);
+ assertUndefined(result.getError());
+
+ assertEquals('Listeners should have been cleaned up.',
+ 0, goog.events.getTotalListenerCount());
+ asyncTestCase.continueTesting();
+ });
+}
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/net/testdata/cleardot.gif b/contexts/data/lib/closure-library/closure/goog/labs/net/testdata/cleardot.gif
new file mode 100644
index 0000000..1d11fa9
--- /dev/null
+++ b/contexts/data/lib/closure-library/closure/goog/labs/net/testdata/cleardot.gif
Binary files differ
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/net/testdata/xhr_test_json.data b/contexts/data/lib/closure-library/closure/goog/labs/net/testdata/xhr_test_json.data
new file mode 100644
index 0000000..44afb24
--- /dev/null
+++ b/contexts/data/lib/closure-library/closure/goog/labs/net/testdata/xhr_test_json.data
@@ -0,0 +1,2 @@
+while(1);
+{"stat":"ok","count":12345}
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/net/testdata/xhr_test_text.data b/contexts/data/lib/closure-library/closure/goog/labs/net/testdata/xhr_test_text.data
new file mode 100644
index 0000000..b7f2f0e
--- /dev/null
+++ b/contexts/data/lib/closure-library/closure/goog/labs/net/testdata/xhr_test_text.data
@@ -0,0 +1 @@
+Just some data. \ No newline at end of file
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/net/xhr.js b/contexts/data/lib/closure-library/closure/goog/labs/net/xhr.js
index e2135d2..224ec74 100644
--- a/contexts/data/lib/closure-library/closure/goog/labs/net/xhr.js
+++ b/contexts/data/lib/closure-library/closure/goog/labs/net/xhr.js
@@ -16,7 +16,7 @@
/**
* @fileoverview Offered as an alternative to XhrIo as a way for making requests
* via XMLHttpRequest. Instead of mirroring the XHR interface and exposing
- * events, deferreds are used as a way to pass a "promise" of the response to
+ * events, results are used as a way to pass a "promise" of the response to
* interested parties.
*
*/
@@ -26,398 +26,419 @@ goog.provide('goog.labs.net.xhr.Error');
goog.provide('goog.labs.net.xhr.HttpError');
goog.provide('goog.labs.net.xhr.TimeoutError');
-goog.require('goog.async.Deferred');
goog.require('goog.debug.Error');
goog.require('goog.json');
goog.require('goog.net.HttpStatus');
goog.require('goog.net.XmlHttp');
+goog.require('goog.result');
goog.require('goog.string');
goog.require('goog.uri.utils');
goog.scope(function() {
- var _ = goog.labs.net.xhr;
- var Deferred = goog.async.Deferred;
- var HttpStatus = goog.net.HttpStatus;
+var _ = goog.labs.net.xhr;
+var Result = goog.result.Result;
+var SimpleResult = goog.result.SimpleResult;
+var Wait = goog.result.wait;
+var HttpStatus = goog.net.HttpStatus;
- /**
- * Configuration options for an XMLHttpRequest.
- * - headers: map of header key/value pairs.
- * - timeoutMs: number of milliseconds after which the request will be timed
- * out by the client. Default is to allow the browser to handle timeouts.
- * - withCredentials: whether user credentials are to be included in a
- * cross-origin request. See:
- * http://dev.w3.org/2006/webapi/XMLHttpRequest-2/#the-withcredentials-attribute
- * - mimeType: allows the caller to override the content-type and charset for
- * the request, which is useful when requesting binary data. See:
- * http://dev.w3.org/2006/webapi/XMLHttpRequest-2/#dom-xmlhttprequest-overridemimetype
- * - xssiPrefix: Prefix used for protecting against XSSI attacks, which should
- * be removed before parsing the response as JSON.
- *
- * @typedef {{
- * headers: (Object.<string>|undefined),
- * timeoutMs: (number|undefined),
- * withCredentials: (boolean|undefined),
- * mimeType: (string|undefined),
- * xssiPrefix: (string|undefined)
- * }}
- */
- _.Options;
-
-
- /**
- * Defines the types that are allowed as post data.
- * @typedef {(ArrayBuffer|Blob|Document|FormData|null|string|undefined)}
- */
- _.PostData;
+/**
+ * Configuration options for an XMLHttpRequest.
+ * - headers: map of header key/value pairs.
+ * - timeoutMs: number of milliseconds after which the request will be timed
+ * out by the client. Default is to allow the browser to handle timeouts.
+ * - withCredentials: whether user credentials are to be included in a
+ * cross-origin request. See:
+ * http://dev.w3.org/2006/webapi/XMLHttpRequest-2/#the-withcredentials-attribute
+ * - mimeType: allows the caller to override the content-type and charset for
+ * the request, which is useful when requesting binary data. See:
+ * http://dev.w3.org/2006/webapi/XMLHttpRequest-2/#dom-xmlhttprequest-overridemimetype
+ * - xssiPrefix: Prefix used for protecting against XSSI attacks, which should
+ * be removed before parsing the response as JSON.
+ *
+ * @typedef {{
+ * headers: (Object.<string>|undefined),
+ * timeoutMs: (number|undefined),
+ * withCredentials: (boolean|undefined),
+ * mimeType: (string|undefined),
+ * xssiPrefix: (string|undefined)
+ * }}
+ */
+_.Options;
- /**
- * The Content-Type HTTP header name.
- * @type {string}
- */
- _.CONTENT_TYPE_HEADER = 'Content-Type';
+/**
+ * Defines the types that are allowed as post data.
+ * @typedef {(ArrayBuffer|Blob|Document|FormData|null|string|undefined)}
+ */
+_.PostData;
- /**
- * The Content-Type HTTP header value for a url-encoded form.
- * @type {string}
- */
- _.FORM_CONTENT_TYPE = 'application/x-www-form-urlencoded;charset=utf-8';
+/**
+ * The Content-Type HTTP header name.
+ * @type {string}
+ */
+_.CONTENT_TYPE_HEADER = 'Content-Type';
- /**
- * Sends a get request, returning a deferred which will be called back with
- * the response text once the request completes.
- *
- * @param {string} url The URL to request.
- * @param {_.Options=} opt_options Configuration options for the request.
- * @return {!Deferred} A deferred object that will be called back
- * with the response text once the request finishes.
- */
- _.get = function(url, opt_options) {
- return _.send('GET', url, null, opt_options).
- addCallback(_.getResponseText_);
- };
+/**
+ * The Content-Type HTTP header value for a url-encoded form.
+ * @type {string}
+ */
+_.FORM_CONTENT_TYPE = 'application/x-www-form-urlencoded;charset=utf-8';
- /**
- * Sends a post request, returning a deferred which will be called back with
- * the response text once the request completes.
- *
- * @param {string} url The URL to request.
- * @param {_.PostData} data The body of the post request.
- * @param {_.Options=} opt_options Configuration options for the request.
- * @return {!Deferred} A deferred object that will be called back
- * with the response text once the request finishes.
- */
- _.post = function(url, data, opt_options) {
- return _.send('POST', url, data, opt_options).
- addCallback(_.getResponseText_);
- };
+/**
+ * Sends a get request, returning a transformed result which will be resolved
+ * with the response text once the request completes.
+ *
+ * @param {string} url The URL to request.
+ * @param {_.Options=} opt_options Configuration options for the request.
+ * @return {!Result} A result object that will be resolved
+ * with the response text once the request finishes.
+ */
+_.get = function(url, opt_options) {
+ var result = _.send('GET', url, null, opt_options);
+ var transformedResult = goog.result.transform(result,
+ _.getResponseText_);
+ return transformedResult;
+};
- /**
- * Sends a get request, returning a deferred which will be called back with
- * the parsed response text once the request completes.
- *
- * @param {string} url The URL to request.
- * @param {_.Options=} opt_options Configuration options for the request.
- * @return {!Deferred} A deferred object that will be called back
- * with the response JSON once the request finishes.
- */
- _.getJson = function(url, opt_options) {
- var d = _.send('GET', url, null, opt_options);
- _.addJsonParsingCallbacks_(d, opt_options);
- return d;
- };
+/**
+ * Sends a post request, returning a transformed result which will be resolved
+ * with the response text once the request completes.
+ *
+ * @param {string} url The URL to request.
+ * @param {_.PostData} data The body of the post request.
+ * @param {_.Options=} opt_options Configuration options for the request.
+ * @return {!Result} A result object that will be resolved
+ * with the response text once the request finishes.
+ */
+_.post = function(url, data, opt_options) {
+ var result = _.send('POST', url, data, opt_options);
+ var transformedResult = goog.result.transform(result,
+ _.getResponseText_);
+ return transformedResult;
+};
- /**
- * Sends a post request, returning a deferred which will be called back with
- * the parsed response text once the request completes.
- *
- * @param {string} url The URL to request.
- * @param {_.PostData} data The body of the post request.
- * @param {_.Options=} opt_options Configuration options for the request.
- * @return {!Deferred} A deferred object that will be called back
- * with the response JSON once the request finishes.
- */
- _.postJson = function(url, data, opt_options) {
- var d = _.send('POST', url, data, opt_options);
- _.addJsonParsingCallbacks_(d, opt_options);
- return d;
- };
+/**
+ * Sends a get request, returning a result which will be resolved with
+ * the parsed response text once the request completes.
+ *
+ * @param {string} url The URL to request.
+ * @param {_.Options=} opt_options Configuration options for the request.
+ * @return {!Result} A result object that will be resolved
+ * with the response JSON once the request finishes.
+ */
+_.getJson = function(url, opt_options) {
+ var result = _.send('GET', url, null, opt_options);
+ var transformedResult = _.addJsonParsingCallbacks_(result, opt_options);
+ return transformedResult;
+};
- /**
- * Sends a request using XMLHttpRequest and returns a deferred.
- *
- * @param {string} method The HTTP method for the request.
- * @param {string} url The URL to request.
- * @param {_.PostData} data The body of the post request.
- * @param {_.Options=} opt_options Configuration options for the request.
- * @return {!Deferred} A deferred object that will be called back
- * with the XHR object when the request finishes.
- */
- _.send = function(method, url, data, opt_options) {
+/**
+ * Sends a post request, returning a result which will be resolved with
+ * the parsed response text once the request completes.
+ *
+ * @param {string} url The URL to request.
+ * @param {_.PostData} data The body of the post request.
+ * @param {_.Options=} opt_options Configuration options for the request.
+ * @return {!Result} A result object that will be resolved
+ * with the response JSON once the request finishes.
+ */
+_.postJson = function(url, data, opt_options) {
+ var result = _.send('POST', url, data, opt_options);
+ var transformedResult = _.addJsonParsingCallbacks_(result, opt_options);
+ return transformedResult;
+};
- // When the deferred is cancelled, we abort the XHR. We want to make sure
- // the readystatechange event still fires, so it can do the timeout
- // cleanup, however we don't want the callback or errback to be called
- // again. Thus the slight ugliness here. If deferreds were pushed into
- // makeRequest, this could become a lot cleaner but we want an option for
- // people not to include goog.async.Deferred.
- function cancel() {
- cancelled = true;
+/**
+ * Sends a request using XMLHttpRequest and returns a result.
+ *
+ * @param {string} method The HTTP method for the request.
+ * @param {string} url The URL to request.
+ * @param {_.PostData} data The body of the post request.
+ * @param {_.Options=} opt_options Configuration options for the request.
+ * @return {!Result} A result object that will be resolved
+ * with the XHR object as it's value when the request finishes.
+ */
+_.send = function(method, url, data, opt_options) {
+
+ var result = new SimpleResult();
+
+ // When the deferred is cancelled, we abort the XHR. We want to make sure
+ // the readystatechange event still fires, so it can do the timeout
+ // cleanup, however we don't want the callback or errback to be called
+ // again. Thus the slight ugliness here. If results were pushed into
+ // makeRequest, this could become a lot cleaner but we want an option for
+ // people not to include goog.result.Result.
+ goog.result.waitOnError(result, function(result) {
+ if (result.isCanceled()) {
xhr.abort();
xhr.onreadystatechange = goog.nullFunction;
- // TODO(user): We could return an AbortedError if we wanted to.
}
+ });
- function callback(data) {
- if (!cancelled) {
- d.callback(data);
- }
- }
+ function callback(data) {
+ result.setValue(data);
+ }
+
+ function errback(err) {
+ result.setError(err);
+ }
- function errback(err) {
- if (!cancelled) {
- d.errback(err);
+ var xhr = _.makeRequest(method, url, data, opt_options, callback, errback);
+
+ return result;
+};
+
+
+/**
+ * Creates a new XMLHttpRequest and initiates a request.
+ *
+ * @param {string} method The HTTP method for the request.
+ * @param {string} url The URL to request.
+ * @param {_.PostData} data The body of the post request, unless the content
+ * type is explicitly set in the Options, then it will default to form
+ * urlencoded.
+ * @param {_.Options=} opt_options Configuration options for the request.
+ * @param {function(XMLHttpRequest)=} opt_callback Optional callback to call
+ * when the request completes.
+ * @param {function(Error)=} opt_errback Optional callback to call
+ * when there is an error.
+ * @return {!XMLHttpRequest} The new XMLHttpRequest.
+ */
+_.makeRequest = function(
+ method, url, data, opt_options, opt_callback, opt_errback) {
+ var options = opt_options || {};
+ var callback = opt_callback || goog.nullFunction;
+ var errback = opt_errback || goog.nullFunction;
+ var timer;
+
+ var xhr = /** @type {!XMLHttpRequest} */ (goog.net.XmlHttp());
+ try {
+ xhr.open(method, url, true);
+ } catch (e) {
+ // XMLHttpRequest.open may throw when 'open' is called, for example, IE7
+ // throws "Access Denied" for cross-origin requests.
+ errback(new _.Error('Error opening XHR: ' + e.message, url, xhr));
+ return xhr;
+ }
+
+ // So sad that IE doesn't support onload and onerror.
+ xhr.onreadystatechange = function() {
+ if (xhr.readyState == goog.net.XmlHttp.ReadyState.COMPLETE) {
+ window.clearTimeout(timer);
+ if (HttpStatus.isSuccess(xhr.status) ||
+ xhr.status === 0 && !_.isEffectiveSchemeHttp_(url)) {
+ callback(xhr);
+ } else {
+ errback(new _.HttpError(xhr.status, url, xhr));
}
}
+ };
- var cancelled = false;
- var d = new Deferred(cancel);
- var xhr = _.makeRequest(method, url, data, opt_options, callback, errback);
+ // Set the headers.
+ var contentTypeIsSet = false;
+ if (options.headers) {
+ for (var key in options.headers) {
+ xhr.setRequestHeader(key, options.headers[key]);
+ }
+ contentTypeIsSet = _.CONTENT_TYPE_HEADER in options.headers;
+ }
+
+ // If a content type hasn't been set, default to form-urlencoded/UTF8 for
+ // POSTs. This is because some proxies have been known to reject posts
+ // without a content-type.
+ if (method == 'POST' && !contentTypeIsSet) {
+ xhr.setRequestHeader(_.CONTENT_TYPE_HEADER, _.FORM_CONTENT_TYPE);
+ }
+
+ // Set whether to pass cookies on cross-domain requests (if applicable).
+ // @see http://dev.w3.org/2006/webapi/XMLHttpRequest-2/#the-withcredentials-attribute
+ if (options.withCredentials) {
+ xhr.withCredentials = options.withCredentials;
+ }
+
+ // Allow the request to override the mime type, useful for getting binary
+ // data from the server. e.g. 'text/plain; charset=x-user-defined'.
+ // @see http://dev.w3.org/2006/webapi/XMLHttpRequest-2/#dom-xmlhttprequest-overridemimetype
+ if (options.mimeType) {
+ xhr.overrideMimeType(options.mimeType);
+ }
+
+ // Handle timeouts, if requested.
+ if (options.timeoutMs > 0) {
+ timer = window.setTimeout(function() {
+ // Clear event listener before aborting so the errback will not be
+ // called twice.
+ xhr.onreadystatechange = goog.nullFunction;
+ xhr.abort();
+ errback(new _.TimeoutError(url, xhr));
+ }, options.timeoutMs);
+ }
- return d;
- };
+ // Trigger the send.
+ try {
+ xhr.send(data);
+ } catch (e) {
+ // XMLHttpRequest.send is known to throw on some versions of FF, for example
+ // if a cross-origin request is disallowed.
+ errback(new _.Error('Error sending XHR: ' + e.message, url, xhr));
+ }
+ return xhr;
+};
- /**
- * Creates a new XMLHttpRequest and initiates a request.
- *
- * @param {string} method The HTTP method for the request.
- * @param {string} url The URL to request.
- * @param {_.PostData} data The body of the post request, unless the content
- * type is explicitly set in the Options, then it will default to form
- * urlencoded.
- * @param {_.Options=} opt_options Configuration options for the request.
- * @param {function(XMLHttpRequest)=} opt_callback Optional callback to call
- * when the request completes.
- * @param {function(Error)=} opt_errback Optional callback to call
- * when there is an error.
- * @return {!XMLHttpRequest} The new XMLHttpRequest.
- */
- _.makeRequest = function(
- method, url, data, opt_options, opt_callback, opt_errback) {
- var options = opt_options || {};
- var callback = opt_callback || goog.nullFunction;
- var errback = opt_errback || goog.nullFunction;
- var timer;
-
- var xhr = /** @type {!XMLHttpRequest} */ (goog.net.XmlHttp());
- try {
- xhr.open(method, url, true);
- } catch (e) {
- // XMLHttpRequest.open may throw when 'open' is called, for example, IE7
- // throws "Access Denied" for cross-origin requests.
- errback(new _.Error('Error opening XHR: ' + e.message, url, xhr));
- return xhr;
- }
- // So sad that IE doesn't support onload and onerror.
- xhr.onreadystatechange = function() {
- if (xhr.readyState == goog.net.XmlHttp.ReadyState.COMPLETE) {
- window.clearTimeout(timer);
- if (HttpStatus.isSuccess(xhr.status) ||
- xhr.status === 0 && !_.isEffectiveSchemeHttp_(url)) {
- callback(xhr);
- } else {
- errback(new _.HttpError(xhr.status, url, xhr));
- }
- }
- };
+/**
+ * @param {string} url The URL to test.
+ * @return {boolean} Whether the effective scheme is HTTP or HTTPs.
+ * @private
+ */
+_.isEffectiveSchemeHttp_ = function(url) {
+ var scheme = goog.uri.utils.getEffectiveScheme(url);
+ // NOTE(user): Empty-string is for the case under FF3.5 when the location
+ // is not defined inside a web worker.
+ return scheme == 'http' || scheme == 'https' || scheme == '';
+};
- // Set the headers.
- var contentTypeIsSet = false;
- if (options.headers) {
- for (var key in options.headers) {
- xhr.setRequestHeader(key, options.headers[key]);
- }
- contentTypeIsSet = _.CONTENT_TYPE_HEADER in options.headers;
- }
- // If a content type hasn't been set, default to form-urlencoded/UTF8 for
- // POSTs. This is because some proxies have been known to reject posts
- // without a content-type.
- if (method == 'POST' && !contentTypeIsSet) {
- xhr.setRequestHeader(_.CONTENT_TYPE_HEADER, _.FORM_CONTENT_TYPE);
- }
+/**
+ * Returns the response text of an XHR object. Intended to be called when
+ * the result resolves.
+ *
+ * @param {!XMLHttpRequest} xhr The XHR object.
+ * @return {string} The response text.
+ * @private
+ */
+_.getResponseText_ = function(xhr) {
+ return xhr.responseText;
+};
- // Set whether to pass cookies on cross-domain requests (if applicable).
- // @see http://dev.w3.org/2006/webapi/XMLHttpRequest-2/#the-withcredentials-attribute
- if (options.withCredentials) {
- xhr.withCredentials = options.withCredentials;
- }
- // Allow the request to override the mime type, useful for getting binary
- // data from the server. e.g. 'text/plain; charset=x-user-defined'.
- // @see http://dev.w3.org/2006/webapi/XMLHttpRequest-2/#dom-xmlhttprequest-overridemimetype
- if (options.mimeType) {
- xhr.overrideMimeType(options.mimeType);
- }
+/**
+ * Transforms a result, parsing the JSON in the original result value's
+ * responseText. The transformed result's value is a javascript object.
+ * Parse errors resolve the transformed result in an error.
+ *
+ * @param {!Result} result The result to wait on.
+ * @param {_.Options|undefined} options The options object.
+ *
+ * @return {!Result} The transformed result.
+ * @private
+ */
+_.addJsonParsingCallbacks_ = function(result, options) {
+ var resultWithResponseText = goog.result.transform(result,
+ _.getResponseText_);
+ var prefixStrippedResult = resultWithResponseText;
+ if (options && options.xssiPrefix) {
+ prefixStrippedResult = goog.result.transform(resultWithResponseText,
+ goog.partial(_.stripXssiPrefix_, options.xssiPrefix));
+ }
- // Handle timeouts, if requested.
- if (options.timeoutMs > 0) {
- timer = window.setTimeout(function() {
- // Clear event listener before aborting so the errback will not be
- // called twice.
- xhr.onreadystatechange = goog.nullFunction;
- xhr.abort();
- errback(new _.TimeoutError(url, xhr));
- }, options.timeoutMs);
- }
+ var jsonParsedResult = goog.result.transform(prefixStrippedResult,
+ goog.json.parse);
+ return jsonParsedResult;
+};
- // Trigger the send.
- try {
- xhr.send(data);
- } catch (e) {
- // XMLHttpRequest.send is known to throw on some versions of FF, for example
- // if a cross-origin request is disallowed.
- errback(new _.Error('Error sending XHR: ' + e.message, url, xhr));
- }
- return xhr;
- };
+/**
+ * Strips the XSSI prefix from the input string.
+ *
+ * @param {string} prefix The XSSI prefix.
+ * @param {string} string The string to strip the prefix from.
+ * @return {string} The input string without the prefix.
+ * @private
+ */
+_.stripXssiPrefix_ = function(prefix, string) {
+ if (goog.string.startsWith(string, prefix)) {
+ string = string.substring(prefix.length);
+ }
+ return string;
+};
- /**
- * @param {string} url The URL to test.
- * @return {boolean} Whether the effective scheme is HTTP or HTTPs.
- * @private
- */
- _.isEffectiveSchemeHttp_ = function(url) {
- var scheme = goog.uri.utils.getEffectiveScheme(url);
- // NOTE(user): Empty-string is for the case under FF3.5 when the location
- // is not defined inside a web worker.
- return scheme == 'http' || scheme == 'https' || scheme == '';
- };
+/**
+ * Generic error that may occur during a request.
+ *
+ * @param {string} message The error message.
+ * @param {string} url The URL that was being requested.
+ * @param {!XMLHttpRequest} xhr The XMLHttpRequest that failed.
+ * @extends {goog.debug.Error}
+ * @constructor
+ */
+_.Error = function(message, url, xhr) {
+ goog.base(this, message + ', url=' + url);
/**
- * Returns the response text of an XHR object. Intended for use as a callback
- * on a deferred.
- *
- * @param {!XMLHttpRequest} xhr The XHR object.
- * @return {string} The response text.
- * @private
+ * The URL that was requested.
+ * @type {string}
*/
- _.getResponseText_ = function(xhr) {
- return xhr.responseText;
- };
-
+ this.url = url;
/**
- * Adds the callbacks required for parsing a response as JSON. The chain will
- * expect an XMLHttpRequest and will result in a JS object. Parse errors will
- * trigger the errback chain.
- *
- * @param {!Deferred} d The deferred to add callbacks to.
- * @param {_.Options|undefined} options The options object.
- * @private
+ * The XMLHttpRequest corresponding with the failed request.
+ * @type {!XMLHttpRequest}
*/
- _.addJsonParsingCallbacks_ = function(d, options) {
- d.addCallback(_.getResponseText_);
- if (options && options.xssiPrefix) {
- d.addCallback(goog.partial(_.stripXssiPrefix_, options.xssiPrefix));
- }
- d.addCallback(goog.json.parse);
- };
+ this.xhr = xhr;
+};
+goog.inherits(_.Error, goog.debug.Error);
- /**
- * Strips the XSSI prefix from the input string.
- *
- * @param {string} prefix The XSSI prefix.
- * @param {string} string The string to strip the prefix from.
- * @return {string} The input string without the prefix.
- * @private
- */
- _.stripXssiPrefix_ = function(prefix, string) {
- if (goog.string.startsWith(string, prefix)) {
- string = string.substring(prefix.length);
- }
- return string;
- };
+/** @override */
+_.Error.prototype.name = 'XhrError';
+/**
+ * Class for HTTP errors.
+ *
+ * @param {number} status The HTTP status code of the response.
+ * @param {string} url The URL that was being requested.
+ * @param {!XMLHttpRequest} xhr The XMLHttpRequest that failed.
+ * @extends {_.Error}
+ * @constructor
+ */
+_.HttpError = function(status, url, xhr) {
+ goog.base(this, 'Request Failed, status=' + status, url, xhr);
+
/**
- * Generic error that may occur during a request.
- *
- * @param {string} message The error message.
- * @param {string} url The URL that was being requested.
- * @param {!XMLHttpRequest} xhr The XMLHttpRequest that failed.
- * @extends {goog.debug.Error}
- * @constructor
+ * The HTTP status code for the error.
+ * @type {number}
*/
- _.Error = function(message, url, xhr) {
- goog.base(this, message + ', url=' + url);
-
- /**
- * The URL that was requested.
- * @type {string}
- */
- this.url = url;
-
- /**
- * The XMLHttpRequest corresponding with the failed request.
- * @type {!XMLHttpRequest}
- */
- this.xhr = xhr;
- };
- goog.inherits(_.Error, goog.debug.Error);
+ this.status = status;
+};
+goog.inherits(_.HttpError, _.Error);
+/** @override */
+_.HttpError.prototype.name = 'XhrHttpError';
- /**
- * Class for HTTP errors.
- *
- * @param {number} status The HTTP status code of the response.
- * @param {string} url The URL that was being requested.
- * @param {!XMLHttpRequest} xhr The XMLHttpRequest that failed.
- * @extends {_.Error}
- * @constructor
- */
- _.HttpError = function(status, url, xhr) {
- goog.base(this, 'Request Failed, status=' + status, url, xhr);
-
- /**
- * The HTTP status code for the error.
- * @type {number}
- */
- this.status = status;
- };
- goog.inherits(_.HttpError, _.Error);
+/**
+ * Class for Timeout errors.
+ *
+ * @param {string} url The URL that timed out.
+ * @param {!XMLHttpRequest} xhr The XMLHttpRequest that failed.
+ * @extends {_.Error}
+ * @constructor
+ */
+_.TimeoutError = function(url, xhr) {
+ goog.base(this, 'Request timed out', url, xhr);
+};
+goog.inherits(_.TimeoutError, _.Error);
- /**
- * Class for Timeout errors.
- *
- * @param {string} url The URL that timed out.
- * @param {!XMLHttpRequest} xhr The XMLHttpRequest that failed.
- * @extends {_.Error}
- * @constructor
- */
- _.TimeoutError = function(url, xhr) {
- goog.base(this, 'Request timed out', url, xhr);
- };
- goog.inherits(_.TimeoutError, _.Error);
-});
+/** @override */
+_.TimeoutError.prototype.name = 'XhrTimeoutError';
+
+}); // goog.scope
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/net/xhr_test.html b/contexts/data/lib/closure-library/closure/goog/labs/net/xhr_test.html
index 601e02d..992a646 100644
--- a/contexts/data/lib/closure-library/closure/goog/labs/net/xhr_test.html
+++ b/contexts/data/lib/closure-library/closure/goog/labs/net/xhr_test.html
@@ -14,6 +14,7 @@ See the COPYING file for details.
<script src="../../base.js"></script>
<script>
goog.require('goog.labs.net.xhr');
+ goog.require('goog.result');
goog.require('goog.string');
goog.require('goog.testing.AsyncTestCase');
goog.require('goog.testing.MockClock');
@@ -101,50 +102,51 @@ See the COPYING file for details.
if (shouldRunLocally) return;
testCase.waitForAsync('simpleRequest');
- xhr.send('GET', 'testdata/xhr_test_text.data').
- addCallback(function(xhr) {
+ var result = xhr.send('GET', 'testdata/xhr_test_text.data');
+ goog.result.waitOnSuccess(result,
+ function(xhr) {
assertEquals('Just some data.', xhr.responseText);
assertEquals(200, xhr.status);
testCase.continueTesting();
- }).
- addErrback(fail);
+ });
+ goog.result.waitOnError(result, fail);
}
function testGetText() {
if (shouldRunLocally) return;
testCase.waitForAsync('getText');
- xhr.get('testdata/xhr_test_text.data').
- addCallback(function(content) {
- assertEquals('Just some data.', content);
- testCase.continueTesting();
- }).
- addErrback(fail);
+ var result = xhr.get('testdata/xhr_test_text.data');
+ goog.result.waitOnSuccess(result, function(content) {
+ assertEquals('Just some data.', content);
+ testCase.continueTesting();
+ });
+ goog.result.waitOnError(result, fail);
}
function testGetTextWithJson() {
if (shouldRunLocally) return;
testCase.waitForAsync('getTextWithJson');
- xhr.get('testdata/xhr_test_json.data').
- addCallback(function(content) {
- assertEquals('while(1);\n{"stat":"ok","count":12345}\n', content);
- testCase.continueTesting();
- }).
- addErrback(fail);
+ var result = xhr.get('testdata/xhr_test_json.data');
+ goog.result.waitOnSuccess(result, function(content) {
+ assertEquals('while(1);\n{"stat":"ok","count":12345}\n', content);
+ testCase.continueTesting();
+ });
+ goog.result.waitOnError(result, fail);
}
function testPostText() {
if (shouldRunLocally) return;
testCase.waitForAsync('postText');
- xhr.post('testdata/xhr_test_text.data', 'post-data').
- addCallback(function(content) {
- // No good way to test post-data gets transported.
- assertEquals('Just some data.', content);
- testCase.continueTesting();
- }).
- addErrback(fail);
+ var result = xhr.post('testdata/xhr_test_text.data', 'post-data');
+ goog.result.waitOnSuccess(result, function(content) {
+ // No good way to test post-data gets transported.
+ assertEquals('Just some data.', content);
+ testCase.continueTesting();
+ });
+ goog.result.waitOnError(result, fail);
}
function testGetJson() {
@@ -163,19 +165,19 @@ See the COPYING file for details.
function testSerialRequests() {
if (shouldRunLocally) return;
- var d = xhr.get('testdata/xhr_test_text.data').
- addCallback(function(txt) {
+ var result = xhr.get('testdata/xhr_test_text.data');
+ var chainedResult = goog.result.chain(result, function(result) {
return xhr.getJson(
'testdata/xhr_test_json.data', {xssiPrefix: 'while(1);\n'});
});
// Data that comes through to callbacks should be from the 2nd request.
- d.addCallback(function(json) {
+ goog.result.waitOnSuccess(chainedResult, function(json) {
assertEquals('ok', json['stat']);
assertEquals(12345, json['count']);
testCase.continueTesting();
});
- d.addErrback(fail);
+ goog.result.waitOnError(chainedResult, fail);
}
function testParallelRequest() {
@@ -189,60 +191,66 @@ See the COPYING file for details.
jsonData = response;
}
- var d = goog.async.Deferred.succeed();
- d.awaitDeferred(
- xhr.get('testdata/xhr_test_text.data').addCallback(loadText));
- d.awaitDeferred(
- xhr.getJson('testdata/xhr_test_json.data',
- {xssiPrefix: 'while(1);\n'}).addCallback(loadJson));
- d.addCallback(function() {
+ var fetchTextResult = xhr.get('testdata/xhr_test_text.data');
+ var textDataResult = goog.result.transform(fetchTextResult, loadText);
+
+ var fetchJsonResult = xhr.getJson('testdata/xhr_test_json.data',
+ {xssiPrefix: 'while(1);\n'});
+ var jsonDataResult = goog.result.transform(fetchJsonResult, loadJson);
+
+ var combinedResult = goog.result.combine(textDataResult,
+ jsonDataResult);
+
+ goog.result.waitOnSuccess(combinedResult, function() {
assertEquals('Just some data.', textData);
assertEquals('ok', jsonData['stat']);
testCase.continueTesting();
});
- d.addErrback(fail);
+ goog.result.waitOnError(combinedResult, fail);
}
function testBadUrlDetectedAsError() {
if (shouldRunLocally) return;
testCase.waitForAsync('badUrl');
- xhr.getJson('unknown-file.dat').
- addCallback(fail).
- addErrback(function(err) {
- assertTrue('Error should be an HTTP error',
- err instanceof xhr.HttpError);
- assertEquals(404, err.status);
- assertNotNull(err.xhr);
- testCase.continueTesting();
- });
+ var result = xhr.getJson('unknown-file.dat');
+ goog.result.waitOnSuccess(result, fail);
+ goog.result.waitOnError(result, function(result) {
+ var err = result.getError();
+ assertTrue('Error should be an HTTP error',
+ err instanceof xhr.HttpError);
+ assertEquals(404, err.status);
+ assertNotNull(err.xhr);
+ testCase.continueTesting();
+ });
}
function testBadOriginTriggersOnErrorHandler() {
testCase.waitForAsync('badOrigin');
- xhr.get('http://www.google.com').
- addCallback(fail).
- addErrback(function(err) {
- // In IE this will be a goog.labs.net.xhr.Error since it is thrown
- // when calling xhr.open(), other browsers will raise an HttpError.
- assertTrue('Error should be an xhr error',
- err instanceof xhr.Error);
- assertNotNull(err.xhr);
- testCase.continueTesting();
- });
+ var result = xhr.get('http://www.google.com');
+ goog.result.waitOnSuccess(result, fail);
+ goog.result.waitOnError(result, function(result) {
+ var err = result.getError();
+ // In IE this will be a goog.labs.net.xhr.Error since it is thrown
+ // when calling xhr.open(), other browsers will raise an HttpError.
+ assertTrue('Error should be an xhr error',
+ err instanceof xhr.Error);
+ assertNotNull(err.xhr);
+ testCase.continueTesting();
+ });
}
function testAbortRequest() {
if (shouldRunLocally) return;
var err;
- var d = xhr.send('GET', 'test-url', null).
- addCallback(fail).
- addErrback(function(e) {
- err = e;
+ var result = xhr.send('GET', 'test-url', null);
+ goog.result.waitOnSuccess(result, fail);
+ goog.result.waitOnError(result, function(result) {
+ err = result.getError();
});
- d.cancel();
- assertTrue(err instanceof goog.async.Deferred.CancelledError);
+ result.cancel();
+ assertTrue(err instanceof goog.result.Result.CancelError);
}
//============================================================================
@@ -252,12 +260,12 @@ See the COPYING file for details.
function testSendNoOptions() {
var stubXhr = setupStubXMLHttpRequest();
var called = false;
- xhr.send('GET', 'test-url', null).
- addCallback(function(xhr) {
- called = true;
- assertEquals('Objects should be equal', xhr, stubXhr);
- }).
- addErrback(fail);
+ var result = xhr.send('GET', 'test-url', null);
+ goog.result.waitOnSuccess(result, function(xhr) {
+ called = true;
+ assertEquals('Objects should be equal', xhr, stubXhr);
+ });
+ goog.result.waitOnError(result, fail);
assertTrue('XHR should have been sent', stubXhr.sent);
assertFalse('Callback should not yet have been called', called);
@@ -272,7 +280,8 @@ See the COPYING file for details.
function testSendPostSetsDefaultHeader() {
var stubXhr = setupStubXMLHttpRequest();
- xhr.send('POST', 'test-url', null).addErrback(fail);
+ var result = xhr.send('POST', 'test-url', null);
+ goog.result.waitOnError(result, fail);
stubXhr.load(200);
@@ -284,9 +293,10 @@ See the COPYING file for details.
function testSendPostHeaders() {
var stubXhr = setupStubXMLHttpRequest();
- xhr.send('POST', 'test-url', null, {
+ var result = xhr.send('POST', 'test-url', null, {
headers: {'Content-Type': 'text/plain', 'X-Made-Up': 'FooBar'}
- }).addErrback(fail);
+ });
+ goog.result.waitOnError(result, fail);
stubXhr.load(200);
@@ -298,8 +308,8 @@ See the COPYING file for details.
function testSendWithCredentials() {
var stubXhr = setupStubXMLHttpRequest();
- xhr.send('POST', 'test-url', null, {withCredentials: true}).
- addErrback(fail);
+ var result = xhr.send('POST', 'test-url', null, {withCredentials: true});
+ goog.result.waitOnError(result, fail);
stubXhr.load(200);
assertTrue('XHR should have been sent', stubXhr.sent);
assertTrue(stubXhr.withCredentials);
@@ -307,8 +317,8 @@ See the COPYING file for details.
function testSendWithMimeType() {
var stubXhr = setupStubXMLHttpRequest();
- xhr.send('POST', 'test-url', null, {mimeType: 'text/plain'}).
- addErrback(fail);
+ var result = xhr.send('POST', 'test-url', null, {mimeType: 'text/plain'});
+ goog.result.waitOnError(result, fail);
stubXhr.load(200);
assertTrue('XHR should have been sent', stubXhr.sent);
@@ -318,9 +328,11 @@ See the COPYING file for details.
function testSendWithHttpError() {
var stubXhr = setupStubXMLHttpRequest();
var err;
- xhr.send('POST', 'test-url', null).
- addCallback(fail).
- addErrback(function(e) { err = e; });
+ var result = xhr.send('POST', 'test-url', null);
+ goog.result.waitOnSuccess(result, fail);
+ goog.result.waitOnError(result, function(result) {
+ err = result.getError();
+ });
stubXhr.load(500);
@@ -338,7 +350,8 @@ See the COPYING file for details.
var stubXhr = setupStubXMLHttpRequest();
var err;
- xhr.send('POST', 'test-url', null, {timeoutMs: 1500}).addErrback(fail);
+ var result = xhr.send('POST', 'test-url', null, {timeoutMs: 1500});
+ goog.result.waitOnError(result, fail);
assertTrue(mockClock.getTimeoutsMade() > 0);
mockClock.tick(1400);
stubXhr.load(200);
@@ -350,9 +363,9 @@ See the COPYING file for details.
function testSendWithTimeoutHit() {
var stubXhr = setupStubXMLHttpRequest();
var err;
- xhr.send('POST', 'test-url', null, {timeoutMs: 50})
- .addCallback(fail)
- .addErrback(function(e) { err = e; });
+ var result = xhr.send('POST', 'test-url', null, {timeoutMs: 50});
+ goog.result.waitOnSuccess(result, fail);
+ goog.result.waitOnError(result, function(res) { err = res.getError(); });
assertTrue(mockClock.getTimeoutsMade() > 0);
mockClock.tick(50);
assertTrue('XHR should have been sent', stubXhr.sent);
@@ -375,28 +388,28 @@ See the COPYING file for details.
function testCancelRequest() {
var stubXhr = setupStubXMLHttpRequest();
var err;
- var d = xhr.send('GET', 'test-url', null, {timeoutMs: 50}).
- addCallback(fail).
- addErrback(function(e) {
- err = e;
- });
- d.cancel();
+ var result = xhr.send('GET', 'test-url', null, {timeoutMs: 50});
+ goog.result.waitOnSuccess(result, fail);
+ goog.result.waitOnError(result, function(result) {
+ err = result.getError();
+ });
+ result.cancel();
stubXhr.load(0); // Call load anyway, shoudn't make a difference.
mockClock.tick(100); // Timeout should never be called.
assertTrue('XHR should have been sent', stubXhr.sent);
assertTrue('XHR should have been aborted', stubXhr.aborted);
- assertTrue(err instanceof goog.async.Deferred.CancelledError);
+ assertTrue(err instanceof goog.result.Result.CancelError);
}
function testGetJson() {
var stubXhr = setupStubXMLHttpRequest();
var responseData;
- xhr.getJson('test-url').
- addCallback(function(data) {
- responseData = data;
- }).
- addErrback(fail);
+ var result = xhr.getJson('test-url');
+ goog.result.waitOnSuccess(result, function(data) {
+ responseData = data;
+ });
+ goog.result.waitOnError(result, fail);
stubXhr.responseText = '{"a": 1, "b": 2}';
stubXhr.load(200);
@@ -407,11 +420,11 @@ See the COPYING file for details.
function testGetJsonWithXssiPrefix() {
var stubXhr = setupStubXMLHttpRequest();
var responseData;
- xhr.getJson('test-url', {xssiPrefix: 'while(1);\n'}).
- addCallback(function(data) {
- responseData = data;
- }).
- addErrback(fail);
+ var result = xhr.getJson('test-url', {xssiPrefix: 'while(1);\n'});
+ goog.result.waitOnSuccess(result, function(data) {
+ responseData = data;
+ });
+ goog.result.waitOnError(result, fail);
stubXhr.responseText = 'while(1);\n{"a": 1, "b": 2}';
stubXhr.load(200);
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/object/object.js b/contexts/data/lib/closure-library/closure/goog/labs/object/object.js
new file mode 100644
index 0000000..e534769
--- /dev/null
+++ b/contexts/data/lib/closure-library/closure/goog/labs/object/object.js
@@ -0,0 +1,46 @@
+// 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 A labs location for functions destined for Closure's
+ * {@code goog.object} namespace.
+ */
+
+goog.provide('goog.labs.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.labs.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;
+};
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/object/object_test.html b/contexts/data/lib/closure-library/closure/goog/labs/object/object_test.html
new file mode 100644
index 0000000..d9bd1be
--- /dev/null
+++ b/contexts/data/lib/closure-library/closure/goog/labs/object/object_test.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html>
+<!--
+Copyright 2012 The Closure Library Authors. All Rights Reserved.
+
+Use of this source code is governed by the Apache License, Version 2.0.
+See the COPYING file for details.
+-->
+<!--
+-->
+<head>
+<meta http-equiv="X-UA-Compatible" content="IE=edge">
+<title>Closure Unit Tests - goog.labs.object</title>
+<script src="../../base.js"></script>
+<script>
+ goog.require('goog.labs.object');
+ goog.require('goog.testing.jsunit');
+</script>
+</head>
+<body>
+<script>
+function testIs() {
+ var object = {};
+ assertTrue(goog.labs.object.is(object, object));
+ assertFalse(goog.labs.object.is(object, {}));
+
+ assertTrue(goog.labs.object.is(NaN, NaN));
+ assertTrue(goog.labs.object.is(0, 0));
+ assertTrue(goog.labs.object.is(1, 1));
+ assertTrue(goog.labs.object.is(-1, -1));
+ assertTrue(goog.labs.object.is(123, 123));
+ assertFalse(goog.labs.object.is(0, -0));
+ assertFalse(goog.labs.object.is(-0, 0));
+ assertFalse(goog.labs.object.is(0, 1));
+
+ assertTrue(goog.labs.object.is(true, true));
+ assertTrue(goog.labs.object.is(false, false));
+ assertFalse(goog.labs.object.is(true, false));
+ assertFalse(goog.labs.object.is(false, true));
+
+ assertTrue(goog.labs.object.is('', ''));
+ assertTrue(goog.labs.object.is('a', 'a'));
+ assertFalse(goog.labs.object.is('', 'a'));
+ assertFalse(goog.labs.object.is('a', ''));
+ assertFalse(goog.labs.object.is('a', 'b'));
+
+ assertFalse(goog.labs.object.is(true, 'true'));
+ assertFalse(goog.labs.object.is('true', true));
+ assertFalse(goog.labs.object.is(false, 'false'));
+ assertFalse(goog.labs.object.is('false', false));
+ assertFalse(goog.labs.object.is(0, '0'));
+ assertFalse(goog.labs.object.is('0', 0));
+}
+</script>
+</body>
+</html>
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/observe/notice.js b/contexts/data/lib/closure-library/closure/goog/labs/observe/notice.js
new file mode 100644
index 0000000..157573e
--- /dev/null
+++ b/contexts/data/lib/closure-library/closure/goog/labs/observe/notice.js
@@ -0,0 +1,63 @@
+// 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 Provides a notice object that is used to encapsulates
+ * information about a particular change/notification on an observable
+ * object.
+ */
+
+goog.provide('goog.labs.observe.Notice');
+
+
+
+/**
+ * A notice object encapsulates information about a notification fired
+ * by an observable.
+ * @param {!goog.labs.observe.Observable} observable The observable
+ * object that fires this notice.
+ * @param {*=} opt_data The optional data associated with this notice.
+ * @constructor
+ */
+goog.labs.observe.Notice = function(observable, opt_data) {
+ /**
+ * @type {!goog.labs.observe.Observable}
+ * @private
+ */
+ this.observable_ = observable;
+
+ /**
+ * @type {*}
+ * @private
+ */
+ this.data_ = opt_data;
+};
+
+
+/**
+ * @return {!goog.labs.observe.Observable} The observable object that
+ * fires this notice.
+ */
+goog.labs.observe.Notice.prototype.getObservable = function() {
+ return this.observable_;
+};
+
+
+/**
+ * @return {*} The optional data associated with this notice. May be
+ * null/undefined.
+ */
+goog.labs.observe.Notice.prototype.getData = function() {
+ return this.data_;
+};
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/observe/observable.js b/contexts/data/lib/closure-library/closure/goog/labs/observe/observable.js
new file mode 100644
index 0000000..2ca576f
--- /dev/null
+++ b/contexts/data/lib/closure-library/closure/goog/labs/observe/observable.js
@@ -0,0 +1,77 @@
+// 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 Experimental observer-observable API. This is
+ * intended as super lightweight replacement of
+ * goog.events.EventTarget when w3c event model bubble/capture
+ * behavior is not required.
+ *
+ * This is similar to {@code goog.pubsub.PubSub} but with different
+ * intent and naming so that it is more discoverable. The API is
+ * tighter while allowing for more flexibility offered by the
+ * interface {@code Observable}.
+ *
+ * WARNING: This is still highly experimental. Please contact author
+ * before using this.
+ *
+ */
+
+goog.provide('goog.labs.observe.Observable');
+
+goog.require('goog.disposable.IDisposable');
+
+
+
+/**
+ * Interface for an observable object.
+ * @interface
+ * @extends {goog.disposable.IDisposable}
+ */
+goog.labs.observe.Observable = function() {};
+
+
+/**
+ * Registers an observer on the observable.
+ *
+ * Note that no guarantee is provided on order of execution of the
+ * observers. For a single notification, one Notice object is reused
+ * across all invoked observers.
+ *
+ * Note that if an observation with the same observer is already
+ * registered, it will not be registered again. Comparison is done via
+ * observer's {@code equals} method.
+ *
+ * @param {!goog.labs.observe.Observer} observer The observer to add.
+ * @return {boolean} Whether the observer was successfully added.
+ */
+goog.labs.observe.Observable.prototype.observe = function(observer) {};
+
+
+/**
+ * Unregisters an observer from the observable. The parameter must be
+ * the same as those passed to {@code observe} method. Comparison is
+ * done via observer's {@code equals} method.
+ * @param {!goog.labs.observe.Observer} observer The observer to remove.
+ * @return {boolean} Whether the observer is removed.
+ */
+goog.labs.observe.Observable.prototype.unobserve = function(observer) {};
+
+
+/**
+ * Notifies observers by invoking them. Optionally, a data object may be
+ * given to be passed to each observer.
+ * @param {*=} opt_data An optional data object.
+ */
+goog.labs.observe.Observable.prototype.notify = function(opt_data) {};
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/observe/observableset.js b/contexts/data/lib/closure-library/closure/goog/labs/observe/observableset.js
new file mode 100644
index 0000000..8ce844e
--- /dev/null
+++ b/contexts/data/lib/closure-library/closure/goog/labs/observe/observableset.js
@@ -0,0 +1,180 @@
+// 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 A set of {@code goog.labs.observe.Observable}s that
+ * allow registering and removing observers to all of the observables
+ * in the set.
+ */
+
+goog.provide('goog.labs.observe.ObservableSet');
+
+goog.require('goog.array');
+goog.require('goog.labs.observe.Observer');
+
+
+
+/**
+ * Creates a set of observables.
+ *
+ * An ObservableSet is a collection of observables. Observers may be
+ * reigstered and will receive notifications when any of the
+ * observables notify. This class is meant to simplify management of
+ * observations on multiple observables of the same nature.
+ *
+ * @constructor
+ */
+goog.labs.observe.ObservableSet = function() {
+ /**
+ * The observers registered with this set.
+ * @type {!Array.<!goog.labs.observe.Observer>}
+ * @private
+ */
+ this.observers_ = [];
+
+ /**
+ * The observables in this set.
+ * @type {!Array.<!goog.labs.observe.Observable>}
+ * @private
+ */
+ this.observables_ = [];
+};
+
+
+/**
+ * Adds an observer that observes all observables in the set. If new
+ * observables are added to or removed from the set, the observer will
+ * be registered or unregistered accordingly.
+ *
+ * The observer will not be added if there is already an equivalent
+ * observer.
+ *
+ * @param {!goog.labs.observe.Observer} observer The observer to invoke.
+ * @return {boolean} Whether the observer is actually added.
+ */
+goog.labs.observe.ObservableSet.prototype.addObserver = function(observer) {
+ // Check whether the observer already exists.
+ if (goog.array.find(this.observers_, goog.partial(
+ goog.labs.observe.Observer.equals, observer))) {
+ return false;
+ }
+
+ this.observers_.push(observer);
+ goog.array.forEach(this.observables_, function(o) {
+ o.observe(observer);
+ });
+ return true;
+};
+
+
+/**
+ * Removes an observer from the set. The observer will be removed from
+ * all observables in the set. Does nothing if the observer is not in
+ * the set.
+ * @param {!goog.labs.observe.Observer} observer The observer to remove.
+ * @return {boolean} Whether the observer is actually removed.
+ */
+goog.labs.observe.ObservableSet.prototype.removeObserver = function(observer) {
+ // Check that the observer exists before removing.
+ var removed = goog.array.removeIf(this.observers_, goog.partial(
+ goog.labs.observe.Observer.equals, observer));
+
+ if (removed) {
+ goog.array.forEach(this.observables_, function(o) {
+ o.unobserve(observer);
+ });
+ }
+ return removed;
+};
+
+
+/**
+ * Removes all registered observers.
+ */
+goog.labs.observe.ObservableSet.prototype.removeAllObservers = function() {
+ this.unregisterAll_();
+ this.observers_.length = 0;
+};
+
+
+/**
+ * Adds an observable to the set. All previously added and future
+ * observers will be added to the new observable as well.
+ *
+ * The observable will not be added if it is already registered in the
+ * set.
+ *
+ * @param {!goog.labs.observe.Observable} observable The observable to add.
+ * @return {boolean} Whether the observable is actually added.
+ */
+goog.labs.observe.ObservableSet.prototype.addObservable = function(observable) {
+ if (goog.array.contains(this.observables_, observable)) {
+ return false;
+ }
+
+ this.observables_.push(observable);
+ goog.array.forEach(this.observers_, function(observer) {
+ observable.observe(observer);
+ });
+ return true;
+};
+
+
+/**
+ * Removes an observable from the set. All observers registered on the
+ * set will be removed from the observable as well.
+ * @param {!goog.labs.observe.Observable} observable The observable to remove.
+ * @return {boolean} Whether the observable is actually removed.
+ */
+goog.labs.observe.ObservableSet.prototype.removeObservable = function(
+ observable) {
+ var removed = goog.array.remove(this.observables_, observable);
+ if (removed) {
+ goog.array.forEach(this.observers_, function(observer) {
+ observable.unobserve(observer);
+ });
+ }
+ return removed;
+};
+
+
+/**
+ * Removes all registered observables.
+ */
+goog.labs.observe.ObservableSet.prototype.removeAllObservables = function() {
+ this.unregisterAll_();
+ this.observables_.length = 0;
+};
+
+
+/**
+ * Removes all registered observations and observables.
+ */
+goog.labs.observe.ObservableSet.prototype.removeAll = function() {
+ this.removeAllObservers();
+ this.observables_.length = 0;
+};
+
+
+/**
+ * Unregisters all registered observers from all registered observables.
+ * @private
+ */
+goog.labs.observe.ObservableSet.prototype.unregisterAll_ = function() {
+ goog.array.forEach(this.observers_, function(observer) {
+ goog.array.forEach(this.observables_, function(o) {
+ o.unobserve(observer);
+ });
+ }, this);
+};
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/observe/observableset_test.html b/contexts/data/lib/closure-library/closure/goog/labs/observe/observableset_test.html
new file mode 100644
index 0000000..fac989d
--- /dev/null
+++ b/contexts/data/lib/closure-library/closure/goog/labs/observe/observableset_test.html
@@ -0,0 +1,207 @@
+<!DOCTYPE html>
+<html>
+<!--
+Copyright 2012 The Closure Library Authors. All Rights Reserved.
+
+Use of this source code is governed by the Apache License, Version 2.0.
+See the COPYING file for details.
+-->
+<!--
+-->
+<head>
+<meta http-equiv="X-UA-Compatible" content="IE=edge">
+<title>Closure Unit Tests - goog.labs.observe.ObservationSet</title>
+<script src="../../base.js"></script>
+<script>
+ goog.require('goog.dispose');
+ goog.require('goog.labs.observe.ObservableSet');
+ goog.require('goog.labs.observe.Observer');
+ goog.require('goog.labs.observe.SimpleObservable');
+ goog.require('goog.testing.jsunit');
+ goog.require('goog.testing.recordFunction');
+</script>
+</head>
+<body>
+<script>
+var observable1;
+var observable2;
+var observableSet;
+
+
+function setUp() {
+ observable1 = new goog.labs.observe.SimpleObservable();
+ observable2 = new goog.labs.observe.SimpleObservable();
+ observableSet = new goog.labs.observe.ObservableSet();
+}
+
+
+function tearDown() {
+ goog.dispose(observable1);
+ goog.dispose(observable2);
+}
+
+
+function testAddingObservers() {
+ var observerFn1 = goog.testing.recordFunction();
+ var observerFn2 = goog.testing.recordFunction();
+ assertTrue(observableSet.addObserver(
+ goog.labs.observe.Observer.fromFunction(observerFn1)));
+ assertFalse(observableSet.addObserver(
+ goog.labs.observe.Observer.fromFunction(observerFn1)));
+ assertTrue(observableSet.addObserver(
+ goog.labs.observe.Observer.fromFunction(observerFn2)));
+ assertFalse(observableSet.addObserver(
+ goog.labs.observe.Observer.fromFunction(observerFn2)));
+}
+
+
+function testAddingObservables() {
+ assertTrue(observableSet.addObservable(observable1));
+ assertFalse(observableSet.addObservable(observable1));
+ assertTrue(observableSet.addObservable(observable2));
+}
+
+
+function testRemovingObservers() {
+ var observerFn = goog.testing.recordFunction();
+ observableSet.addObserver(
+ goog.labs.observe.Observer.fromFunction(observerFn));
+ observableSet.addObservable(observable1);
+ observableSet.addObservable(observable2);
+
+ assertTrue(observableSet.removeObserver(
+ goog.labs.observe.Observer.fromFunction(observerFn)));
+ assertFalse(observableSet.removeObserver(
+ goog.labs.observe.Observer.fromFunction(observerFn)));
+
+ observable1.notify();
+ observable2.notify();
+ assertEquals(0, observerFn.getCallCount());
+}
+
+
+function testRemovingObservables() {
+ var observerFn = goog.testing.recordFunction();
+ observableSet.addObservable(observable1);
+ observableSet.addObserver(
+ goog.labs.observe.Observer.fromFunction(observerFn));
+ observableSet.addObserver(
+ goog.labs.observe.Observer.fromFunction(observerFn));
+
+ assertTrue(observableSet.removeObservable(observable1));
+ assertFalse(observableSet.removeObservable(observable1));
+
+ observable1.notify();
+ observable1.notify();
+ assertEquals(0, observerFn.getCallCount());
+}
+
+
+function testAddingObserverBeforeAddingObservable() {
+ var observerFn = goog.testing.recordFunction();
+ observableSet.addObserver(
+ goog.labs.observe.Observer.fromFunction(observerFn));
+
+ observableSet.addObservable(observable1);
+ observableSet.addObservable(observable2);
+
+ observable1.notify();
+ assertEquals(1, observerFn.getCallCount());
+ observable2.notify();
+ assertEquals(2, observerFn.getCallCount());
+}
+
+
+function testAddingObserverAfterAddingObservable() {
+ var observerFn1 = goog.testing.recordFunction();
+ var observerFn2 = goog.testing.recordFunction();
+
+ observableSet.addObservable(observable1);
+
+ observableSet.addObserver(
+ goog.labs.observe.Observer.fromFunction(observerFn1));
+ observableSet.addObserver(
+ goog.labs.observe.Observer.fromFunction(observerFn2));
+
+ observable1.notify();
+ assertEquals(1, observerFn1.getCallCount());
+ assertEquals(1, observerFn2.getCallCount());
+}
+
+
+function testRemoveAllObservers() {
+ var observerFn1 = goog.testing.recordFunction();
+ var observerFn2 = goog.testing.recordFunction();
+
+ observableSet.addObservable(observable1);
+ observableSet.addObservable(observable2);
+ observableSet.addObserver(
+ goog.labs.observe.Observer.fromFunction(observerFn1));
+ observableSet.addObserver(
+ goog.labs.observe.Observer.fromFunction(observerFn2));
+
+ observableSet.removeAllObservers();
+
+ observable1.notify();
+ observable2.notify();
+ assertEquals(0, observerFn1.getCallCount());
+
+ // Ensures that the observables are still around.
+ observableSet.addObserver(
+ goog.labs.observe.Observer.fromFunction(observerFn1));
+ observable1.notify();
+ observable2.notify();
+ assertEquals(2, observerFn1.getCallCount());
+}
+
+
+function testRemoveAllObservables() {
+ var observerFn1 = goog.testing.recordFunction();
+ var observerFn2 = goog.testing.recordFunction();
+
+ observableSet.addObservable(observable1);
+ observableSet.addObservable(observable2);
+ observableSet.addObserver(
+ goog.labs.observe.Observer.fromFunction(observerFn1));
+ observableSet.addObserver(
+ goog.labs.observe.Observer.fromFunction(observerFn2));
+
+ observableSet.removeAllObservables();
+
+ observable1.notify();
+ observable2.notify();
+ assertEquals(0, observerFn1.getCallCount());
+ assertEquals(0, observerFn2.getCallCount());
+
+ // Ensures that the observers are still around.
+ observableSet.addObservable(observable1);
+ observable1.notify();
+ assertEquals(1, observerFn1.getCallCount());
+ assertEquals(1, observerFn2.getCallCount());
+}
+
+
+function testRemoveAll() {
+ var observerFn1 = goog.testing.recordFunction();
+ var observerFn2 = goog.testing.recordFunction();
+
+ observableSet.addObservable(observable1);
+ observableSet.addObservable(observable2);
+ observableSet.addObserver(
+ goog.labs.observe.Observer.fromFunction(observerFn1));
+ observableSet.addObserver(
+ goog.labs.observe.Observer.fromFunction(observerFn2));
+
+ observableSet.removeAll();
+
+ observable1.notify();
+ observable2.notify();
+ assertEquals(0, observerFn1.getCallCount());
+
+ assertEquals(0, observableSet.observers_.length);
+ assertEquals(0, observableSet.observables_.length);
+}
+
+</script>
+</body>
+</html>
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/observe/observationset.js b/contexts/data/lib/closure-library/closure/goog/labs/observe/observationset.js
new file mode 100644
index 0000000..892c3c6
--- /dev/null
+++ b/contexts/data/lib/closure-library/closure/goog/labs/observe/observationset.js
@@ -0,0 +1,156 @@
+// 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 A set of observations. This set provides a convenient
+ * means of observing many observables at once.
+ *
+ * This is similar in purpose to {@code goog.events.EventHandler}.
+ *
+ */
+
+goog.provide('goog.labs.observe.ObservationSet');
+
+goog.require('goog.array');
+goog.require('goog.labs.observe.Observer');
+
+
+
+/**
+ * A set of observations. An observation is defined by an observable
+ * and an observer. The set keeps track of observations and
+ * allows their removal.
+ * @param {!Object=} opt_defaultScope Optional function scope to use
+ * when using {@code observeWithFunction} and
+ * {@code unobserveWithFunction}.
+ * @constructor
+ */
+goog.labs.observe.ObservationSet = function(opt_defaultScope) {
+ /**
+ * @type {!Array.<!goog.labs.observe.ObservationSet.Observation_>}
+ * @private
+ */
+ this.storedObservations_ = [];
+
+ /**
+ * @type {!Object|undefined}
+ * @private
+ */
+ this.defaultScope_ = opt_defaultScope;
+};
+
+
+/**
+ * Observes the given observer on the observable.
+ * @param {!goog.labs.observe.Observable} observable The observable to
+ * observe on.
+ * @param {!goog.labs.observe.Observer} observer The observer.
+ * @return {boolean} True if the observer is successfully registered.
+ */
+goog.labs.observe.ObservationSet.prototype.observe = function(
+ observable, observer) {
+ var success = observable.observe(observer);
+ if (success) {
+ this.storedObservations_.push(
+ new goog.labs.observe.ObservationSet.Observation_(
+ observable, observer));
+ }
+ return success;
+};
+
+
+/**
+ * Observes the given function on the observable.
+ * @param {!goog.labs.observe.Observable} observable The observable to
+ * observe on.
+ * @param {function(!goog.labs.observe.Notice)} fn The handler function.
+ * @param {!Object=} opt_scope Optional scope.
+ * @return {goog.labs.observe.Observer} The registered observer object.
+ * If the observer is not successfully registered, this will be null.
+ */
+goog.labs.observe.ObservationSet.prototype.observeWithFunction = function(
+ observable, fn, opt_scope) {
+ var observer = goog.labs.observe.Observer.fromFunction(
+ fn, opt_scope || this.defaultScope_);
+ if (this.observe(observable, observer)) {
+ return observer;
+ }
+ return null;
+};
+
+
+/**
+ * Unobserves the given observer from the observable.
+ * @param {!goog.labs.observe.Observable} observable The observable to
+ * unobserve from.
+ * @param {!goog.labs.observe.Observer} observer The observer.
+ * @return {boolean} True if the observer is successfully removed.
+ */
+goog.labs.observe.ObservationSet.prototype.unobserve = function(
+ observable, observer) {
+ var removed = goog.array.removeIf(
+ this.storedObservations_, function(o) {
+ return o.observable == observable &&
+ goog.labs.observe.Observer.equals(o.observer, observer);
+ });
+
+ if (removed) {
+ observable.unobserve(observer);
+ }
+ return removed;
+};
+
+
+/**
+ * Unobserves the given function from the observable.
+ * @param {!goog.labs.observe.Observable} observable The observable to
+ * unobserve from.
+ * @param {function(!goog.labs.observe.Notice)} fn The handler function.
+ * @param {!Object=} opt_scope Optional scope.
+ * @return {boolean} True if the observer is successfully removed.
+ */
+goog.labs.observe.ObservationSet.prototype.unobserveWithFunction = function(
+ observable, fn, opt_scope) {
+ var observer = goog.labs.observe.Observer.fromFunction(
+ fn, opt_scope || this.defaultScope_);
+ return this.unobserve(observable, observer);
+};
+
+
+/**
+ * Removes all observations registered through this set.
+ */
+goog.labs.observe.ObservationSet.prototype.removeAll = function() {
+ goog.array.forEach(this.storedObservations_, function(observation) {
+ var observable = observation.observable;
+ var observer = observation.observer;
+ observable.unobserve(observer);
+ });
+};
+
+
+
+/**
+ * A representation of an observation, which is defined uniquely by
+ * the observable and observer.
+ * @param {!goog.labs.observe.Observable} observable The observable.
+ * @param {!goog.labs.observe.Observer} observer The observer.
+ * @constructor
+ * @private
+ */
+goog.labs.observe.ObservationSet.Observation_ = function(
+ observable, observer) {
+ this.observable = observable;
+ this.observer = observer;
+};
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/observe/observationset_test.html b/contexts/data/lib/closure-library/closure/goog/labs/observe/observationset_test.html
new file mode 100644
index 0000000..17e0ebe
--- /dev/null
+++ b/contexts/data/lib/closure-library/closure/goog/labs/observe/observationset_test.html
@@ -0,0 +1,252 @@
+<!DOCTYPE html>
+<html>
+<!--
+Copyright 2012 The Closure Library Authors. All Rights Reserved.
+
+Use of this source code is governed by the Apache License, Version 2.0.
+See the COPYING file for details.
+-->
+<!--
+-->
+<head>
+<meta http-equiv="X-UA-Compatible" content="IE=edge">
+<title>Closure Unit Tests - goog.labs.observe.ObservationSet</title>
+<script src="../../base.js"></script>
+<script>
+ goog.require('goog.dispose');
+ goog.require('goog.labs.observe.Observer');
+ goog.require('goog.labs.observe.ObservationSet');
+ goog.require('goog.labs.observe.SimpleObservable');
+ goog.require('goog.testing.jsunit');
+ goog.require('goog.testing.recordFunction');
+</script>
+</head>
+<body>
+<script>
+var observable;
+var observable2;
+var observationSet;
+
+
+function setUp() {
+ observable = new goog.labs.observe.SimpleObservable();
+ observable2 = new goog.labs.observe.SimpleObservable();
+ observationSet = new goog.labs.observe.ObservationSet();
+}
+
+
+function tearDown() {
+ goog.dispose(observable);
+ goog.dispose(observable2);
+}
+
+
+function testObserveObservesCorrectly() {
+ var observerFn = goog.testing.recordFunction();
+ assertTrue(observationSet.observe(
+ observable, goog.labs.observe.Observer.fromFunction(observerFn)));
+
+ observable.notify();
+ assertEquals(1, observerFn.getCallCount());
+}
+
+
+function testObserveDoesNotRegisterDuplicateObserver() {
+ var observerFn = goog.testing.recordFunction();
+ assertTrue(observationSet.observe(
+ observable, goog.labs.observe.Observer.fromFunction(observerFn)));
+ assertFalse(observationSet.observe(
+ observable, goog.labs.observe.Observer.fromFunction(observerFn)));
+
+ observable.notify();
+ assertEquals(1, observerFn.getCallCount());
+}
+
+
+function testObserveWithMultipleObservers() {
+ var observerFn = goog.testing.recordFunction();
+ var observerFn2 = goog.testing.recordFunction();
+ assertTrue(observationSet.observe(
+ observable, goog.labs.observe.Observer.fromFunction(observerFn)));
+ assertTrue(observationSet.observe(
+ observable, goog.labs.observe.Observer.fromFunction(observerFn2)));
+
+ observable.notify();
+ assertEquals(1, observerFn.getCallCount());
+ assertEquals(1, observerFn2.getCallCount());
+}
+
+
+function testObserveWithFunction() {
+ var observerFn = goog.testing.recordFunction();
+ assertNotNull(observationSet.observeWithFunction(observable, observerFn));
+ assertNull(observationSet.observeWithFunction(observable, observerFn));
+ assertFalse(observationSet.observe(
+ observable, goog.labs.observe.Observer.fromFunction(observerFn)));
+
+ observable.notify();
+ assertEquals(1, observerFn.getCallCount());
+}
+
+
+function testObserveWithFunctionAndScope() {
+ var scope = {};
+
+ var observerFn = goog.testing.recordFunction(function() {
+ assertEquals(scope, this);
+ });
+ assertNotNull(observationSet.observeWithFunction(
+ observable, observerFn, scope));
+ assertNull(observationSet.observeWithFunction(
+ observable, observerFn, scope));
+ assertFalse(observationSet.observe(
+ observable, goog.labs.observe.Observer.fromFunction(observerFn, scope)));
+
+ observable.notify();
+ assertEquals(1, observerFn.getCallCount());
+}
+
+
+function testObserveWithFunctionWithDefaultScope() {
+ var scope = {};
+ observationSet = new goog.labs.observe.ObservationSet(scope);
+
+ var observerFn = goog.testing.recordFunction(function() {
+ assertEquals(scope, this);
+ });
+
+ assertNotNull(observationSet.observeWithFunction(observable, observerFn));
+ assertNull(observationSet.observeWithFunction(observable, observerFn));
+ assertNull(observationSet.observeWithFunction(observable, observerFn, scope));
+ assertFalse(observationSet.observe(
+ observable, goog.labs.observe.Observer.fromFunction(observerFn, scope)));
+
+ observable.notify();
+ assertEquals(1, observerFn.getCallCount());
+
+ // This should succeed since we use observe method, defaultScope is
+ // not used in this case.
+ assertTrue(observationSet.observe(
+ observable, goog.labs.observe.Observer.fromFunction(observerFn)));
+
+ // This should succeed since the scope is different.
+ var scope2 = {};
+ assertNotNull(observationSet.observeWithFunction(
+ observable, observerFn, scope2));
+}
+
+
+function testUnobserveUnobservesCorrectly() {
+ var observerFn = goog.testing.recordFunction();
+ assertTrue(observationSet.observe(
+ observable, goog.labs.observe.Observer.fromFunction(observerFn)));
+ assertTrue(observationSet.unobserve(
+ observable, goog.labs.observe.Observer.fromFunction(observerFn)));
+
+ observable.notify();
+ assertEquals(0, observerFn.getCallCount());
+}
+
+
+function testUnobserveDoesNotUnobserveObservationNotMadeByObservationSet() {
+ var observerFn = goog.testing.recordFunction();
+
+ // Observation via observable directly.
+ observable.observe(goog.labs.observe.Observer.fromFunction(observerFn));
+
+ assertFalse(observationSet.unobserve(
+ observable, goog.labs.observe.Observer.fromFunction(observerFn)));
+ observable.notify();
+ assertEquals(1, observerFn.getCallCount());
+ observerFn.reset();
+
+ // Observation via another ObservationSet.
+ var observationSet2 = new goog.labs.observe.ObservationSet();
+ observationSet2.observe(
+ observable2, goog.labs.observe.Observer.fromFunction(observerFn));
+
+ assertFalse(observationSet.unobserve(
+ observable2, goog.labs.observe.Observer.fromFunction(observerFn)));
+ observable2.notify();
+ assertEquals(1, observerFn.getCallCount());
+ observerFn.reset();
+}
+
+
+function testUnobserveWithFunction() {
+ var observerFn = goog.testing.recordFunction();
+ observationSet.observeWithFunction(observable, observerFn);
+ assertTrue(observationSet.unobserveWithFunction(observable, observerFn));
+ assertNotNull(observationSet.observeWithFunction(observable, observerFn));
+ assertTrue(observationSet.unobserve(
+ observable, goog.labs.observe.Observer.fromFunction(observerFn)));
+
+ observable.notify();
+ assertEquals(0, observerFn.getCallCount());
+}
+
+
+function testUnobserveWithFunctionAndScope() {
+ var scope = {};
+
+ var observerFn = goog.testing.recordFunction();
+ observationSet.observeWithFunction(
+ observable, observerFn, scope);
+ assertTrue(observationSet.unobserveWithFunction(
+ observable, observerFn, scope));
+ assertNotNull(observationSet.observeWithFunction(
+ observable, observerFn, scope));
+ assertTrue(observationSet.unobserve(
+ observable, goog.labs.observe.Observer.fromFunction(observerFn, scope)));
+
+ observable.notify();
+ assertEquals(0, observerFn.getCallCount());
+}
+
+
+function testUnobserveWithFunctionWithDefaultScope() {
+ var scope = {};
+ observationSet = new goog.labs.observe.ObservationSet(scope);
+
+ var observerFn = goog.testing.recordFunction();
+ observationSet.observeWithFunction(observable, observerFn);
+ assertTrue(observationSet.unobserveWithFunction(observable, observerFn));
+
+ assertNotNull(observationSet.observeWithFunction(
+ observable, observerFn, scope));
+ assertTrue(observationSet.unobserveWithFunction(observable, observerFn));
+
+ assertNotNull(observationSet.observeWithFunction(observable, observerFn));
+ assertTrue(observationSet.unobserveWithFunction(
+ observable, observerFn, scope));
+
+ observable.notify();
+ assertEquals(0, observerFn.getCallCount());
+}
+
+
+function testRemoveAllWithZeroObservation() {
+ observationSet.removeAll();
+}
+
+
+function testRemoveAll() {
+ var observerFn = goog.testing.recordFunction();
+ var observerFn2 = goog.testing.recordFunction();
+ assertTrue(observationSet.observe(
+ observable, goog.labs.observe.Observer.fromFunction(observerFn)));
+ assertTrue(observationSet.observe(
+ observable, goog.labs.observe.Observer.fromFunction(observerFn2)));
+ assertTrue(observationSet.observe(
+ observable2, goog.labs.observe.Observer.fromFunction(observerFn)));
+ observationSet.removeAll();
+
+ observable.notify();
+ observable2.notify();
+
+ assertEquals(0, observerFn.getCallCount());
+ assertEquals(0, observerFn2.getCallCount());
+}
+</script>
+</body>
+</html>
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/observe/observer.js b/contexts/data/lib/closure-library/closure/goog/labs/observe/observer.js
new file mode 100644
index 0000000..ff04430
--- /dev/null
+++ b/contexts/data/lib/closure-library/closure/goog/labs/observe/observer.js
@@ -0,0 +1,100 @@
+// 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 Provide definition of an observer. This is meant to
+ * be used with {@code goog.labs.observe.Observable}.
+ *
+ * This file also provides convenient functions to compare and create
+ * Observer objects.
+ *
+ */
+
+goog.provide('goog.labs.observe.Observer');
+
+
+
+/**
+ * A class implementing {@code Observer} may be informed of changes in
+ * observable object.
+ * @see {goog.labs.observe.Observable}
+ * @interface
+ */
+goog.labs.observe.Observer = function() {};
+
+
+/**
+ * Notifies the observer of changes to the observable object.
+ * @param {!goog.labs.observe.Notice} notice The notice object.
+ */
+goog.labs.observe.Observer.prototype.notify;
+
+
+/**
+ * Whether this observer is equal to the given observer.
+ * @param {!goog.labs.observe.Observer} observer The observer to compare with.
+ * @return {boolean} Whether the two observers are equal.
+ */
+goog.labs.observe.Observer.prototype.equals;
+
+
+/**
+ * @param {!goog.labs.observe.Observer} observer1 Observer to compare.
+ * @param {!goog.labs.observe.Observer} observer2 Observer to compare.
+ * @return {boolean} Whether observer1 and observer2 are equal, as
+ * determined by the first observer1's {@code equals} method.
+ */
+goog.labs.observe.Observer.equals = function(observer1, observer2) {
+ return observer1 == observer2 || observer1.equals(observer2);
+};
+
+
+/**
+ * Creates an observer that calls the given function.
+ * @param {function(!goog.labs.observe.Notice)} fn Function to be converted.
+ * @param {!Object=} opt_scope Optional scope to execute the function.
+ * @return {!goog.labs.observe.Observer} An observer object.
+ */
+goog.labs.observe.Observer.fromFunction = function(fn, opt_scope) {
+ return new goog.labs.observe.Observer.FunctionObserver_(fn, opt_scope);
+};
+
+
+
+/**
+ * An observer that calls the given function on {@code notify}.
+ * @param {function(!goog.labs.observe.Notice)} fn Function to delegate to.
+ * @param {!Object=} opt_scope Optional scope to execute the function.
+ * @constructor
+ * @implements {goog.labs.observe.Observer}
+ * @private
+ */
+goog.labs.observe.Observer.FunctionObserver_ = function(fn, opt_scope) {
+ this.fn_ = fn;
+ this.scope_ = opt_scope;
+};
+
+
+/** @override */
+goog.labs.observe.Observer.FunctionObserver_.prototype.notify = function(
+ notice) {
+ this.fn_.call(this.scope_, notice);
+};
+
+
+/** @override */
+goog.labs.observe.Observer.FunctionObserver_.prototype.equals = function(
+ observer) {
+ return this.fn_ === observer.fn_ && this.scope_ === observer.scope_;
+};
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/observe/observer_test.html b/contexts/data/lib/closure-library/closure/goog/labs/observe/observer_test.html
new file mode 100644
index 0000000..3b3e5d2
--- /dev/null
+++ b/contexts/data/lib/closure-library/closure/goog/labs/observe/observer_test.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<html>
+<!--
+Copyright 2012 The Closure Library Authors. All Rights Reserved.
+
+Use of this source code is governed by the Apache License, Version 2.0.
+See the COPYING file for details.
+-->
+<!--
+-->
+<head>
+<meta http-equiv="X-UA-Compatible" content="IE=edge">
+<title>Closure Unit Tests - goog.labs.observe.Observer</title>
+<script src="../../base.js"></script>
+<script>
+ goog.require('goog.labs.observe.Notice');
+ goog.require('goog.labs.observe.Observer');
+ goog.require('goog.testing.jsunit');
+ goog.require('goog.testing.recordFunction');
+</script>
+</head>
+<body>
+<script>
+var TYPE = 'testtype';
+
+
+function testToObserver() {
+ var expectedScope = this;
+ var notice = new goog.labs.observe.Notice(null, TYPE);
+ var fn = goog.testing.recordFunction(function(n) {
+ assertEquals('Function receives wrong notice object.', notice, n);
+ assertEquals('Function is executed with wrong scope.', expectedScope, this);
+ });
+ var observer = goog.labs.observe.Observer.fromFunction(fn);
+ observer.notify(notice);
+ assertEquals(1, fn.getCallCount());
+}
+
+
+function testToObserverWithScope() {
+ var expectedScope = {};
+ var notice = new goog.labs.observe.Notice(null, TYPE);
+ var fn = goog.testing.recordFunction(function(n) {
+ assertEquals('Function receives wrong notice object.', notice, n);
+ assertEquals('Function is executed with wrong scope.', expectedScope, this);
+ });
+ var observer = goog.labs.observe.Observer.fromFunction(fn, expectedScope);
+ observer.notify(notice);
+ assertEquals(1, fn.getCallCount());
+}
+
+
+function testToObserverEquals() {
+ var fn = function() {};
+ assertTrue(
+ goog.labs.observe.Observer.fromFunction(fn).equals(
+ goog.labs.observe.Observer.fromFunction(fn)));
+
+ var scope = {};
+ assertTrue(
+ goog.labs.observe.Observer.fromFunction(fn, scope).equals(
+ goog.labs.observe.Observer.fromFunction(fn, scope)));
+}
+</script>
+</body>
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/observe/simpleobservable.js b/contexts/data/lib/closure-library/closure/goog/labs/observe/simpleobservable.js
new file mode 100644
index 0000000..acc3a84
--- /dev/null
+++ b/contexts/data/lib/closure-library/closure/goog/labs/observe/simpleobservable.js
@@ -0,0 +1,129 @@
+// 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 implementation of {@code Observable} that can be
+ * used as base class or composed into another class that wants to
+ * implement {@code Observable}.
+ */
+
+goog.provide('goog.labs.observe.SimpleObservable');
+
+goog.require('goog.Disposable');
+goog.require('goog.array');
+goog.require('goog.asserts');
+goog.require('goog.labs.observe.Notice');
+goog.require('goog.labs.observe.Observable');
+goog.require('goog.labs.observe.Observer');
+goog.require('goog.object');
+
+
+
+/**
+ * A simple implementation of {@code goog.labs.observe.Observable} that can
+ * be used as a standalone observable or as a base class for other
+ * observable object.
+ *
+ * When another class wants to implement observable without extending
+ * {@code SimpleObservable}, they can create an instance of
+ * {@code SimpleObservable}, specifying {@code opt_actualObservable},
+ * and delegate to the instance. Here is a trivial example:
+ *
+ * <pre>
+ * ClassA = function() {
+ * goog.base(this);
+ * this.observable_ = new SimpleObservable(this);
+ * this.registerDisposable(this.observable_);
+ * };
+ * goog.inherits(ClassA, goog.Disposable);
+ *
+ * ClassA.prototype.observe = function(observer) {
+ * this.observable_.observe(observer);
+ * };
+ *
+ * ClassA.prototype.unobserve = function(observer) {
+ * this.observable_.unobserve(observer);
+ * };
+ *
+ * ClassA.prototype.notify = function(opt_data) {
+ * this.observable_.notify(opt_data);
+ * };
+ * </pre>
+ *
+ * @param {!goog.labs.observe.Observable=} opt_actualObservable
+ * Optional observable object. Defaults to 'this'. When used as
+ * base class, the parameter need not be given. It is only useful
+ * when using this class to implement implement {@code Observable}
+ * interface on another object, see example above.
+ * @constructor
+ * @implements {goog.labs.observe.Observable}
+ * @extends {goog.Disposable}
+ */
+goog.labs.observe.SimpleObservable = function(opt_actualObservable) {
+ goog.base(this);
+
+ /**
+ * @type {!goog.labs.observe.Observable}
+ * @private
+ */
+ this.actualObservable_ = opt_actualObservable || this;
+
+ /**
+ * Observers registered on this object.
+ * @type {!Array.<!goog.labs.observe.Observer>}
+ * @private
+ */
+ this.observers_ = [];
+};
+goog.inherits(goog.labs.observe.SimpleObservable, goog.Disposable);
+
+
+/** @override */
+goog.labs.observe.SimpleObservable.prototype.observe = function(observer) {
+ goog.asserts.assert(!this.isDisposed());
+
+ // Registers the (type, observer) only if it has not been previously
+ // registered.
+ var shouldRegisterObserver = !goog.array.some(this.observers_, goog.partial(
+ goog.labs.observe.Observer.equals, observer));
+ if (shouldRegisterObserver) {
+ this.observers_.push(observer);
+ }
+ return shouldRegisterObserver;
+};
+
+
+/** @override */
+goog.labs.observe.SimpleObservable.prototype.unobserve = function(observer) {
+ goog.asserts.assert(!this.isDisposed());
+ return goog.array.removeIf(this.observers_, goog.partial(
+ goog.labs.observe.Observer.equals, observer));
+};
+
+
+/** @override */
+goog.labs.observe.SimpleObservable.prototype.notify = function(opt_data) {
+ goog.asserts.assert(!this.isDisposed());
+ var notice = new goog.labs.observe.Notice(this.actualObservable_, opt_data);
+ goog.array.forEach(
+ goog.array.clone(this.observers_), function(observer) {
+ observer.notify(notice);
+ });
+};
+
+
+/** @override */
+goog.labs.observe.SimpleObservable.prototype.disposeInternal = function() {
+ this.observers_.length = 0;
+};
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/observe/simpleobservable_test.html b/contexts/data/lib/closure-library/closure/goog/labs/observe/simpleobservable_test.html
new file mode 100644
index 0000000..288b859
--- /dev/null
+++ b/contexts/data/lib/closure-library/closure/goog/labs/observe/simpleobservable_test.html
@@ -0,0 +1,196 @@
+<!DOCTYPE html>
+<html>
+<!--
+Copyright 2012 The Closure Library Authors. All Rights Reserved.
+
+Use of this source code is governed by the Apache License, Version 2.0.
+See the COPYING file for details.
+-->
+<!--
+-->
+<head>
+<meta http-equiv="X-UA-Compatible" content="IE=edge">
+<title>Closure Unit Tests - goog.labs.observe.SimpleObservable</title>
+<script src="../../base.js"></script>
+<script>
+ goog.require('goog.dispose');
+ goog.require('goog.labs.observe.Observer');
+ goog.require('goog.labs.observe.SimpleObservable');
+ goog.require('goog.testing.jsunit');
+ goog.require('goog.testing.recordFunction');
+</script>
+</head>
+<body>
+<script>
+var observable;
+
+
+function setUp() {
+ observable = new goog.labs.observe.SimpleObservable();
+}
+
+
+function tearDown() {
+ goog.dispose(observable);
+}
+
+
+function testObserve() {
+ var observerFn = goog.testing.recordFunction();
+ observable.observe(goog.labs.observe.Observer.fromFunction(observerFn));
+ observable.notify();
+ assertEquals(1, observerFn.getCallCount());
+}
+
+
+function testObserveWithTheSameObserver() {
+ var observerFn = goog.testing.recordFunction();
+ var observer = goog.labs.observe.Observer.fromFunction(observerFn);
+
+ assertTrue(observable.observe(observer));
+ assertFalse(observable.observe(observer));
+ observable.notify();
+ assertEquals(1, observerFn.getCallCount());
+ observerFn.reset();
+
+ // observer.equals(observer2) == true
+ var observer2 = goog.labs.observe.Observer.fromFunction(observerFn);
+ assertFalse(observable.observe(observer2));
+ observable.notify();
+ assertEquals(1, observerFn.getCallCount());
+}
+
+
+function testMultipleObservers() {
+ var observerFn = goog.testing.recordFunction();
+ var observerFn2 = goog.testing.recordFunction();
+ observable.observe(goog.labs.observe.Observer.fromFunction(observerFn));
+ observable.observe(goog.labs.observe.Observer.fromFunction(observerFn2));
+ observable.notify();
+ assertEquals(1, observerFn.getCallCount());
+ assertEquals(1, observerFn2.getCallCount());
+}
+
+
+function testUnobserve() {
+ var observerFn = goog.testing.recordFunction();
+ var observer = goog.labs.observe.Observer.fromFunction(observerFn);
+ observable.observe(observer);
+ assertTrue(observable.unobserve(observer));
+ observable.notify();
+ assertEquals(0, observerFn.getCallCount());
+ assertFalse(observable.unobserve(observer));
+}
+
+
+function testNotice() {
+ var observerFn = goog.testing.recordFunction(function(notice) {
+ assertNotNull(notice);
+ assertEquals(observable, notice.getObservable());
+ assertUndefined(notice.getData());
+ });
+
+ observable.observe(goog.labs.observe.Observer.fromFunction(observerFn));
+ observable.notify();
+ assertEquals(1, observerFn.getCallCount());
+}
+
+
+function testNoticeWithData() {
+ var data = {foo: 1};
+ var observerFn = goog.testing.recordFunction(function(notice) {
+ assertNotNull(notice);
+ assertEquals(observable, notice.getObservable());
+ assertEquals(data, notice.getData());
+ });
+
+ observable.observe(goog.labs.observe.Observer.fromFunction(observerFn));
+ observable.notify(data);
+ assertEquals(1, observerFn.getCallCount());
+}
+
+
+function testUnobserveWhileObserverIsFiring() {
+ var observerFn = goog.testing.recordFunction(function() {
+ observable.unobserve(observer);
+ assertFalse(observable.unobserve(observer));
+ });
+ var observer = goog.labs.observe.Observer.fromFunction(observerFn);
+ var observerFn2 = goog.testing.recordFunction();
+ observable.observe(observer);
+ observable.observe(goog.labs.observe.Observer.fromFunction(observerFn2));
+
+ observable.notify();
+ assertEquals(1, observerFn.getCallCount());
+ assertEquals(1, observerFn2.getCallCount());
+
+ observable.notify();
+ assertEquals(1, observerFn.getCallCount());
+ assertEquals(2, observerFn2.getCallCount());
+}
+
+
+function testSimpleObservableAsComposition() {
+ var observable = new ActualObservable();
+
+ var observerFn = goog.testing.recordFunction(function(notice) {
+ assertNotNull(notice);
+ assertEquals(observable, notice.getObservable());
+ assertUndefined(notice.getData());
+ });
+ var observer = goog.labs.observe.Observer.fromFunction(observerFn);
+
+ observable.observe(observer);
+ observable.notify();
+ assertEquals(1, observerFn.getCallCount());
+ observerFn.reset();
+}
+
+
+function testDispose() {
+ var observerFn = function() {};
+ var observer = goog.labs.observe.Observer.fromFunction(observerFn);
+
+ observable.dispose();
+ assertThrows(function() {
+ observable.observe(observer);
+ });
+ assertThrows(function() {
+ observable.notify();
+ });
+ assertThrows(function() {
+ observable.observe(observer);
+ });
+}
+
+
+
+/**
+ * @constructor
+ * @extends {goog.Disposable}
+ */
+ActualObservable = function() {
+ goog.base(this);
+ this.observable_ = new goog.labs.observe.SimpleObservable(this);
+ this.registerDisposable(this.observable_);
+};
+goog.inherits(ActualObservable, goog.Disposable);
+
+
+ActualObservable.prototype.observe = function(type, observer) {
+ this.observable_.observe(type, observer);
+};
+
+
+ActualObservable.prototype.unobserve = function(type, observer) {
+ this.observable_.unobserve(type, observer);
+};
+
+
+ActualObservable.prototype.notify = function(type, opt_data) {
+ this.observable_.notify(type, opt_data);
+};
+
+</script>
+</body>
+</html>
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/structs/map.js b/contexts/data/lib/closure-library/closure/goog/labs/structs/map.js
new file mode 100644
index 0000000..0c030d8
--- /dev/null
+++ b/contexts/data/lib/closure-library/closure/goog/labs/structs/map.js
@@ -0,0 +1,339 @@
+// 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 A map data structure that offers a convenient API to
+ * manipulate a key, value map. The key must be a string.
+ *
+ * This implementation also ensure that you can use keys that would
+ * not be usable using a normal object literal {}. Some examples
+ * include __proto__ (all newer browsers), toString/hasOwnProperty (IE
+ * <= 8).
+ */
+
+goog.provide('goog.labs.structs.Map');
+
+goog.require('goog.array');
+goog.require('goog.asserts');
+goog.require('goog.labs.object');
+goog.require('goog.object');
+
+
+
+/**
+ * Creates a new map.
+ * @constructor
+ */
+goog.labs.structs.Map = function() {
+ // clear() initializes the map to the empty state.
+ this.clear();
+};
+
+
+/**
+ * @type {function(this: Object, string): boolean}
+ * @private
+ */
+goog.labs.structs.Map.objectPropertyIsEnumerable_ =
+ Object.prototype.propertyIsEnumerable;
+
+
+/**
+ * @type {function(this: Object, string): boolean}
+ * @private
+ */
+goog.labs.structs.Map.objectHasOwnProperty_ =
+ Object.prototype.hasOwnProperty;
+
+
+/**
+ * Primary backing store of this map.
+ * @type {!Object}
+ * @private
+ */
+goog.labs.structs.Map.prototype.map_;
+
+
+/**
+ * Secondary backing store for keys. The index corresponds to the
+ * index for secondaryStoreValues_.
+ * @type {!Array.<string>}
+ * @private
+ */
+goog.labs.structs.Map.prototype.secondaryStoreKeys_;
+
+
+/**
+ * Secondary backing store for keys. The index corresponds to the
+ * index for secondaryStoreValues_.
+ * @type {!Array.<*>}
+ * @private
+ */
+goog.labs.structs.Map.prototype.secondaryStoreValues_;
+
+
+/**
+ * Adds the (key, value) pair, overriding previous entry with the same
+ * key, if any.
+ * @param {string} key The key.
+ * @param {*} value The value.
+ */
+goog.labs.structs.Map.prototype.set = function(key, value) {
+ this.assertKeyIsString_(key);
+
+ var newKey = !this.hasKeyInPrimaryStore_(key);
+ this.map_[key] = value;
+
+ // __proto__ is not settable on object.
+ if (key == '__proto__' ||
+ // Shadows for built-in properties are not enumerable in IE <= 8 .
+ (!goog.labs.structs.Map.BrowserFeature.OBJECT_CREATE_SUPPORTED &&
+ !goog.labs.structs.Map.objectPropertyIsEnumerable_.call(
+ this.map_, key))) {
+ delete this.map_[key];
+ var index = goog.array.indexOf(this.secondaryStoreKeys_, key);
+ if ((newKey = index < 0)) {
+ index = this.secondaryStoreKeys_.length;
+ }
+
+ this.secondaryStoreKeys_[index] = key;
+ this.secondaryStoreValues_[index] = value;
+ }
+
+ if (newKey) this.count_++;
+};
+
+
+/**
+ * Gets the value for the given key.
+ * @param {string} key The key whose value we want to retrieve.
+ * @param {*=} opt_default The default value to return if the key does
+ * not exist in the map, default to undefined.
+ * @return {*} The value corresponding to the given key, or opt_default
+ * if the key does not exist in this map.
+ */
+goog.labs.structs.Map.prototype.get = function(key, opt_default) {
+ this.assertKeyIsString_(key);
+
+ if (this.hasKeyInPrimaryStore_(key)) {
+ return this.map_[key];
+ }
+
+ var index = goog.array.indexOf(this.secondaryStoreKeys_, key);
+ return index >= 0 ? this.secondaryStoreValues_[index] : opt_default;
+};
+
+
+/**
+ * Removes the map entry with the given key.
+ * @param {string} key The key to remove.
+ * @return {boolean} True if the entry is removed.
+ */
+goog.labs.structs.Map.prototype.remove = function(key) {
+ this.assertKeyIsString_(key);
+
+ if (this.hasKeyInPrimaryStore_(key)) {
+ this.count_--;
+ delete this.map_[key];
+ return true;
+ } else {
+ var index = goog.array.indexOf(this.secondaryStoreKeys_, key);
+ if (index >= 0) {
+ this.count_--;
+ goog.array.removeAt(this.secondaryStoreKeys_, index);
+ goog.array.removeAt(this.secondaryStoreValues_, index);
+ return true;
+ }
+ }
+ return false;
+};
+
+
+/**
+ * Adds the content of the map to this map. If a new entry uses a key
+ * that already exists in this map, the existing key is replaced.
+ * @param {!goog.labs.structs.Map} map The map to add.
+ */
+goog.labs.structs.Map.prototype.addAll = function(map) {
+ goog.array.forEach(map.getKeys(), function(key) {
+ this.set(key, map.get(key));
+ }, this);
+};
+
+
+/**
+ * @return {boolean} True if the map is empty.
+ */
+goog.labs.structs.Map.prototype.isEmpty = function() {
+ return !this.count_;
+};
+
+
+/**
+ * @return {number} The number of the entries in this map.
+ */
+goog.labs.structs.Map.prototype.getCount = function() {
+ return this.count_;
+};
+
+
+/**
+ * @param {string} key The key to check.
+ * @return {boolean} True if the map contains the given key.
+ */
+goog.labs.structs.Map.prototype.containsKey = function(key) {
+ this.assertKeyIsString_(key);
+ return this.hasKeyInPrimaryStore_(key) ||
+ goog.array.contains(this.secondaryStoreKeys_, key);
+};
+
+
+/**
+ * Whether the map contains the given value. The comparison is done
+ * using !== comparator. Also returns true if the passed value is NaN
+ * and a NaN value exists in the map.
+ * @param {*} value Value to check.
+ * @return {boolean} True if the map contains the given value.
+ */
+goog.labs.structs.Map.prototype.containsValue = function(value) {
+ var found = goog.object.some(this.map_, function(v, k) {
+ return this.hasKeyInPrimaryStore_(k) &&
+ goog.labs.object.is(v, value);
+ }, this);
+ return found || goog.array.contains(this.secondaryStoreValues_, value);
+};
+
+
+/**
+ * @return {!Array.<string>} An array of all the keys contained in this map.
+ */
+goog.labs.structs.Map.prototype.getKeys = function() {
+ var keys;
+ if (goog.labs.structs.Map.BrowserFeature.OBJECT_KEYS_SUPPORTED) {
+ keys = goog.array.clone(Object.keys(this.map_));
+ } else {
+ keys = [];
+ for (var key in this.map_) {
+ if (goog.labs.structs.Map.objectHasOwnProperty_.call(this.map_, key)) {
+ keys.push(key);
+ }
+ }
+ }
+
+ goog.array.extend(keys, this.secondaryStoreKeys_);
+ return keys;
+};
+
+
+/**
+ * @return {!Array.<*>} An array of all the values contained in this map.
+ * There may be duplicates.
+ */
+goog.labs.structs.Map.prototype.getValues = function() {
+ var values = [];
+ var keys = this.getKeys();
+ for (var i = 0; i < keys.length; i++) {
+ values.push(this.get(keys[i]));
+ }
+ return values;
+};
+
+
+/**
+ * @return {!Array.<Array>} An array of entries. Each entry is of the
+ * form [key, value]. Do not rely on consistent ordering of entries.
+ */
+goog.labs.structs.Map.prototype.getEntries = function() {
+ var entries = [];
+ var keys = this.getKeys();
+ for (var i = 0; i < keys.length; i++) {
+ var key = keys[i];
+ entries.push([key, this.get(key)]);
+ }
+ return entries;
+};
+
+
+/**
+ * Clears the map to the initial state.
+ */
+goog.labs.structs.Map.prototype.clear = function() {
+ this.map_ = goog.labs.structs.Map.BrowserFeature.OBJECT_CREATE_SUPPORTED ?
+ Object.create(null) : {};
+ this.secondaryStoreKeys_ = [];
+ this.secondaryStoreValues_ = [];
+ this.count_ = 0;
+};
+
+
+/**
+ * Clones this map.
+ * @return {!goog.labs.structs.Map} The clone of this map.
+ */
+goog.labs.structs.Map.prototype.clone = function() {
+ var map = new goog.labs.structs.Map();
+ map.addAll(this);
+ return map;
+};
+
+
+/**
+ * @param {string} key The key to check.
+ * @return {boolean} True if the given key has been added successfully
+ * to the primary store.
+ * @private
+ */
+goog.labs.structs.Map.prototype.hasKeyInPrimaryStore_ = function(key) {
+ // New browsers that support Object.create do not allow setting of
+ // __proto__. In other browsers, hasOwnProperty will return true for
+ // __proto__ for object created with literal {}, so we need to
+ // special case it.
+ if (key == '__proto__') {
+ return false;
+ }
+
+ if (goog.labs.structs.Map.BrowserFeature.OBJECT_CREATE_SUPPORTED) {
+ return key in this.map_;
+ }
+
+ return goog.labs.structs.Map.objectHasOwnProperty_.call(this.map_, key);
+};
+
+
+/**
+ * Asserts that the given key is a string.
+ * @param {string} key The key to check.
+ * @private
+ */
+goog.labs.structs.Map.prototype.assertKeyIsString_ = function(key) {
+ goog.asserts.assert(goog.isString(key), 'key must be a string.');
+};
+
+
+/**
+ * Browser feature enum necessary for map.
+ * @enum {boolean}
+ */
+goog.labs.structs.Map.BrowserFeature = {
+ // TODO(user): Replace with goog.userAgent detection.
+ /**
+ * Whether Object.create method is supported.
+ */
+ OBJECT_CREATE_SUPPORTED: !!Object.create,
+
+ /**
+ * Whether Object.keys method is supported.
+ */
+ OBJECT_KEYS_SUPPORTED: !!Object.keys
+};
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/structs/map_perf.js b/contexts/data/lib/closure-library/closure/goog/labs/structs/map_perf.js
new file mode 100644
index 0000000..1bc9d7a
--- /dev/null
+++ b/contexts/data/lib/closure-library/closure/goog/labs/structs/map_perf.js
@@ -0,0 +1,201 @@
+// 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 Performance test for goog.structs.Map and
+ * goog.labs.structs.Map. To run this test fairly, you would have to
+ * compile this via JsCompiler (with --export_test_functions), and
+ * pull the compiled JS into an empty HTML file.
+ */
+
+goog.provide('goog.labs.structs.mapPerf');
+
+goog.require('goog.dom');
+goog.require('goog.labs.structs.Map');
+goog.require('goog.structs.Map');
+goog.require('goog.testing.PerformanceTable');
+goog.require('goog.testing.jsunit');
+
+goog.scope(function() {
+var mapPerf = goog.labs.structs.mapPerf;
+
+
+/**
+ * @typedef {goog.labs.structs.Map|goog.structs.Map}
+ */
+mapPerf.MapType;
+
+
+/**
+ * @type {goog.testing.PerformanceTable}
+ */
+mapPerf.perfTable;
+
+
+/**
+ * A key list. This maps loop index to key name to be used during
+ * benchmark. This ensure that we do not need to pay the cost of
+ * string concatenation/GC whenever we derive a key from loop index.
+ *
+ * This is filled once in setUpPage and then remain unchanged for the
+ * rest of the test case.
+ *
+ * @type {Array}
+ */
+mapPerf.keyList = [];
+
+
+/**
+ * Maxium number of keys in keyList (and, by extension, the map under
+ * test).
+ * @type {number}
+ */
+mapPerf.MAX_NUM_KEY = 10000;
+
+
+/**
+ * Fills the given map with generated key-value pair.
+ * @param {mapPerf.MapType} map The map to fill.
+ * @param {number} numKeys The number of key-value pair to fill.
+ */
+mapPerf.fillMap = function(map, numKeys) {
+ goog.asserts.assert(numKeys <= mapPerf.MAX_NUM_KEY);
+
+ for (var i = 0; i < numKeys; ++i) {
+ map.set(mapPerf.keyList[i], i);
+ }
+};
+
+
+/**
+ * Primes the given map with deletion of keys.
+ * @param {mapPerf.MapType} map The map to prime.
+ * @return {mapPerf.MapType} The primed map (for chaining).
+ */
+mapPerf.primeMapWithDeletion = function(map) {
+ for (var i = 0; i < 1000; ++i) {
+ map.set(mapPerf.keyList[i], i);
+ }
+ for (var i = 0; i < 1000; ++i) {
+ map.remove(mapPerf.keyList[i]);
+ }
+ return map;
+};
+
+
+/**
+ * Runs performance test for Map#get with the given map.
+ * @param {mapPerf.MapType} map The map to stress.
+ * @param {string} message Message to be put in performance table.
+ */
+mapPerf.runPerformanceTestForMapGet = function(map, message) {
+ mapPerf.fillMap(map, 10000);
+
+ mapPerf.perfTable.run(
+ function() {
+ // Creates local alias for map and keyList.
+ var localMap = map;
+ var localKeyList = mapPerf.keyList;
+
+ for (var i = 0; i < 500; ++i) {
+ var sum = 0;
+ for (var j = 0; j < 10000; ++j) {
+ sum += localMap.get(localKeyList[j]);
+ }
+ }
+ },
+ message);
+};
+
+
+/**
+ * Runs performance test for Map#set with the given map.
+ * @param {mapPerf.MapType} map The map to stress.
+ * @param {string} message Message to be put in performance table.
+ */
+mapPerf.runPerformanceTestForMapSet = function(map, message) {
+ mapPerf.perfTable.run(
+ function() {
+ // Creates local alias for map and keyList.
+ var localMap = map;
+ var localKeyList = mapPerf.keyList;
+
+ for (var i = 0; i < 500; ++i) {
+ for (var j = 0; j < 10000; ++j) {
+ localMap.set(localKeyList[i], i);
+ }
+ }
+ },
+ message);
+};
+
+
+goog.global['setUpPage'] = function() {
+ var content = goog.dom.createDom('div');
+ goog.dom.insertChildAt(document.body, content, 0);
+ var ua = navigator.userAgent;
+ content.innerHTML =
+ '<h1>Closure Performance Tests - Map</h1>' +
+ '<p><strong>User-agent: </strong><span id="ua">' + ua + '</span></p>' +
+ '<div id="perf-table"></div>' +
+ '<hr>';
+
+ mapPerf.perfTable = new goog.testing.PerformanceTable(
+ goog.dom.getElement('perf-table'));
+
+ // Fills keyList.
+ for (var i = 0; i < mapPerf.MAX_NUM_KEY; ++i) {
+ mapPerf.keyList.push('k' + i);
+ }
+};
+
+
+goog.global['testGetFromLabsMap'] = function() {
+ mapPerf.runPerformanceTestForMapGet(
+ new goog.labs.structs.Map(), '#get: no previous deletion (Labs)');
+};
+
+
+goog.global['testGetFromOriginalMap'] = function() {
+ mapPerf.runPerformanceTestForMapGet(
+ new goog.structs.Map(), '#get: no previous deletion (Original)');
+};
+
+
+goog.global['testGetWithPreviousDeletionFromLabsMap'] = function() {
+ mapPerf.runPerformanceTestForMapGet(
+ mapPerf.primeMapWithDeletion(new goog.labs.structs.Map()),
+ '#get: with previous deletion (Labs)');
+};
+
+
+goog.global['testGetWithPreviousDeletionFromOriginalMap'] = function() {
+ mapPerf.runPerformanceTestForMapGet(
+ mapPerf.primeMapWithDeletion(new goog.structs.Map()),
+ '#get: with previous deletion (Original)');
+};
+
+
+goog.global['testSetFromLabsMap'] = function() {
+ mapPerf.runPerformanceTestForMapSet(
+ new goog.labs.structs.Map(), '#set: no previous deletion (Labs)');
+};
+
+
+goog.global['testSetFromOriginalMap'] = function() {
+ mapPerf.runPerformanceTestForMapSet(
+ new goog.structs.Map(), '#set: no previous deletion (Original)');
+};
+
+}); // goog.scope
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/structs/map_test.html b/contexts/data/lib/closure-library/closure/goog/labs/structs/map_test.html
new file mode 100644
index 0000000..9a4d375
--- /dev/null
+++ b/contexts/data/lib/closure-library/closure/goog/labs/structs/map_test.html
@@ -0,0 +1,434 @@
+<!DOCTYPE html>
+<html>
+<!--
+Copyright 2012 The Closure Library Authors. All Rights Reserved.
+
+Use of this source code is governed by the Apache License, Version 2.0.
+See the COPYING file for details.
+-->
+<!--
+-->
+<head>
+<meta http-equiv="X-UA-Compatible" content="IE=edge">
+<title>Closure Unit Tests - goog.labs.structs.Map</title>
+<script src="../../base.js"></script>
+<script>
+goog.require('goog.dispose');
+goog.require('goog.labs.structs.Map');
+goog.require('goog.testing.PropertyReplacer');
+goog.require('goog.testing.jsunit');
+</script>
+</head>
+<body>
+<script>
+var map;
+var stubs = new goog.testing.PropertyReplacer();
+
+
+function setUp() {
+ map = new goog.labs.structs.Map();
+}
+
+
+function testSet() {
+ var key = 'test';
+ var value = 'value';
+ map.set(key, value);
+ assertEquals(value, map.get(key));
+}
+
+
+function testSetsWithSameKey() {
+ var key = 'test';
+ var value = 'value';
+ var value2 = 'value2';
+ map.set(key, value);
+ map.set(key, value2);
+ assertEquals(value2, map.get(key));
+}
+
+
+function testSetWithUndefinedValue() {
+ var key = 'test';
+ map.set(key, undefined);
+ assertUndefined(map.get(key));
+}
+
+
+function testSetWithUnderUnderProtoUnderUnder() {
+ var key = '__proto__';
+ var value = 'value';
+ var value2 = 'value2';
+
+ map.set(key, value);
+ assertEquals(value, map.get(key));
+
+ map.set(key, value2);
+ assertEquals(value2, map.get(key));
+}
+
+
+function testSetWithBuiltInPropertyShadows() {
+ var key = 'toString';
+ var value = 'value';
+ var key2 = 'hasOwnProperty';
+ var value2 = 'value2';
+
+ map.set(key, value);
+ map.set(key2, value2);
+ assertEquals(value, map.get(key));
+ assertEquals(value2, map.get(key2));
+
+ map.set(key, value2);
+ map.set(key2, value);
+ assertEquals(value2, map.get(key));
+ assertEquals(value, map.get(key2));
+}
+
+
+function testGetBeforeSetOfUnderUnderProtoUnderUnder() {
+ assertUndefined(map.get('__proto__'));
+}
+
+
+function testContainsKey() {
+ assertFalse(map.containsKey('key'));
+ assertFalse(map.containsKey('__proto__'));
+ assertFalse(map.containsKey('toString'));
+ assertFalse(map.containsKey('hasOwnProperty'));
+ assertFalse(map.containsKey('key2'));
+ assertFalse(map.containsKey('key3'));
+ assertFalse(map.containsKey('key4'));
+
+ map.set('key', 'v');
+ map.set('__proto__', 'v');
+ map.set('toString', 'v');
+ map.set('hasOwnProperty', 'v');
+ map.set('key2', undefined);
+ map.set('key3', null);
+ map.set('key4', '');
+
+ assertTrue(map.containsKey('key'));
+ assertTrue(map.containsKey('__proto__'));
+ assertTrue(map.containsKey('toString'));
+ assertTrue(map.containsKey('hasOwnProperty'));
+ assertTrue(map.containsKey('key2'));
+ assertTrue(map.containsKey('key3'));
+ assertTrue(map.containsKey('key4'));
+}
+
+
+function testContainsValueWithShadowKeys() {
+ assertFalse(map.containsValue('v2'));
+ assertFalse(map.containsValue('v3'));
+ assertFalse(map.containsValue('v4'));
+
+ map.set('__proto__', 'v2');
+ map.set('toString', 'v3');
+ map.set('hasOwnProperty', 'v4');
+
+ assertTrue(map.containsValue('v2'));
+ assertTrue(map.containsValue('v3'));
+ assertTrue(map.containsValue('v4'));
+
+ assertFalse(map.containsValue(Object.prototype.toString));
+ assertFalse(map.containsValue(Object.prototype.hasOwnProperty));
+}
+
+
+function testContainsValueWithNullAndUndefined() {
+ assertFalse(map.containsValue(undefined));
+ assertFalse(map.containsValue(null));
+
+ map.set('key2', undefined);
+ map.set('key3', null);
+
+ assertTrue(map.containsValue(undefined));
+ assertTrue(map.containsValue(null));
+}
+
+
+function testContainsValueWithNumber() {
+ assertFalse(map.containsValue(-1));
+ assertFalse(map.containsValue(0));
+ assertFalse(map.containsValue(1));
+ map.set('key', -1);
+ map.set('key2', 0);
+ map.set('key3', 1);
+ assertTrue(map.containsValue(-1));
+ assertTrue(map.containsValue(0));
+ assertTrue(map.containsValue(1));
+}
+
+
+function testContainsValueWithNaN() {
+ assertFalse(map.containsValue(NaN));
+ map.set('key', NaN);
+ assertTrue(map.containsValue(NaN));
+}
+
+
+function testContainsValueWithNegativeZero() {
+ assertFalse(map.containsValue(-0));
+ map.set('key', -0);
+ assertTrue(map.containsValue(-0));
+ assertFalse(map.containsValue(0));
+
+ map.set('key', 0);
+ assertFalse(map.containsValue(-0));
+ assertTrue(map.containsValue(0));
+}
+
+
+function testContainsValueWithStrings() {
+ assertFalse(map.containsValue(''));
+ assertFalse(map.containsValue('v'));
+ map.set('key', '');
+ map.set('key2', 'v');
+ assertTrue(map.containsValue(''));
+ assertTrue(map.containsValue('v'));
+}
+
+function testRemove() {
+ map.set('key', 'v');
+ map.set('__proto__', 'v2');
+ map.set('toString', 'v3');
+ map.set('hasOwnProperty', 'v4');
+ map.set('key2', undefined);
+ map.set('key3', null);
+ map.set('key4', '');
+
+ assertFalse(map.remove('key do not exist'));
+
+ assertTrue(map.remove('key'));
+ assertFalse(map.containsKey('key'));
+ assertFalse(map.remove('key'));
+
+ assertTrue(map.remove('__proto__'));
+ assertFalse(map.containsKey('__proto__'));
+ assertFalse(map.remove('__proto__'));
+
+ assertTrue(map.remove('toString'));
+ assertFalse(map.containsKey('toString'));
+ assertFalse(map.remove('toString'));
+
+ assertTrue(map.remove('hasOwnProperty'));
+ assertFalse(map.containsKey('hasOwnProperty'));
+ assertFalse(map.remove('hasOwnProperty'));
+
+ assertTrue(map.remove('key2'));
+ assertFalse(map.containsKey('key2'));
+ assertFalse(map.remove('key2'));
+
+ assertTrue(map.remove('key3'));
+ assertFalse(map.containsKey('key3'));
+ assertFalse(map.remove('key3'));
+
+ assertTrue('', map.remove('key4'));
+ assertFalse(map.containsKey('key4'));
+ assertFalse(map.remove('key4'));
+}
+
+
+function testGetCountAndIsEmpty() {
+ assertEquals(0, map.getCount());
+ assertTrue(map.isEmpty());
+
+ map.set('key', 'v');
+ assertEquals(1, map.getCount());
+ map.set('__proto__', 'v2');
+ assertEquals(2, map.getCount());
+ map.set('toString', 'v3');
+ assertEquals(3, map.getCount());
+ map.set('hasOwnProperty', 'v4');
+ assertEquals(4, map.getCount());
+
+ map.set('key', 'a');
+ assertEquals(4, map.getCount());
+ map.set('__proto__', 'a2');
+ assertEquals(4, map.getCount());
+ map.set('toString', 'a3');
+ assertEquals(4, map.getCount());
+ map.set('hasOwnProperty', 'a4');
+ assertEquals(4, map.getCount());
+
+ map.remove('key');
+ assertEquals(3, map.getCount());
+ map.remove('__proto__');
+ assertEquals(2, map.getCount());
+ map.remove('toString');
+ assertEquals(1, map.getCount());
+ map.remove('hasOwnProperty');
+ assertEquals(0, map.getCount());
+}
+
+
+function testClear() {
+ map.set('key', 'v');
+ map.set('__proto__', 'v');
+ map.set('toString', 'v');
+ map.set('hasOwnProperty', 'v');
+ map.set('key2', undefined);
+ map.set('key3', null);
+ map.set('key4', '');
+
+ map.clear();
+
+ assertFalse(map.containsKey('key'));
+ assertFalse(map.containsKey('__proto__'));
+ assertFalse(map.containsKey('toString'));
+ assertFalse(map.containsKey('hasOwnProperty'));
+ assertFalse(map.containsKey('key2'));
+ assertFalse(map.containsKey('key3'));
+ assertFalse(map.containsKey('key4'));
+}
+
+
+function testGetEntries() {
+ map.set('key', 'v');
+ map.set('__proto__', 'v');
+ map.set('toString', 'v');
+ map.set('hasOwnProperty', 'v');
+ map.set('key2', undefined);
+ map.set('key3', null);
+ map.set('key4', '');
+
+ var entries = map.getEntries();
+ assertEquals(7, entries.length);
+ assertContainsEntry(['key', 'v'], entries);
+ assertContainsEntry(['__proto__', 'v'], entries);
+ assertContainsEntry(['toString', 'v'], entries);
+ assertContainsEntry(['hasOwnProperty', 'v'], entries);
+ assertContainsEntry(['key2', undefined], entries);
+ assertContainsEntry(['key3', null], entries);
+ assertContainsEntry(['key4', ''], entries);
+}
+
+
+function testGetKeys() {
+ map.set('key', 'v');
+ map.set('__proto__', 'v');
+ map.set('toString', 'v');
+ map.set('hasOwnProperty', 'v');
+ map.set('key2', undefined);
+ map.set('key3', null);
+ map.set('k4', '');
+
+ var values = map.getKeys();
+ assertSameElements(
+ ['key', '__proto__', 'toString', 'hasOwnProperty', 'key2', 'key3', 'k4'],
+ values);
+}
+
+
+function testGetValues() {
+ map.set('key', 'v');
+ map.set('__proto__', 'v');
+ map.set('toString', 'v');
+ map.set('hasOwnProperty', 'v');
+ map.set('key2', undefined);
+ map.set('key3', null);
+ map.set('key4', '');
+
+ var values = map.getValues();
+ assertSameElements(['v', 'v', 'v', 'v', undefined, null, ''], values);
+}
+
+
+function testAddAllToEmptyMap() {
+ map.set('key', 'v');
+ map.set('key2', 'v2');
+ map.set('key3', 'v3');
+ map.set('key4', 'v4');
+
+ var map2 = new goog.labs.structs.Map();
+ map2.addAll(map);
+
+ assertEquals(4, map2.getCount());
+ assertEquals('v', map2.get('key'));
+ assertEquals('v2', map2.get('key2'));
+ assertEquals('v3', map2.get('key3'));
+ assertEquals('v4', map2.get('key4'));
+}
+
+
+function testAddAllToNonEmptyMap() {
+ map.set('key', 'v');
+ map.set('key2', 'v2');
+ map.set('key3', 'v3');
+ map.set('key4', 'v4');
+
+ var map2 = new goog.labs.structs.Map();
+ map2.set('key0', 'o');
+ map2.set('key', 'o');
+ map2.set('key2', 'o2');
+ map2.set('key3', 'o3');
+ map2.addAll(map);
+
+ assertEquals(5, map2.getCount());
+ assertEquals('o', map2.get('key0'));
+ assertEquals('v', map2.get('key'));
+ assertEquals('v2', map2.get('key2'));
+ assertEquals('v3', map2.get('key3'));
+ assertEquals('v4', map2.get('key4'));
+}
+
+
+function testClone() {
+ map.set('key', 'v');
+ map.set('key2', 'v2');
+ map.set('key3', 'v3');
+ map.set('key4', 'v4');
+
+ var map2 = map.clone();
+
+ assertEquals(4, map2.getCount());
+ assertEquals('v', map2.get('key'));
+ assertEquals('v2', map2.get('key2'));
+ assertEquals('v3', map2.get('key3'));
+ assertEquals('v4', map2.get('key4'));
+}
+
+
+function testMapWithModifiedObjectPrototype() {
+ stubs.set(Object.prototype, 'toString', function() {});
+ stubs.set(Object.prototype, 'foo', function() {});
+ stubs.set(Object.prototype, 'field', 100);
+ stubs.set(Object.prototype, 'fooKey', function() {});
+
+ map = new goog.labs.structs.Map();
+ map.set('key', 'v');
+ map.set('key2', 'v2');
+ map.set('fooKey', 'v3');
+
+ assertTrue(map.containsKey('key'));
+ assertTrue(map.containsKey('key2'));
+ assertTrue(map.containsKey('fooKey'));
+ assertFalse(map.containsKey('toString'));
+ assertFalse(map.containsKey('foo'));
+ assertFalse(map.containsKey('field'));
+
+ assertTrue(map.containsValue('v'));
+ assertTrue(map.containsValue('v2'));
+ assertTrue(map.containsValue('v3'));
+ assertFalse(map.containsValue(100));
+
+ var entries = map.getEntries();
+ assertEquals(3, entries.length);
+ assertContainsEntry(['key', 'v'], entries);
+ assertContainsEntry(['key2', 'v2'], entries);
+ assertContainsEntry(['fooKey', 'v3'], entries);
+}
+
+
+function assertContainsEntry(entry, entryList) {
+ for (var i = 0; i < entryList.length; ++i) {
+ if (entry[0] == entryList[i][0] && entry[1] === entryList[i][1]) {
+ return;
+ }
+ }
+ fail('Did not find entry: ' + entry + ' in: ' + entryList);
+}
+</script>
+</body>
+</html>
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/structs/multimap.js b/contexts/data/lib/closure-library/closure/goog/labs/structs/multimap.js
new file mode 100644
index 0000000..d7399f9
--- /dev/null
+++ b/contexts/data/lib/closure-library/closure/goog/labs/structs/multimap.js
@@ -0,0 +1,279 @@
+// 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 A collection similar to
+ * {@code goog.labs.structs.Map}, but also allows associating multiple
+ * values with a single key.
+ *
+ * This implementation ensures that you can use any string keys.
+ *
+ */
+
+goog.provide('goog.labs.structs.Multimap');
+
+goog.require('goog.array');
+goog.require('goog.labs.object');
+goog.require('goog.labs.structs.Map');
+
+
+
+/**
+ * Creates a new multimap.
+ * @constructor
+ */
+goog.labs.structs.Multimap = function() {
+ this.clear();
+};
+
+
+/**
+ * The backing map.
+ * @type {!goog.labs.structs.Map}
+ * @private
+ */
+goog.labs.structs.Multimap.prototype.map_;
+
+
+/**
+ * @type {number}
+ * @private
+ */
+goog.labs.structs.Multimap.prototype.count_ = 0;
+
+
+/**
+ * Clears the multimap.
+ */
+goog.labs.structs.Multimap.prototype.clear = function() {
+ this.count_ = 0;
+ this.map_ = new goog.labs.structs.Map();
+};
+
+
+/**
+ * Clones this multimap.
+ * @return {!goog.labs.structs.Multimap} A multimap that contains all
+ * the mapping this multimap has.
+ */
+goog.labs.structs.Multimap.prototype.clone = function() {
+ var map = new goog.labs.structs.Multimap();
+ map.addAllFromMultimap(this);
+ return map;
+};
+
+
+/**
+ * Adds the given (key, value) pair to the map. The (key, value) pair
+ * is guaranteed to be added.
+ * @param {string} key The key to add.
+ * @param {*} value The value to add.
+ */
+goog.labs.structs.Multimap.prototype.add = function(key, value) {
+ var values = this.map_.get(key);
+ if (!values) {
+ this.map_.set(key, (values = []));
+ }
+
+ values.push(value);
+ this.count_++;
+};
+
+
+/**
+ * Stores a collection of values to the given key. Does not replace
+ * existing (key, value) pairs.
+ * @param {string} key The key to add.
+ * @param {!Array.<*>} values The values to add.
+ */
+goog.labs.structs.Multimap.prototype.addAllValues = function(key, values) {
+ goog.array.forEach(values, function(v) {
+ this.add(key, v);
+ }, this);
+};
+
+
+/**
+ * Adds the contents of the given map/multimap to this multimap.
+ * @param {!(goog.labs.structs.Map|goog.labs.structs.Multimap)} map The
+ * map to add.
+ */
+goog.labs.structs.Multimap.prototype.addAllFromMultimap = function(map) {
+ goog.array.forEach(map.getEntries(), function(entry) {
+ this.add(entry[0], entry[1]);
+ }, this);
+};
+
+
+/**
+ * Replaces all the values for the given key with the given values.
+ * @param {string} key The key whose values are to be replaced.
+ * @param {!Array.<*>} values The new values. If empty, this is
+ * equivalent to {@code removaAll(key)}.
+ */
+goog.labs.structs.Multimap.prototype.replaceValues = function(key, values) {
+ this.removeAll(key);
+ this.addAllValues(key, values);
+};
+
+
+/**
+ * Gets the values correspond to the given key.
+ * @param {string} key The key to retrieve.
+ * @return {!Array.<*>} An array of values corresponding to the given
+ * key. May be empty. Note that the ordering of values are not
+ * guaranteed to be consistent.
+ */
+goog.labs.structs.Multimap.prototype.get = function(key) {
+ var values = /** @type {Array.<string>} */ (this.map_.get(key));
+ return values ? goog.array.clone(values) : [];
+};
+
+
+/**
+ * Removes a single occurrence of (key, value) pair.
+ * @param {string} key The key to remove.
+ * @param {*} value The value to remove.
+ * @return {boolean} Whether any matching (key, value) pair is removed.
+ */
+goog.labs.structs.Multimap.prototype.remove = function(key, value) {
+ var values = /** @type {Array.<string>} */ (this.map_.get(key));
+ if (!values) {
+ return false;
+ }
+
+ var removed = goog.array.removeIf(values, function(v) {
+ return goog.labs.object.is(value, v);
+ });
+
+ if (removed) {
+ this.count_--;
+ if (values.length == 0) {
+ this.map_.remove(key);
+ }
+ }
+ return removed;
+};
+
+
+/**
+ * Removes all values corresponding to the given key.
+ * @param {string} key The key whose values are to be removed.
+ * @return {boolean} Whether any value is removed.
+ */
+goog.labs.structs.Multimap.prototype.removeAll = function(key) {
+ // We have to first retrieve the values from the backing map because
+ // we need to keep track of count (and correctly calculates the
+ // return value). values may be undefined.
+ var values = this.map_.get(key);
+ if (this.map_.remove(key)) {
+ this.count_ -= values.length;
+ return true;
+ }
+
+ return false;
+};
+
+
+/**
+ * @return {boolean} Whether the multimap is empty.
+ */
+goog.labs.structs.Multimap.prototype.isEmpty = function() {
+ return !this.count_;
+};
+
+
+/**
+ * @return {number} The count of (key, value) pairs in the map.
+ */
+goog.labs.structs.Multimap.prototype.getCount = function() {
+ return this.count_;
+};
+
+
+/**
+ * @param {string} key The key to check.
+ * @param {string} value The value to check.
+ * @return {boolean} Whether the (key, value) pair exists in the multimap.
+ */
+goog.labs.structs.Multimap.prototype.containsEntry = function(key, value) {
+ var values = /** @type {Array.<string>} */ (this.map_.get(key));
+ if (!values) {
+ return false;
+ }
+
+ var index = goog.array.findIndex(values, function(v) {
+ return goog.labs.object.is(v, value);
+ });
+ return index >= 0;
+};
+
+
+/**
+ * @param {string} key The key to check.
+ * @return {boolean} Whether the multimap contains at least one (key,
+ * value) pair with the given key.
+ */
+goog.labs.structs.Multimap.prototype.containsKey = function(key) {
+ return this.map_.containsKey(key);
+};
+
+
+/**
+ * @param {*} value The value to check.
+ * @return {boolean} Whether the multimap contains at least one (key,
+ * value) pair with the given value.
+ */
+goog.labs.structs.Multimap.prototype.containsValue = function(value) {
+ return goog.array.some(this.map_.getValues(),
+ function(values) {
+ return goog.array.some(/** @type {Array} */ (values), function(v) {
+ return goog.labs.object.is(v, value);
+ });
+ });
+};
+
+
+/**
+ * @return {!Array.<string>} An array of unique keys.
+ */
+goog.labs.structs.Multimap.prototype.getKeys = function() {
+ return this.map_.getKeys();
+};
+
+
+/**
+ * @return {!Array.<*>} An array of values. There may be duplicates.
+ */
+goog.labs.structs.Multimap.prototype.getValues = function() {
+ return goog.array.flatten(this.map_.getValues());
+};
+
+
+/**
+ * @return {!Array.<!Array>} An array of entries. Each entry is of the
+ * form [key, value].
+ */
+goog.labs.structs.Multimap.prototype.getEntries = function() {
+ var keys = this.getKeys();
+ var entries = [];
+ for (var i = 0; i < keys.length; i++) {
+ var key = keys[i];
+ var values = this.get(key);
+ for (var j = 0; j < values.length; j++) {
+ entries.push([key, values[j]]);
+ }
+ }
+ return entries;
+};
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/structs/multimap_test.html b/contexts/data/lib/closure-library/closure/goog/labs/structs/multimap_test.html
new file mode 100644
index 0000000..38390e7
--- /dev/null
+++ b/contexts/data/lib/closure-library/closure/goog/labs/structs/multimap_test.html
@@ -0,0 +1,332 @@
+<!DOCTYPE html>
+<html>
+<!--
+Copyright 2012 The Closure Library Authors. All Rights Reserved.
+
+Use of this source code is governed by the Apache License, Version 2.0.
+See the COPYING file for details.
+-->
+<!--
+-->
+<head>
+<meta http-equiv="X-UA-Compatible" content="IE=edge">
+<title>Closure Unit Tests - goog.labs.structs.Multimap</title>
+<script src="../../base.js"></script>
+<script>
+ goog.require('goog.dispose');
+ goog.require('goog.labs.structs.Multimap');
+ goog.require('goog.testing.jsunit');
+</script>
+</head>
+<body>
+<script>
+var map;
+
+
+function setUp() {
+ map = new goog.labs.structs.Multimap();
+}
+
+
+function testGetCountWithEmptyMultimap() {
+ assertEquals(0, map.getCount());
+ assertTrue(map.isEmpty());
+}
+
+
+function testClone() {
+ map.add('k', 'v');
+ map.addAllValues('k2', ['v', 'v1', 'v2']);
+
+ var map2 = map.clone();
+
+ assertSameElements(['v'], map.get('k'));
+ assertSameElements(['v', 'v1', 'v2'], map.get('k2'));
+}
+
+
+function testAdd() {
+ map.add('key', 'v');
+ assertEquals(1, map.getCount());
+ map.add('key', 'v2');
+ assertEquals(2, map.getCount());
+ map.add('key', 'v3');
+ assertEquals(3, map.getCount());
+
+ var values = map.get('key');
+ assertEquals(3, values.length);
+ assertContains('v', values);
+ assertContains('v2', values);
+ assertContains('v3', values);
+}
+
+
+function testAddValues() {
+ map.addAllValues('key', ['v', 'v2', 'v3']);
+ assertSameElements(['v', 'v2', 'v3'], map.get('key'));
+
+ map.add('key2', 'a');
+ map.addAllValues('key2', ['v', 'v2', 'v3']);
+ assertSameElements(['a', 'v', 'v2', 'v3'], map.get('key2'));
+}
+
+
+function testAddAllWithMultimap() {
+ map.add('k', 'v');
+ map.addAllValues('k2', ['v', 'v1', 'v2']);
+
+ var map2 = new goog.labs.structs.Multimap();
+ map2.add('k2', 'v');
+ map2.addAllValues('k3', ['a', 'a1', 'a2']);
+
+ map.addAllFromMultimap(map2);
+ assertSameElements(['v'], map.get('k'));
+ assertSameElements(['v', 'v1', 'v2', 'v'], map.get('k2'));
+ assertSameElements(['a', 'a1', 'a2'], map.get('k3'));
+}
+
+
+function testAddAllWithMap() {
+ map.add('k', 'v');
+ map.addAllValues('k2', ['v', 'v1', 'v2']);
+
+ var map2 = new goog.labs.structs.Map();
+ map2.set('k2', 'v');
+ map2.set('k3', 'a');
+
+ map.addAllFromMultimap(map2);
+ assertSameElements(['v'], map.get('k'));
+ assertSameElements(['v', 'v1', 'v2', 'v'], map.get('k2'));
+ assertSameElements(['a'], map.get('k3'));
+}
+
+
+function testReplaceValues() {
+ map.add('key', 'v');
+ map.add('key', 'v2');
+
+ map.replaceValues('key', [0, 1, 2]);
+ assertSameElements([0, 1, 2], map.get('key'));
+ assertEquals(3, map.getCount());
+
+ map.replaceValues('key', ['v']);
+ assertSameElements(['v'], map.get('key'));
+ assertEquals(1, map.getCount());
+
+ map.replaceValues('key', []);
+ assertSameElements([], map.get('key'));
+ assertEquals(0, map.getCount());
+}
+
+
+function testRemove() {
+ map.add('key', 'v');
+ map.add('key', 'v2');
+ map.add('key', 'v3');
+
+ assertTrue(map.remove('key', 'v'));
+ var values = map.get('key');
+ assertEquals(2, map.getCount());
+ assertEquals(2, values.length);
+ assertContains('v2', values);
+ assertContains('v3', values);
+ assertFalse(map.remove('key', 'v'));
+
+ assertTrue(map.remove('key', 'v2'));
+ values = map.get('key');
+ assertEquals(1, map.getCount());
+ assertEquals(1, values.length);
+ assertContains('v3', values);
+ assertFalse(map.remove('key', 'v2'));
+
+ assertTrue(map.remove('key', 'v3'));
+ map.remove('key', 'v3');
+ assertTrue(map.isEmpty());
+ assertEquals(0, map.get('key').length);
+ assertFalse(map.remove('key', 'v2'));
+}
+
+
+function testRemoveWithNaN() {
+ map.add('key', NaN);
+ map.add('key', NaN);
+
+ assertTrue(map.remove('key', NaN));
+ var values = map.get('key');
+ assertEquals(1, values.length);
+ assertTrue(isNaN(values[0]));
+
+ assertTrue(map.remove('key', NaN));
+ assertEquals(0, map.get('key').length);
+ assertFalse(map.remove('key', NaN));
+}
+
+
+function testRemoveWithNegativeZero() {
+ map.add('key', 0);
+ map.add('key', -0);
+
+ assertTrue(map.remove('key', -0));
+ var values = map.get('key');
+ assertEquals(1, values.length);
+ assertTrue(1 / values[0] === 1 / 0);
+ assertFalse(map.remove('key', -0));
+
+ map.add('key', -0);
+
+ assertTrue(map.remove('key', 0));
+ var values = map.get('key');
+ assertEquals(1, values.length);
+ assertTrue(1 / values[0] === 1 / -0);
+ assertFalse(map.remove('key', 0));
+
+ assertTrue(map.remove('key', -0));
+ assertEquals(0, map.get('key').length);
+}
+
+
+function testRemoveAll() {
+ map.add('key', 'v');
+ map.add('key', 'v2');
+ map.add('key', 'v3');
+ map.add('key', 'v4');
+ map.add('key2', 'v');
+
+ assertTrue(map.removeAll('key'));
+ assertSameElements([], map.get('key'));
+ assertSameElements(['v'], map.get('key2'));
+ assertFalse(map.removeAll('key'));
+ assertEquals(1, map.getCount());
+
+ assertTrue(map.removeAll('key2'));
+ assertSameElements([], map.get('key2'));
+ assertFalse(map.removeAll('key2'));
+ assertTrue(map.isEmpty());
+}
+
+
+function testAddWithDuplicateValue() {
+ map.add('key', 'v');
+ map.add('key', 'v');
+ map.add('key', 'v');
+ assertArrayEquals(['v', 'v', 'v'], map.get('key'));
+}
+
+
+function testContainsEntry() {
+ assertFalse(map.containsEntry('k', 'v'));
+ assertFalse(map.containsEntry('k', 'v2'));
+ assertFalse(map.containsEntry('k2', 'v'));
+
+ map.add('k', 'v');
+ assertTrue(map.containsEntry('k', 'v'));
+ assertFalse(map.containsEntry('k', 'v2'));
+ assertFalse(map.containsEntry('k2', 'v'));
+
+ map.add('k', 'v2');
+ assertTrue(map.containsEntry('k', 'v'));
+ assertTrue(map.containsEntry('k', 'v2'));
+ assertFalse(map.containsEntry('k2', 'v'));
+
+ map.add('k2', 'v');
+ assertTrue(map.containsEntry('k', 'v'));
+ assertTrue(map.containsEntry('k', 'v2'));
+ assertTrue(map.containsEntry('k2', 'v'));
+}
+
+
+function testContainsKey() {
+ assertFalse(map.containsKey('k'));
+ assertFalse(map.containsKey('k2'));
+
+ map.add('k', 'v');
+ assertTrue(map.containsKey('k'));
+ map.add('k2', 'v');
+ assertTrue(map.containsKey('k2'));
+
+ map.remove('k', 'v');
+ assertFalse(map.containsKey('k'));
+ map.remove('k2', 'v');
+ assertFalse(map.containsKey('k2'));
+}
+
+
+function testContainsValue() {
+ assertFalse(map.containsValue('v'));
+ assertFalse(map.containsValue('v2'));
+
+ map.add('key', 'v');
+ assertTrue(map.containsValue('v'));
+ map.add('key', 'v2');
+ assertTrue(map.containsValue('v2'));
+}
+
+
+function testGetEntries() {
+ map.add('key', 'v');
+ map.add('key', 'v2');
+ map.add('key2', 'v3');
+
+ var entries = map.getEntries();
+ assertEquals(3, entries.length);
+ assertContainsEntry(['key', 'v'], entries);
+ assertContainsEntry(['key', 'v2'], entries);
+ assertContainsEntry(['key2', 'v3'], entries);
+}
+
+
+function testGetKeys() {
+ map.add('key', 'v');
+ map.add('key', 'v2');
+ map.add('key2', 'v3');
+ map.add('key3', 'v4');
+ map.removeAll('key3');
+
+ assertSameElements(['key', 'key2'], map.getKeys());
+}
+
+
+function testGetKeys() {
+ map.add('key', 'v');
+ map.add('key', 'v2');
+ map.add('key2', 'v2');
+ map.add('key3', 'v4');
+ map.removeAll('key3');
+
+ assertSameElements(['v', 'v2', 'v2'], map.getValues());
+}
+
+
+function testGetReturnsDefensiveCopyOfUnderlyingData() {
+ map.add('key', 'v');
+ map.add('key', 'v2');
+ map.add('key', 'v3');
+
+ var values = map.get('key');
+ values.push('v4');
+ assertFalse(map.containsEntry('key', 'v4'));
+}
+
+
+function testClear() {
+ map.add('key', 'v');
+ map.add('key', 'v2');
+ map.add('key2', 'v3');
+
+ map.clear();
+ assertTrue(map.isEmpty());
+ assertSameElements([], map.getEntries());
+}
+
+
+function assertContainsEntry(entry, entryList) {
+ for (var i = 0; i < entryList.length; ++i) {
+ if (entry[0] == entryList[i][0] && entry[1] === entryList[i][1]) {
+ return;
+ }
+ }
+ fail('Did not find entry: ' + entry + ' in: ' + entryList);
+}
+</script>
+</body>
+</html>
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/testing/assertthat.js b/contexts/data/lib/closure-library/closure/goog/labs/testing/assertthat.js
new file mode 100644
index 0000000..b6e8215
--- /dev/null
+++ b/contexts/data/lib/closure-library/closure/goog/labs/testing/assertthat.js
@@ -0,0 +1,59 @@
+// 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 Provides main functionality of assertThat. assertThat calls the
+ * matcher's matches method to test if a matcher matches assertThat's arguments.
+ */
+
+
+goog.provide('goog.labs.testing.MatcherError');
+goog.provide('goog.labs.testing.assertThat');
+
+goog.require('goog.asserts');
+goog.require('goog.debug.Error');
+goog.require('goog.labs.testing.Matcher');
+
+
+/**
+ * Asserts that the actual value evaluated by the matcher is true.
+ *
+ * @param {*} actual The object to assert by the matcher.
+ * @param {!goog.labs.testing.Matcher} matcher A matcher to verify values.
+ * @param {string=} opt_reason Description of what is asserted.
+ *
+ */
+goog.labs.testing.assertThat = function(actual, matcher, opt_reason) {
+ if (!matcher.matches(actual)) {
+ // Prefix the error description with a reason from the assert ?
+ var prefix = opt_reason ? opt_reason + ': ' : '';
+ var desc = prefix + matcher.describe(actual);
+
+ // some sort of failure here
+ throw new goog.labs.testing.MatcherError(desc);
+ }
+};
+
+
+
+/**
+ * Error thrown when a Matcher fails to match the input value.
+ * @param {string=} opt_message The error message.
+ * @constructor
+ * @extends {goog.debug.Error}
+ */
+goog.labs.testing.MatcherError = function(opt_message) {
+ goog.base(this, opt_message);
+};
+goog.inherits(goog.labs.testing.MatcherError, goog.debug.Error);
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/testing/assertthat_test.html b/contexts/data/lib/closure-library/closure/goog/labs/testing/assertthat_test.html
new file mode 100644
index 0000000..a70e9c7
--- /dev/null
+++ b/contexts/data/lib/closure-library/closure/goog/labs/testing/assertthat_test.html
@@ -0,0 +1,73 @@
+<!DOCTYPE html>
+<html>
+<!--
+Copyright 2012 The Closure Library Authors. All Rights Reserved.
+
+Use of this source code is governed by the Apache License, Version 2.0.
+See the COPYING file for details.
+-->
+<head>
+<title>Closure Unit Tests - goog.labs.testing.assertThat</title>
+<script src="../../base.js"></script>
+<script>
+
+goog.require('goog.labs.testing.Matcher');
+goog.require('goog.labs.testing.assertThat');
+goog.require('goog.testing.recordFunction');
+goog.require('goog.testing.jsunit');
+
+</script>
+</head>
+<body>
+<script>
+
+var successMatchesFn, failureMatchesFn, describeFn, successTestMatcher;
+var failureTestMatcher;
+
+function setUp() {
+ successMatchesFn = new goog.testing.recordFunction(function() {return true;});
+ failureMatchesFn = new goog.testing.recordFunction(function() {return false;});
+ describeFn = new goog.testing.recordFunction();
+
+ successTestMatcher = function() {
+ return { matches: successMatchesFn, describe: describeFn };
+ };
+ failureTestMatcher = function() {
+ return { matches: failureMatchesFn, describe: describeFn };
+ };
+}
+
+function testAssertthatAlwaysCallsMatches() {
+ var value = 7;
+ goog.labs.testing.assertThat(value, successTestMatcher(),
+ 'matches is called on success');
+
+ assertEquals(1, successMatchesFn.getCallCount());
+ var matchesCall = successMatchesFn.popLastCall();
+ assertEquals(value, matchesCall.getArgument(0));
+
+ var e = assertThrows(goog.bind(goog.labs.testing.assertThat, null,
+ value, failureTestMatcher(), 'matches is called on failure'));
+
+ assertTrue(e instanceof goog.labs.testing.MatcherError);
+
+ assertEquals(1, failureMatchesFn.getCallCount());
+}
+
+function testAssertthatCallsDescribeOnFailure() {
+ var value = 7;
+ var e = assertThrows(goog.bind(goog.labs.testing.assertThat, null,
+ value, failureTestMatcher(), 'describe is called on failure'));
+
+ assertTrue(e instanceof goog.labs.testing.MatcherError);
+
+ assertEquals(1, failureMatchesFn.getCallCount());
+ assertEquals(1, describeFn.getCallCount());
+
+ var matchesCall = describeFn.popLastCall();
+ assertEquals(value, matchesCall.getArgument(0));
+}
+
+</script>
+</body>
+</html>
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/testing/decoratormatcher.js b/contexts/data/lib/closure-library/closure/goog/labs/testing/decoratormatcher.js
new file mode 100644
index 0000000..1d3c4ed
--- /dev/null
+++ b/contexts/data/lib/closure-library/closure/goog/labs/testing/decoratormatcher.js
@@ -0,0 +1,94 @@
+// 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 Provides the built-in decorators: is, describedAs, anything.
+ */
+
+
+
+goog.provide('goog.labs.testing.AnythingMatcher');
+
+
+goog.require('goog.labs.testing.Matcher');
+
+
+
+/**
+ * The Anything matcher. Matches all possible inputs.
+ *
+ * @constructor
+ * @implements {goog.labs.testing.Matcher}
+ */
+goog.labs.testing.AnythingMatcher = function() {};
+
+
+/**
+ * Matches anything. Useful if one doesn't care what the object under test is.
+ *
+ * @override
+ */
+goog.labs.testing.AnythingMatcher.prototype.matches =
+ function(actualObject) {
+ return true;
+};
+
+
+/**
+ * This method is never called but is needed so AnythingMatcher implements the
+ * Matcher interface.
+ *
+ * @override
+ */
+goog.labs.testing.AnythingMatcher.prototype.describe =
+ function(actualObject) {
+ throw Error('AnythingMatcher should never fail!');
+};
+
+
+/**
+ * Returns a matcher that matches anything.
+ *
+ * @return {!goog.labs.testing.AnythingMatcher} A AnythingMatcher.
+ */
+function anything() {
+ return new goog.labs.testing.AnythingMatcher();
+}
+
+
+/**
+ * Returnes any matcher that is passed to it (aids readability).
+ *
+ * @param {!goog.labs.testing.Matcher} matcher A matcher.
+ * @return {!goog.labs.testing.Matcher} The wrapped matcher.
+ */
+function is(matcher) {
+ return matcher;
+}
+
+
+/**
+ * Returns a matcher with a customized description for the given matcher.
+ *
+ * @param {string} description The custom description for the matcher.
+ * @param {!goog.labs.testing.Matcher} matcher The matcher.
+ *
+ * @return {!goog.labs.testing.Matcher} The matcher with custom description.
+ */
+function describedAs(description, matcher) {
+ matcher.describe = function(value) {
+ return description;
+ };
+ return matcher;
+}
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/testing/decoratormatcher_test.html b/contexts/data/lib/closure-library/closure/goog/labs/testing/decoratormatcher_test.html
new file mode 100644
index 0000000..85323dc
--- /dev/null
+++ b/contexts/data/lib/closure-library/closure/goog/labs/testing/decoratormatcher_test.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<html>
+<!--
+Copyright 2012 The Closure Library Authors. All Rights Reserved.
+
+Use of this source code is governed by the Apache License, Version 2.0.
+See the COPYING file for details.
+-->
+<head>
+<title>Closure Unit Tests - Decorator matchers</title>
+<script src="../../base.js"></script>
+<script>
+
+goog.require('goog.labs.testing.AnythingMatcher');
+goog.require('goog.labs.testing.GreaterThanMatcher');
+goog.require('goog.labs.testing.assertThat');
+goog.require('goog.testing.jsunit');
+
+</script>
+</head>
+<body>
+<script>
+
+function testAnythingMatcher() {
+ goog.labs.testing.assertThat(true, anything(), 'anything matches true');
+ goog.labs.testing.assertThat(false, anything(), 'false matches anything');
+}
+
+function testIs() {
+ goog.labs.testing.assertThat(5, is(greaterThan(4)), '5 is > 4');
+}
+
+function testDescribedAs() {
+ var e = assertThrows(function() {
+ goog.labs.testing.assertThat(4, describedAs('this is a test',
+ greaterThan(6)))});
+ assertTrue(e instanceof goog.labs.testing.MatcherError);
+ assertEquals('this is a test', e.message);
+}
+
+</script>
+</body>
+</html>
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/testing/dictionarymatcher.js b/contexts/data/lib/closure-library/closure/goog/labs/testing/dictionarymatcher.js
new file mode 100644
index 0000000..9254d8e
--- /dev/null
+++ b/contexts/data/lib/closure-library/closure/goog/labs/testing/dictionarymatcher.js
@@ -0,0 +1,266 @@
+// 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 Provides the built-in dictionary matcher methods like
+ * hasEntry, hasEntries, hasKey, hasValue, etc.
+ */
+
+
+
+goog.provide('goog.labs.testing.HasEntriesMatcher');
+goog.provide('goog.labs.testing.HasEntryMatcher');
+goog.provide('goog.labs.testing.HasKeyMatcher');
+goog.provide('goog.labs.testing.HasValueMatcher');
+
+
+goog.require('goog.array');
+goog.require('goog.asserts');
+goog.require('goog.labs.testing.Matcher');
+goog.require('goog.string');
+
+
+
+/**
+ * The HasEntries matcher.
+ *
+ * @param {!Object} entries The entries to check in the object.
+ *
+ * @constructor
+ * @implements {goog.labs.testing.Matcher}
+ */
+goog.labs.testing.HasEntriesMatcher = function(entries) {
+ /**
+ * @type {Object}
+ * @private
+ */
+ this.entries_ = entries;
+};
+
+
+/**
+ * Determines if an object has particular entries.
+ *
+ * @override
+ */
+goog.labs.testing.HasEntriesMatcher.prototype.matches =
+ function(actualObject) {
+ goog.asserts.assertObject(actualObject, 'Expected an Object');
+ var object = /** @type {!Object} */(actualObject);
+ return goog.object.every(this.entries_, function(value, key) {
+ return goog.object.containsKey(object, key) &&
+ object[key] === value;
+ });
+};
+
+
+/**
+ * @override
+ */
+goog.labs.testing.HasEntriesMatcher.prototype.describe =
+ function(actualObject) {
+ goog.asserts.assertObject(actualObject, 'Expected an Object');
+ var object = /** @type {!Object} */(actualObject);
+ var errorString = 'Input object did not contain the following entries:\n';
+ goog.object.forEach(this.entries_, function(value, key) {
+ if (!goog.object.containsKey(object, key) ||
+ object[key] !== value) {
+ errorString += key + ': ' + value + '\n';
+ }
+ });
+ return errorString;
+};
+
+
+
+/**
+ * The HasEntry matcher.
+ *
+ * @param {string} key The key for the entry.
+ * @param {*} value The value for the key.
+ *
+ * @constructor
+ * @implements {goog.labs.testing.Matcher}
+ */
+goog.labs.testing.HasEntryMatcher = function(key, value) {
+ /**
+ * @type {string}
+ * @private
+ */
+ this.key_ = key;
+ /**
+ * @type {*}
+ * @private
+ */
+ this.value_ = value;
+};
+
+
+/**
+ * Determines if an object has a particular entry.
+ *
+ * @override
+ */
+goog.labs.testing.HasEntryMatcher.prototype.matches =
+ function(actualObject) {
+ goog.asserts.assertObject(actualObject);
+ return goog.object.containsKey(actualObject, this.key_) &&
+ actualObject[this.key_] === this.value_;
+};
+
+
+/**
+ * @override
+ */
+goog.labs.testing.HasEntryMatcher.prototype.describe =
+ function(actualObject) {
+ goog.asserts.assertObject(actualObject);
+ var errorMsg;
+ if (goog.object.containsKey(actualObject, this.key_)) {
+ errorMsg = 'Input object did not contain key: ' + this.key_;
+ } else {
+ errorMsg = 'Value for key did not match value: ' + this.value_;
+ }
+ return errorMsg;
+};
+
+
+
+/**
+ * The HasKey matcher.
+ *
+ * @param {string} key The key to check in the object.
+ *
+ * @constructor
+ * @implements {goog.labs.testing.Matcher}
+ */
+goog.labs.testing.HasKeyMatcher = function(key) {
+ /**
+ * @type {string}
+ * @private
+ */
+ this.key_ = key;
+};
+
+
+/**
+ * Determines if an object has a key.
+ *
+ * @override
+ */
+goog.labs.testing.HasKeyMatcher.prototype.matches =
+ function(actualObject) {
+ goog.asserts.assertObject(actualObject);
+ return goog.object.containsKey(actualObject, this.key_);
+};
+
+
+/**
+ * @override
+ */
+goog.labs.testing.HasKeyMatcher.prototype.describe =
+ function(actualObject) {
+ goog.asserts.assertObject(actualObject);
+ return 'Input object did not contain the key: ' + this.key_;
+};
+
+
+
+/**
+ * The HasValue matcher.
+ *
+ * @param {*} value The value to check in the object.
+ *
+ * @constructor
+ * @implements {goog.labs.testing.Matcher}
+ */
+goog.labs.testing.HasValueMatcher = function(value) {
+ /**
+ * @type {*}
+ * @private
+ */
+ this.value_ = value;
+};
+
+
+/**
+ * Determines if an object contains a value
+ *
+ * @override
+ */
+goog.labs.testing.HasValueMatcher.prototype.matches =
+ function(actualObject) {
+ goog.asserts.assertObject(actualObject, 'Expected an Object');
+ var object = /** @type {!Object} */(actualObject);
+ return goog.object.containsValue(object, this.value_);
+};
+
+
+/**
+ * @override
+ */
+goog.labs.testing.HasValueMatcher.prototype.describe =
+ function(actualObject) {
+ return 'Input object did not contain the value: ' + this.value_;
+};
+
+
+/**
+ * Gives a matcher that asserts an object contains all the given key-value pairs
+ * in the input object.
+ *
+ * @param {!Object} entries The entries to check for presence in the object.
+ *
+ * @return {!goog.labs.testing.HasEntriesMatcher} A HasEntriesMatcher.
+ */
+function hasEntries(entries) {
+ return new goog.labs.testing.HasEntriesMatcher(entries);
+}
+
+
+/**
+ * Gives a matcher that asserts an object contains the given key-value pair.
+ *
+ * @param {string} key The key to check for presence in the object.
+ * @param {*} value The value to check for presence in the object.
+ *
+ * @return {!goog.labs.testing.HasEntryMatcher} A HasEntryMatcher.
+ */
+function hasEntry(key, value) {
+ return new goog.labs.testing.HasEntryMatcher(key, value);
+}
+
+
+/**
+ * Gives a matcher that asserts an object contains the given key.
+ *
+ * @param {string} key The key to check for presence in the object.
+ *
+ * @return {!goog.labs.testing.HasKeyMatcher} A HasKeyMatcher.
+ */
+function hasKey(key) {
+ return new goog.labs.testing.HasKeyMatcher(key);
+}
+
+
+/**
+ * Gives a matcher that asserts an object contains the given value.
+ *
+ * @param {*} value The value to check for presence in the object.
+ *
+ * @return {!goog.labs.testing.HasValueMatcher} A HasValueMatcher.
+ */
+function hasValue(value) {
+ return new goog.labs.testing.HasValueMatcher(value);
+}
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/testing/dictionarymatcher_test.html b/contexts/data/lib/closure-library/closure/goog/labs/testing/dictionarymatcher_test.html
new file mode 100644
index 0000000..9e86778
--- /dev/null
+++ b/contexts/data/lib/closure-library/closure/goog/labs/testing/dictionarymatcher_test.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<html>
+<!--
+Copyright 2012 The Closure Library Authors. All Rights Reserved.
+
+Use of this source code is governed by the Apache License, Version 2.0.
+See the COPYING file for details.
+-->
+<head>
+<title>Closure Unit Tests - Object matchers</title>
+<script src="../../base.js"></script>
+<script>
+
+goog.require('goog.labs.testing.HasEntriesMatcher');
+goog.require('goog.labs.testing.HasKeyMatcher');
+goog.require('goog.labs.testing.HasValueMatcher');
+goog.require('goog.labs.testing.assertThat');
+goog.require('goog.testing.jsunit');
+
+</script>
+</head>
+<body>
+<script>
+
+function testHasEntries() {
+ var obj1 = {x: 1, y: 2, z: 3};
+ goog.labs.testing.assertThat(obj1, hasEntries({x: 1, y: 2}),
+ 'obj1 has entries: {x:1, y:2}');
+
+ assertMatcherError(function() {
+ goog.labs.testing.assertThat(obj1, hasEntries({z: 5, a: 4}));
+ }, 'hasEntries should throw exception when it fails');
+}
+
+function testHasEntry() {
+ var obj1 = {x: 1, y: 2, z: 3};
+ goog.labs.testing.assertThat(obj1, hasEntry('x', 1),
+ 'obj1 has entry: {x:1}');
+
+ assertMatcherError(function() {
+ goog.labs.testing.assertThat(obj1, hasEntry('z', 5));
+ }, 'hasEntry should throw exception when it fails');
+}
+
+function testHasKey() {
+ var obj1 = {x: 1};
+ goog.labs.testing.assertThat(obj1, hasKey('x'), 'obj1 has key x');
+
+ assertMatcherError(function() {
+ goog.labs.testing.assertThat(obj1, hasKey('z'));
+ }, 'hasKey should throw exception when it fails');
+}
+
+function testHasValue() {
+ var obj1 = {x: 1};
+ goog.labs.testing.assertThat(obj1, hasValue(1), 'obj1 has value 1');
+
+ assertMatcherError(function() {
+ goog.labs.testing.assertThat(obj1, hasValue(2));
+ }, 'hasValue should throw exception when it fails');
+}
+
+function assertMatcherError(callable, errorString) {
+ var e = assertThrows(errorString || 'callable throws exception', callable);
+ assertTrue(e instanceof goog.labs.testing.MatcherError);
+}
+
+</script>
+</body>
+</html>
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/testing/logicmatcher.js b/contexts/data/lib/closure-library/closure/goog/labs/testing/logicmatcher.js
new file mode 100644
index 0000000..3004210
--- /dev/null
+++ b/contexts/data/lib/closure-library/closure/goog/labs/testing/logicmatcher.js
@@ -0,0 +1,206 @@
+// 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 Provides the built-in logic matchers: anyOf, allOf, and isNot.
+ *
+ */
+
+
+goog.provide('goog.labs.testing.AllOfMatcher');
+goog.provide('goog.labs.testing.AnyOfMatcher');
+goog.provide('goog.labs.testing.IsNotMatcher');
+
+
+goog.require('goog.array');
+goog.require('goog.labs.testing.Matcher');
+
+
+
+/**
+ * The AllOf matcher.
+ *
+ * @param {!Array.<!goog.labs.testing.Matcher>} matchers Input matchers.
+ *
+ * @constructor
+ * @implements {goog.labs.testing.Matcher}
+ */
+goog.labs.testing.AllOfMatcher = function(matchers) {
+ /**
+ * @type {!Array.<!goog.labs.testing.Matcher>}
+ * @private
+ */
+ this.matchers_ = matchers;
+};
+
+
+/**
+ * Determines if all of the matchers match the input value.
+ *
+ * @override
+ */
+goog.labs.testing.AllOfMatcher.prototype.matches = function(actualValue) {
+ return goog.array.every(this.matchers_, function(matcher) {
+ return matcher.matches(actualValue);
+ });
+};
+
+
+/**
+ * Describes why the matcher failed. The returned string is a concatenation of
+ * all the failed matchers' error strings.
+ *
+ * @override
+ */
+goog.labs.testing.AllOfMatcher.prototype.describe =
+ function(actualValue) {
+ // TODO(user) : Optimize this to remove duplication with matches ?
+ var errorString = '';
+ goog.array.forEach(this.matchers_, function(matcher) {
+ if (!matcher.matches(actualValue)) {
+ errorString += matcher.describe(actualValue) + '\n';
+ }
+ });
+ return errorString;
+};
+
+
+
+/**
+ * The AnyOf matcher.
+ *
+ * @param {!Array.<!goog.labs.testing.Matcher>} matchers Input matchers.
+ *
+ * @constructor
+ * @implements {goog.labs.testing.Matcher}
+ */
+goog.labs.testing.AnyOfMatcher = function(matchers) {
+ /**
+ * @type {!Array.<!goog.labs.testing.Matcher>}
+ * @private
+ */
+ this.matchers_ = matchers;
+};
+
+
+/**
+ * Determines if any of the matchers matches the input value.
+ *
+ * @override
+ */
+goog.labs.testing.AnyOfMatcher.prototype.matches = function(actualValue) {
+ return goog.array.some(this.matchers_, function(matcher) {
+ return matcher.matches(actualValue);
+ });
+};
+
+
+/**
+ * Describes why the matcher failed.
+ *
+ * @override
+ */
+goog.labs.testing.AnyOfMatcher.prototype.describe =
+ function(actualValue) {
+ // TODO(user) : Optimize this to remove duplication with matches ?
+ var errorString = '';
+ goog.array.forEach(this.matchers_, function(matcher) {
+ if (!matcher.matches(actualValue)) {
+ errorString += matcher.describe(actualValue) + '\n';
+ }
+ });
+ return errorString;
+};
+
+
+
+/**
+ * The IsNot matcher.
+ *
+ * @param {!goog.labs.testing.Matcher} matcher The matcher to negate.
+ *
+ * @constructor
+ * @implements {goog.labs.testing.Matcher}
+ */
+goog.labs.testing.IsNotMatcher = function(matcher) {
+ /**
+ * @type {!goog.labs.testing.Matcher}
+ * @private
+ */
+ this.matcher_ = matcher;
+};
+
+
+/**
+ * Determines if the input value doesn't satisfy a matcher.
+ *
+ * @override
+ */
+goog.labs.testing.IsNotMatcher.prototype.matches = function(actualValue) {
+ return !this.matcher_.matches(actualValue);
+};
+
+
+/**
+ * Describes why the matcher failed.
+ *
+ * @override
+ */
+goog.labs.testing.IsNotMatcher.prototype.describe =
+ function(actualValue) {
+ return 'The following is false: ' + this.matcher_.describe(actualValue);
+};
+
+
+/**
+ * Creates a matcher that will succeed only if all of the given matchers
+ * succeed.
+ *
+ * @param {...goog.labs.testing.Matcher} var_args The matchers to test
+ * against.
+ *
+ * @return {!goog.labs.testing.AllOfMatcher} The AllOf matcher.
+ */
+function allOf(var_args) {
+ var matchers = goog.array.toArray(arguments);
+ return new goog.labs.testing.AllOfMatcher(matchers);
+}
+
+
+/**
+ * Accepts a set of matchers and returns a matcher which matches
+ * values which satisfy the constraints of any of the given matchers.
+ *
+ * @param {...goog.labs.testing.Matcher} var_args The matchers to test
+ * against.
+ *
+ * @return {!goog.labs.testing.AnyOfMatcher} The AnyOf matcher.
+ */
+function anyOf(var_args) {
+ var matchers = goog.array.toArray(arguments);
+ return new goog.labs.testing.AnyOfMatcher(matchers);
+}
+
+
+/**
+ * Returns a matcher that negates the input matcher. The returned
+ * matcher matches the values not matched by the input matcher and vice-versa.
+ *
+ * @param {!goog.labs.testing.Matcher} matcher The matcher to test against.
+ *
+ * @return {!goog.labs.testing.IsNotMatcher} The IsNot matcher.
+ */
+function isNot(matcher) {
+ return new goog.labs.testing.IsNotMatcher(matcher);
+}
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/testing/logicmatcher_test.html b/contexts/data/lib/closure-library/closure/goog/labs/testing/logicmatcher_test.html
new file mode 100644
index 0000000..58335fc
--- /dev/null
+++ b/contexts/data/lib/closure-library/closure/goog/labs/testing/logicmatcher_test.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html>
+<!--
+Copyright 2012 The Closure Library Authors. All Rights Reserved.
+
+Use of this source code is governed by the Apache License, Version 2.0.
+See the COPYING file for details.
+-->
+<head>
+<title>Closure Unit Tests - Logic matchers</title>
+<script src="../../base.js"></script>
+<script>
+
+goog.require('goog.labs.testing.AllOfMatcher');
+goog.require('goog.labs.testing.GreaterThanMatcher');
+goog.require('goog.labs.testing.assertThat');
+goog.require('goog.testing.jsunit');
+
+</script>
+</head>
+<body>
+<script>
+
+function testAnyOf() {
+ goog.labs.testing.assertThat(5, anyOf(greaterThan(4), lessThan(3)),
+ '5 > 4 || 5 < 3');
+ goog.labs.testing.assertThat(2, anyOf(greaterThan(4), lessThan(3)),
+ '2 > 4 || 2 < 3');
+
+ assertMatcherError(function() {
+ goog.labs.testing.assertThat(4, anyOf(greaterThan(5), lessThan(2)));
+ }, 'anyOf should throw exception when it fails');
+}
+
+function testAllOf() {
+ goog.labs.testing.assertThat(5, allOf(greaterThan(4), lessThan(6)),
+ '5 > 4 && 5 < 6');
+
+ assertMatcherError(function() {
+ goog.labs.testing.assertThat(4, allOf(lessThan(5), lessThan(3)));
+ }, 'allOf should throw exception when it fails');
+}
+
+function testIsNot() {
+ goog.labs.testing.assertThat(5, isNot(greaterThan(6)), '5 !> 6');
+
+ assertMatcherError(function() {
+ goog.labs.testing.assertThat(4, isNot(greaterThan(3)));
+ }, 'isNot should throw exception when it fails');
+}
+
+function assertMatcherError(callable, errorString) {
+ var e = assertThrows(errorString || 'callable throws exception', callable);
+ assertTrue(e instanceof goog.labs.testing.MatcherError);
+}
+
+</script>
+</body>
+</html>
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/testing/matcher.js b/contexts/data/lib/closure-library/closure/goog/labs/testing/matcher.js
new file mode 100644
index 0000000..b0f5fd4
--- /dev/null
+++ b/contexts/data/lib/closure-library/closure/goog/labs/testing/matcher.js
@@ -0,0 +1,51 @@
+// 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 Provides the base Matcher interface. User code should use the
+ * matchers through assertThat statements and not directly.
+ */
+
+
+goog.provide('goog.labs.testing.Matcher');
+
+
+
+/**
+ * A matcher object to be used in assertThat statements.
+ * @interface
+ */
+goog.labs.testing.Matcher = function() {};
+
+
+/**
+ * Determines whether a value matches the constraints of the match.
+ *
+ * @param {*} value The object to match.
+ * @return {boolean} Whether the input value matches this matcher.
+ */
+goog.labs.testing.Matcher.prototype.matches = function(value) {};
+
+
+/**
+ * Describes why the matcher failed.
+ *
+ * @param {*} value The value that didn't match.
+ * @param {string=} opt_description A partial description to which the reason
+ * will be appended.
+ *
+ * @return {string} Description of why the matcher failed.
+ */
+goog.labs.testing.Matcher.prototype.describe =
+ function(value, opt_description) {};
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/testing/numbermatcher.js b/contexts/data/lib/closure-library/closure/goog/labs/testing/numbermatcher.js
new file mode 100644
index 0000000..a78e5be
--- /dev/null
+++ b/contexts/data/lib/closure-library/closure/goog/labs/testing/numbermatcher.js
@@ -0,0 +1,334 @@
+// 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 Provides the built-in number matchers like lessThan,
+ * greaterThan, etc.
+ */
+
+
+goog.provide('goog.labs.testing.CloseToMatcher');
+goog.provide('goog.labs.testing.EqualToMatcher');
+goog.provide('goog.labs.testing.GreaterThanEqualToMatcher');
+goog.provide('goog.labs.testing.GreaterThanMatcher');
+goog.provide('goog.labs.testing.LessThanEqualToMatcher');
+goog.provide('goog.labs.testing.LessThanMatcher');
+
+
+goog.require('goog.asserts');
+goog.require('goog.labs.testing.Matcher');
+
+
+
+/**
+ * The GreaterThan matcher.
+ *
+ * @param {number} value The value to compare.
+ *
+ * @constructor
+ * @implements {goog.labs.testing.Matcher}
+ */
+goog.labs.testing.GreaterThanMatcher = function(value) {
+ /**
+ * @type {number}
+ * @private
+ */
+ this.value_ = value;
+};
+
+
+/**
+ * Determines if input value is greater than the expected value.
+ *
+ * @override
+ */
+goog.labs.testing.GreaterThanMatcher.prototype.matches = function(actualValue) {
+ goog.asserts.assertNumber(actualValue);
+ return actualValue > this.value_;
+};
+
+
+/**
+ * @override
+ */
+goog.labs.testing.GreaterThanMatcher.prototype.describe =
+ function(actualValue) {
+ goog.asserts.assertNumber(actualValue);
+ return actualValue + ' is not greater than ' + this.value_;
+};
+
+
+
+/**
+ * The lessThan matcher.
+ *
+ * @param {number} value The value to compare.
+ *
+ * @constructor
+ * @implements {goog.labs.testing.Matcher}
+ */
+goog.labs.testing.LessThanMatcher = function(value) {
+ /**
+ * @type {number}
+ * @private
+ */
+ this.value_ = value;
+};
+
+
+/**
+ * Determines if the input value is less than the expected value.
+ *
+ * @override
+ */
+goog.labs.testing.LessThanMatcher.prototype.matches = function(actualValue) {
+ goog.asserts.assertNumber(actualValue);
+ return actualValue < this.value_;
+};
+
+
+/**
+ * @override
+ */
+goog.labs.testing.LessThanMatcher.prototype.describe =
+ function(actualValue) {
+ goog.asserts.assertNumber(actualValue);
+ return actualValue + ' is not less than ' + this.value_;
+};
+
+
+
+/**
+ * The GreaterThanEqualTo matcher.
+ *
+ * @param {number} value The value to compare.
+ *
+ * @constructor
+ * @implements {goog.labs.testing.Matcher}
+ */
+goog.labs.testing.GreaterThanEqualToMatcher = function(value) {
+ /**
+ * @type {number}
+ * @private
+ */
+ this.value_ = value;
+};
+
+
+/**
+ * Determines if the input value is greater than equal to the expected value.
+ *
+ * @override
+ */
+goog.labs.testing.GreaterThanEqualToMatcher.prototype.matches =
+ function(actualValue) {
+ goog.asserts.assertNumber(actualValue);
+ return actualValue >= this.value_;
+};
+
+
+/**
+ * @override
+ */
+goog.labs.testing.GreaterThanEqualToMatcher.prototype.describe =
+ function(actualValue) {
+ goog.asserts.assertNumber(actualValue);
+ return actualValue + ' is not greater than equal to ' + this.value_;
+};
+
+
+
+/**
+ * The LessThanEqualTo matcher.
+ *
+ * @param {number} value The value to compare.
+ *
+ * @constructor
+ * @implements {goog.labs.testing.Matcher}
+ */
+goog.labs.testing.LessThanEqualToMatcher = function(value) {
+ /**
+ * @type {number}
+ * @private
+ */
+ this.value_ = value;
+};
+
+
+/**
+ * Determines if the input value is less than or equal to the expected value.
+ *
+ * @override
+ */
+goog.labs.testing.LessThanEqualToMatcher.prototype.matches =
+ function(actualValue) {
+ goog.asserts.assertNumber(actualValue);
+ return actualValue <= this.value_;
+};
+
+
+/**
+ * @override
+ */
+goog.labs.testing.LessThanEqualToMatcher.prototype.describe =
+ function(actualValue) {
+ goog.asserts.assertNumber(actualValue);
+ return actualValue + ' is not less than equal to ' + this.value_;
+};
+
+
+
+/**
+ * The EqualTo matcher.
+ *
+ * @param {number} value The value to compare.
+ *
+ * @constructor
+ * @implements {goog.labs.testing.Matcher}
+ */
+goog.labs.testing.EqualToMatcher = function(value) {
+ /**
+ * @type {number}
+ * @private
+ */
+ this.value_ = value;
+};
+
+
+/**
+ * Determines if the input value is equal to the expected value.
+ *
+ * @override
+ */
+goog.labs.testing.EqualToMatcher.prototype.matches = function(actualValue) {
+ goog.asserts.assertNumber(actualValue);
+ return actualValue === this.value_;
+};
+
+
+/**
+ * @override
+ */
+goog.labs.testing.EqualToMatcher.prototype.describe =
+ function(actualValue) {
+ goog.asserts.assertNumber(actualValue);
+ return actualValue + ' is not equal to ' + this.value_;
+};
+
+
+
+/**
+ * The CloseTo matcher.
+ *
+ * @param {number} value The value to compare.
+ * @param {number} range The range to check within.
+ *
+ * @constructor
+ * @implements {goog.labs.testing.Matcher}
+ */
+goog.labs.testing.CloseToMatcher = function(value, range) {
+ /**
+ * @type {number}
+ * @private
+ */
+ this.value_ = value;
+ /**
+ * @type {number}
+ * @private
+ */
+ this.range_ = range;
+};
+
+
+/**
+ * Determines if input value is within a certain range of the expected value.
+ *
+ * @override
+ */
+goog.labs.testing.CloseToMatcher.prototype.matches = function(actualValue) {
+ goog.asserts.assertNumber(actualValue);
+ return Math.abs(this.value_ - actualValue) < this.range_;
+};
+
+
+/**
+ * @override
+ */
+goog.labs.testing.CloseToMatcher.prototype.describe =
+ function(actualValue) {
+ goog.asserts.assertNumber(actualValue);
+ return actualValue + ' is not close to(' + this.range_ + ') ' + this.value_;
+};
+
+
+/**
+ * @param {number} value The expected value.
+ *
+ * @return {!goog.labs.testing.GreaterThanMatcher} A GreaterThanMatcher.
+ */
+function greaterThan(value) {
+ return new goog.labs.testing.GreaterThanMatcher(value);
+}
+
+
+/**
+ * @param {number} value The expected value.
+ *
+ * @return {!goog.labs.testing.GreaterThanEqualToMatcher} A
+ * GreaterThanEqualToMatcher.
+ */
+function greaterThanEqualTo(value) {
+ return new goog.labs.testing.GreaterThanEqualToMatcher(value);
+}
+
+
+/**
+ * @param {number} value The expected value.
+ *
+ * @return {!goog.labs.testing.LessThanMatcher} A LessThanMatcher.
+ */
+function lessThan(value) {
+ return new goog.labs.testing.LessThanMatcher(value);
+}
+
+
+/**
+ * @param {number} value The expected value.
+ *
+ * @return {!goog.labs.testing.LessThanEqualToMatcher} A LessThanEqualToMatcher.
+ */
+function lessThanEqualTo(value) {
+ return new goog.labs.testing.LessThanEqualToMatcher(value);
+}
+
+
+/**
+ * @param {number} value The expected value.
+ *
+ * @return {!goog.labs.testing.EqualToMatcher} An EqualToMatcher.
+ */
+function equalTo(value) {
+ return new goog.labs.testing.EqualToMatcher(value);
+}
+
+
+/**
+ * @param {number} value The expected value.
+ * @param {number} range The maximum allowed difference from the expected value.
+ *
+ * @return {!goog.labs.testing.CloseToMatcher} A CloseToMatcher.
+ */
+function closeTo(value, range) {
+ return new goog.labs.testing.CloseToMatcher(value, range);
+}
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/testing/numbermatcher_test.html b/contexts/data/lib/closure-library/closure/goog/labs/testing/numbermatcher_test.html
new file mode 100644
index 0000000..3d1f40f
--- /dev/null
+++ b/contexts/data/lib/closure-library/closure/goog/labs/testing/numbermatcher_test.html
@@ -0,0 +1,79 @@
+<!DOCTYPE html>
+<html>
+<!--
+Copyright 2012 The Closure Library Authors. All Rights Reserved.
+
+Use of this source code is governed by the Apache License, Version 2.0.
+See the COPYING file for details.
+-->
+<head>
+<title>Closure Unit Tests - Number matchers</title>
+<script src="../../base.js"></script>
+<script>
+
+goog.require('goog.labs.testing.CloseToMatcher');
+goog.require('goog.labs.testing.EqualToMatcher');
+goog.require('goog.labs.testing.GreaterThanEqualToMatcher');
+goog.require('goog.labs.testing.GreaterThanMatcher');
+goog.require('goog.labs.testing.LessThanEqualToMatcher');
+goog.require('goog.labs.testing.LessThanMatcher');
+goog.require('goog.labs.testing.assertThat');
+goog.require('goog.testing.jsunit');
+
+</script>
+</head>
+<body>
+<script>
+
+function testGreaterThan() {
+ goog.labs.testing.assertThat(4, greaterThan(3), '4 > 3');
+ assertMatcherError(function() {
+ goog.labs.testing.assertThat(2, greaterThan(3));
+ }, '2 > 3');
+}
+
+function testGreaterThanEqualTo() {
+ goog.labs.testing.assertThat(5, greaterThanEqualTo(4), '5 >= 4');
+ goog.labs.testing.assertThat(5, greaterThanEqualTo(5), '5 >= 5');
+ assertMatcherError(function() {
+ goog.labs.testing.assertThat(3, greaterThanEqualTo(5));
+ }, '3 >= 5');
+}
+
+function testLessThan() {
+ goog.labs.testing.assertThat(6, lessThan(7), '6 < 7');
+ assertMatcherError(function() {
+ goog.labs.testing.assertThat(7, lessThan(5));
+ }, '7 < 5');
+}
+
+function testLessThanEqualTo() {
+ goog.labs.testing.assertThat(8, lessThanEqualTo(8), '8 <= 8');
+ goog.labs.testing.assertThat(8, lessThanEqualTo(9), '8 <= 9');
+ assertMatcherError(function() {
+ goog.labs.testing.assertThat(7, lessThanEqualTo(5));
+ }, '7 <= 5');
+}
+
+function testEqualTo() {
+ goog.labs.testing.assertThat(7, equalTo(7), '7 equals 7');
+ assertMatcherError(function() {
+ goog.labs.testing.assertThat(7, equalTo(5));
+ }, '7 == 5');
+}
+
+function testCloseTo() {
+ goog.labs.testing.assertThat(7, closeTo(10, 4), '7 within range(4) of 10');
+ assertMatcherError(function() {
+ goog.labs.testing.assertThat(5, closeTo(10, 3));
+ }, '5 within range(3) of 10');
+}
+
+function assertMatcherError(callable, errorString) {
+ var e = assertThrows(errorString || 'callable throws exception', callable);
+ assertTrue(e instanceof goog.labs.testing.MatcherError);
+}
+
+</script>
+</body>
+</html>
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/testing/objectmatcher.js b/contexts/data/lib/closure-library/closure/goog/labs/testing/objectmatcher.js
new file mode 100644
index 0000000..974d024
--- /dev/null
+++ b/contexts/data/lib/closure-library/closure/goog/labs/testing/objectmatcher.js
@@ -0,0 +1,306 @@
+// 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 Provides the built-in object matchers like equalsObject,
+ * hasProperty, instanceOf, etc.
+ */
+
+
+
+goog.provide('goog.labs.testing.HasPropertyMatcher');
+goog.provide('goog.labs.testing.InstanceOfMatcher');
+goog.provide('goog.labs.testing.IsNullMatcher');
+goog.provide('goog.labs.testing.IsNullOrUndefinedMatcher');
+goog.provide('goog.labs.testing.IsUndefinedMatcher');
+goog.provide('goog.labs.testing.ObjectEqualsMatcher');
+
+
+goog.require('goog.labs.testing.Matcher');
+goog.require('goog.string');
+
+
+
+/**
+ * The Equals matcher.
+ *
+ * @param {!Object} expectedObject The expected object.
+ *
+ * @constructor
+ * @implements {goog.labs.testing.Matcher}
+ */
+goog.labs.testing.ObjectEqualsMatcher = function(expectedObject) {
+ /**
+ * @type {!Object}
+ * @private
+ */
+ this.object_ = expectedObject;
+};
+
+
+/**
+ * Determines if two objects are the same.
+ *
+ * @override
+ */
+goog.labs.testing.ObjectEqualsMatcher.prototype.matches =
+ function(actualObject) {
+ return actualObject === this.object_;
+};
+
+
+/**
+ * @override
+ */
+goog.labs.testing.ObjectEqualsMatcher.prototype.describe =
+ function(actualObject) {
+ return 'Input object is not the same as the expected object.';
+};
+
+
+
+/**
+ * The HasProperty matcher.
+ *
+ * @param {string} property Name of the property to test.
+ *
+ * @constructor
+ * @implements {goog.labs.testing.Matcher}
+ */
+goog.labs.testing.HasPropertyMatcher = function(property) {
+ /**
+ * @type {string}
+ * @private
+ */
+ this.property_ = property;
+};
+
+
+/**
+ * Determines if an object has a property.
+ *
+ * @override
+ */
+goog.labs.testing.HasPropertyMatcher.prototype.matches =
+ function(actualObject) {
+ return this.property_ in actualObject;
+};
+
+
+/**
+ * @override
+ */
+goog.labs.testing.HasPropertyMatcher.prototype.describe =
+ function(actualObject) {
+ return 'Object does not have property: ' + this.property_;
+};
+
+
+
+/**
+ * The InstanceOf matcher.
+ *
+ * @param {!Object} object The expected class object.
+ *
+ * @constructor
+ * @implements {goog.labs.testing.Matcher}
+ */
+goog.labs.testing.InstanceOfMatcher = function(object) {
+ /**
+ * @type {!Object}
+ * @private
+ */
+ this.object_ = object;
+};
+
+
+/**
+ * Determines if an object is an instance of another object.
+ *
+ * @override
+ */
+goog.labs.testing.InstanceOfMatcher.prototype.matches =
+ function(actualObject) {
+ return actualObject instanceof this.object_;
+};
+
+
+/**
+ * @override
+ */
+goog.labs.testing.InstanceOfMatcher.prototype.describe =
+ function(actualObject) {
+ return 'Input object is not an instance of the expected object';
+};
+
+
+
+/**
+ * The IsNullOrUndefined matcher.
+ *
+ * @constructor
+ * @implements {goog.labs.testing.Matcher}
+ */
+goog.labs.testing.IsNullOrUndefinedMatcher = function() {};
+
+
+/**
+ * Determines if input value is null or undefined.
+ *
+ * @override
+ */
+goog.labs.testing.IsNullOrUndefinedMatcher.prototype.matches =
+ function(actualValue) {
+ return !goog.isDefAndNotNull(actualValue);
+};
+
+
+/**
+ * @override
+ */
+goog.labs.testing.IsNullOrUndefinedMatcher.prototype.describe =
+ function(actualValue) {
+ return actualValue + ' is not null or undefined.';
+};
+
+
+
+/**
+ * The IsNull matcher.
+ *
+ * @constructor
+ * @implements {goog.labs.testing.Matcher}
+ */
+goog.labs.testing.IsNullMatcher = function() {};
+
+
+/**
+ * Determines if input value is null.
+ *
+ * @override
+ */
+goog.labs.testing.IsNullMatcher.prototype.matches =
+ function(actualValue) {
+ return goog.isNull(actualValue);
+};
+
+
+/**
+ * @override
+ */
+goog.labs.testing.IsNullMatcher.prototype.describe =
+ function(actualValue) {
+ return actualValue + ' is not null.';
+};
+
+
+
+/**
+ * The IsUndefined matcher.
+ *
+ * @constructor
+ * @implements {goog.labs.testing.Matcher}
+ */
+goog.labs.testing.IsUndefinedMatcher = function() {};
+
+
+/**
+ * Determines if input value is undefined.
+ *
+ * @override
+ */
+goog.labs.testing.IsUndefinedMatcher.prototype.matches =
+ function(actualValue) {
+ return !goog.isDef(actualValue);
+};
+
+
+/**
+ * @override
+ */
+goog.labs.testing.IsUndefinedMatcher.prototype.describe =
+ function(actualValue) {
+ return actualValue + ' is not undefined.';
+};
+
+
+/**
+ * Returns a matcher that matches objects that are equal to the input object.
+ * Equality in this case means the two objects are references to the same
+ * object.
+ *
+ * @param {!Object} object The expected object.
+ *
+ * @return {!goog.labs.testing.ObjectEqualsMatcher} A
+ * ObjectEqualsMatcher.
+ */
+function equalsObject(object) {
+ return new goog.labs.testing.ObjectEqualsMatcher(object);
+}
+
+
+/**
+ * Returns a matcher that matches objects that contain the input property.
+ *
+ * @param {string} property The property name to check.
+ *
+ * @return {!goog.labs.testing.HasPropertyMatcher} A HasPropertyMatcher.
+ */
+function hasProperty(property) {
+ return new goog.labs.testing.HasPropertyMatcher(property);
+}
+
+
+/**
+ * Returns a matcher that matches instances of the input class.
+ *
+ * @param {!Object} object The class object.
+ *
+ * @return {!goog.labs.testing.InstanceOfMatcher} A
+ * InstanceOfMatcher.
+ */
+function instanceOfClass(object) {
+ return new goog.labs.testing.InstanceOfMatcher(object);
+}
+
+
+/**
+ * Returns a matcher that matches all null values.
+ *
+ * @return {!goog.labs.testing.IsNullMatcher} A IsNullMatcher.
+ */
+function isNull() {
+ return new goog.labs.testing.IsNullMatcher();
+}
+
+
+/**
+ * Returns a matcher that matches all null and undefined values.
+ *
+ * @return {!goog.labs.testing.IsNullOrUndefinedMatcher} A
+ * IsNullOrUndefinedMatcher.
+ */
+function isNullOrUndefined() {
+ return new goog.labs.testing.IsNullOrUndefinedMatcher();
+}
+
+
+/**
+ * Returns a matcher that matches undefined values.
+ *
+ * @return {!goog.labs.testing.IsUndefinedMatcher} A IsUndefinedMatcher.
+ */
+function isUndefined() {
+ return new goog.labs.testing.IsUndefinedMatcher();
+}
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/testing/objectmatcher_test.html b/contexts/data/lib/closure-library/closure/goog/labs/testing/objectmatcher_test.html
new file mode 100644
index 0000000..92cc2de
--- /dev/null
+++ b/contexts/data/lib/closure-library/closure/goog/labs/testing/objectmatcher_test.html
@@ -0,0 +1,102 @@
+<!DOCTYPE html>
+<html>
+<!--
+Copyright 2012 The Closure Library Authors. All Rights Reserved.
+
+Use of this source code is governed by the Apache License, Version 2.0.
+See the COPYING file for details.
+-->
+<head>
+<title>Closure Unit Tests - Object matchers</title>
+<script src="../../base.js"></script>
+<script>
+
+goog.require('goog.labs.testing.ObjectEqualsMatcher');
+goog.require('goog.labs.testing.HasPropertyMatcher');
+goog.require('goog.labs.testing.InstanceOfMatcher');
+goog.require('goog.labs.testing.IsNullOrUndefinedMatcher');
+goog.require('goog.labs.testing.IsNullMatcher');
+goog.require('goog.labs.testing.IsUndefinedMatcher');
+goog.require('goog.labs.testing.assertThat');
+goog.require('goog.testing.jsunit');
+
+</script>
+</head>
+<body>
+<script>
+
+function testObjectEquals() {
+ var obj1 = {x: 1};
+ var obj2 = obj1;
+ goog.labs.testing.assertThat(obj1, equalsObject(obj2), 'obj1 equals obj2');
+
+ assertMatcherError(function() {
+ goog.labs.testing.assertThat({x: 1}, equalsObject({}));
+ }, 'equalsObject does not throw exception on failure');
+}
+
+function testInstanceOf() {
+ function expected() {
+ this.x = 1;
+ }
+ var input = new expected();
+ goog.labs.testing.assertThat(input, instanceOfClass(expected),
+ 'input is an instance of expected');
+
+ assertMatcherError(function() {
+ goog.labs.testing.assertThat(5, instanceOfClass(function() {}));
+ }, 'instanceOfClass does not throw exception on failure');
+}
+
+function testHasProperty() {
+ goog.labs.testing.assertThat({x: 1}, hasProperty('x'),
+ '{x:1} has property x}');
+
+ assertMatcherError(function() {
+ goog.labs.testing.assertThat({x: 1}, hasProperty('y'));
+ }, 'hasProperty does not throw exception on failure');
+}
+
+function testIsNull() {
+ goog.labs.testing.assertThat(null, isNull(), 'null is null');
+
+ assertMatcherError(function() {
+ goog.labs.testing.assertThat(5, isNull());
+ }, 'isNull does not throw exception on failure');
+}
+
+function testIsNullOrUndefined() {
+ var x;
+ goog.labs.testing.assertThat(undefined, isNullOrUndefined(),
+ 'undefined is null or undefined');
+ goog.labs.testing.assertThat(x, isNullOrUndefined(),
+ 'undefined is null or undefined');
+ x = null;
+ goog.labs.testing.assertThat(null, isNullOrUndefined(),
+ 'null is null or undefined');
+ goog.labs.testing.assertThat(x, isNullOrUndefined(),
+ 'null is null or undefined');
+
+ assertMatcherError(function() {
+ goog.labs.testing.assertThat(5, isNullOrUndefined());
+ }, 'isNullOrUndefined does not throw exception on failure');
+}
+
+function testIsUndefined() {
+ var x;
+ goog.labs.testing.assertThat(undefined, isUndefined(),
+ 'undefined is undefined');
+ goog.labs.testing.assertThat(x, isUndefined(), 'undefined is undefined');
+
+ assertMatcherError(function() {
+ goog.labs.testing.assertThat(5, isUndefined());
+ }, 'isUndefined does not throw exception on failure');
+}
+
+function assertMatcherError(callable, errorString) {
+ var e = assertThrows(errorString || 'callable throws exception', callable);
+ assertTrue(e instanceof goog.labs.testing.MatcherError);
+}
+</script>
+</body>
+</html>
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/testing/stringmatcher.js b/contexts/data/lib/closure-library/closure/goog/labs/testing/stringmatcher.js
new file mode 100644
index 0000000..26daabc
--- /dev/null
+++ b/contexts/data/lib/closure-library/closure/goog/labs/testing/stringmatcher.js
@@ -0,0 +1,350 @@
+// 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 Provides the built-in string matchers like containsString,
+ * startsWith, endsWith, etc.
+ */
+
+
+goog.provide('goog.labs.testing.ContainsStringMatcher');
+goog.provide('goog.labs.testing.EndsWithMatcher');
+goog.provide('goog.labs.testing.EqualToIgnoringCaseMatcher');
+goog.provide('goog.labs.testing.EqualToIgnoringWhitespaceMatcher');
+goog.provide('goog.labs.testing.EqualsMatcher');
+goog.provide('goog.labs.testing.StartsWithMatcher');
+goog.provide('goog.labs.testing.StringContainsInOrderMatcher');
+
+
+goog.require('goog.asserts');
+goog.require('goog.labs.testing.Matcher');
+goog.require('goog.string');
+
+
+
+/**
+ * The ContainsString matcher.
+ *
+ * @param {string} value The expected string.
+ *
+ * @constructor
+ * @implements {goog.labs.testing.Matcher}
+ */
+goog.labs.testing.ContainsStringMatcher = function(value) {
+ /**
+ * @type {string}
+ * @private
+ */
+ this.value_ = value;
+};
+
+
+/**
+ * Determines if input string contains the expected string.
+ *
+ * @override
+ */
+goog.labs.testing.ContainsStringMatcher.prototype.matches =
+ function(actualValue) {
+ goog.asserts.assertString(actualValue);
+ return goog.string.contains(actualValue, this.value_);
+};
+
+
+/**
+ * @override
+ */
+goog.labs.testing.ContainsStringMatcher.prototype.describe =
+ function(actualValue) {
+ return actualValue + ' does not contain ' + this.value_;
+};
+
+
+
+/**
+ * The EndsWith matcher.
+ *
+ * @param {string} value The expected string.
+ *
+ * @constructor
+ * @implements {goog.labs.testing.Matcher}
+ */
+goog.labs.testing.EndsWithMatcher = function(value) {
+ /**
+ * @type {string}
+ * @private
+ */
+ this.value_ = value;
+};
+
+
+/**
+ * Determines if input string ends with the expected string.
+ *
+ * @override
+ */
+goog.labs.testing.EndsWithMatcher.prototype.matches = function(actualValue) {
+ goog.asserts.assertString(actualValue);
+ return goog.string.endsWith(actualValue, this.value_);
+};
+
+
+/**
+ * @override
+ */
+goog.labs.testing.EndsWithMatcher.prototype.describe =
+ function(actualValue) {
+ return actualValue + ' does not end with ' + this.value_;
+};
+
+
+
+/**
+ * The EqualToIgnoringWhitespace matcher.
+ *
+ * @param {string} value The expected string.
+ *
+ * @constructor
+ * @implements {goog.labs.testing.Matcher}
+ */
+goog.labs.testing.EqualToIgnoringWhitespaceMatcher = function(value) {
+ /**
+ * @type {string}
+ * @private
+ */
+ this.value_ = value;
+};
+
+
+/**
+ * Determines if input string contains the expected string.
+ *
+ * @override
+ */
+goog.labs.testing.EqualToIgnoringWhitespaceMatcher.prototype.matches =
+ function(actualValue) {
+ goog.asserts.assertString(actualValue);
+ var string1 = goog.string.collapseWhitespace(actualValue);
+
+ return goog.string.caseInsensitiveCompare(this.value_, string1) === 0;
+};
+
+
+/**
+ * @override
+ */
+goog.labs.testing.EqualToIgnoringWhitespaceMatcher.prototype.describe =
+ function(actualValue) {
+ return actualValue + ' is not equal(ignoring whitespace) to ' + this.value_;
+};
+
+
+
+/**
+ * The Equals matcher.
+ *
+ * @param {string} value The expected string.
+ *
+ * @constructor
+ * @implements {goog.labs.testing.Matcher}
+ */
+goog.labs.testing.EqualsMatcher = function(value) {
+ /**
+ * @type {string}
+ * @private
+ */
+ this.value_ = value;
+};
+
+
+/**
+ * Determines if input string is equal to the expected string.
+ *
+ * @override
+ */
+goog.labs.testing.EqualsMatcher.prototype.matches = function(actualValue) {
+ goog.asserts.assertString(actualValue);
+ return this.value_ === actualValue;
+};
+
+
+/**
+ * @override
+ */
+goog.labs.testing.EqualsMatcher.prototype.describe =
+ function(actualValue) {
+ return actualValue + ' is not equal to ' + this.value_;
+};
+
+
+
+/**
+ * The StartsWith matcher.
+ *
+ * @param {string} value The expected string.
+ *
+ * @constructor
+ * @implements {goog.labs.testing.Matcher}
+ */
+goog.labs.testing.StartsWithMatcher = function(value) {
+ /**
+ * @type {string}
+ * @private
+ */
+ this.value_ = value;
+};
+
+
+/**
+ * Determines if input string starts with the expected string.
+ *
+ * @override
+ */
+goog.labs.testing.StartsWithMatcher.prototype.matches = function(actualValue) {
+ goog.asserts.assertString(actualValue);
+ return goog.string.startsWith(actualValue, this.value_);
+};
+
+
+/**
+ * @override
+ */
+goog.labs.testing.StartsWithMatcher.prototype.describe =
+ function(actualValue) {
+ return actualValue + ' does not start with ' + this.value_;
+};
+
+
+
+/**
+ * The StringContainsInOrdermatcher.
+ *
+ * @param {Array.<string>} values The expected string values.
+ *
+ * @constructor
+ * @implements {goog.labs.testing.Matcher}
+ */
+goog.labs.testing.StringContainsInOrderMatcher = function(values) {
+ /**
+ * @type {Array.<string>}
+ * @private
+ */
+ this.values_ = values;
+};
+
+
+/**
+ * Determines if input string contains, in order, the expected array of strings.
+ *
+ * @override
+ */
+goog.labs.testing.StringContainsInOrderMatcher.prototype.matches =
+ function(actualValue) {
+ goog.asserts.assertString(actualValue);
+ var currentIndex, previousIndex = 0;
+ for (var i = 0; i < this.values_.length; i++) {
+ currentIndex = goog.string.contains(actualValue, this.values_[i]);
+ if (currentIndex < 0 || currentIndex < previousIndex) {
+ return false;
+ }
+ previousIndex = currentIndex;
+ }
+ return true;
+};
+
+
+/**
+ * @override
+ */
+goog.labs.testing.StringContainsInOrderMatcher.prototype.describe =
+ function(actualValue) {
+ return actualValue + ' does not contain the expected values in order.';
+};
+
+
+/**
+ * Matches a string containing the given string.
+ *
+ * @param {string} value The expected value.
+ *
+ * @return {!goog.labs.testing.ContainsStringMatcher} A
+ * ContainsStringMatcher.
+ */
+function containsString(value) {
+ return new goog.labs.testing.ContainsStringMatcher(value);
+}
+
+
+/**
+ * Matches a string that ends with the given string.
+ *
+ * @param {string} value The expected value.
+ *
+ * @return {!goog.labs.testing.EndsWithMatcher} A
+ * EndsWithMatcher.
+ */
+function endsWith(value) {
+ return new goog.labs.testing.EndsWithMatcher(value);
+}
+
+
+/**
+ * Matches a string that equals (ignoring whitespace) the given string.
+ *
+ * @param {string} value The expected value.
+ *
+ * @return {!goog.labs.testing.EqualToIgnoringWhitespaceMatcher} A
+ * EqualToIgnoringWhitespaceMatcher.
+ */
+function equalToIgnoringWhitespace(value) {
+ return new goog.labs.testing.EqualToIgnoringWhitespaceMatcher(value);
+}
+
+
+/**
+ * Matches a string that equals the given string.
+ *
+ * @param {string} value The expected value.
+ *
+ * @return {!goog.labs.testing.EqualsMatcher} A EqualsMatcher.
+ */
+function equals(value) {
+ return new goog.labs.testing.EqualsMatcher(value);
+}
+
+
+/**
+ * Matches a string that starts with the given string.
+ *
+ * @param {string} value The expected value.
+ *
+ * @return {!goog.labs.testing.StartsWithMatcher} A
+ * StartsWithMatcher.
+ */
+function startsWith(value) {
+ return new goog.labs.testing.StartsWithMatcher(value);
+}
+
+
+/**
+ * Matches a string that contains the given strings in order.
+ *
+ * @param {Array.<string>} values The expected value.
+ *
+ * @return {!goog.labs.testing.StringContainsInOrderMatcher} A
+ * StringContainsInOrderMatcher.
+ */
+function stringContainsInOrder(values) {
+ return new goog.labs.testing.StringContainsInOrderMatcher(values);
+}
diff --git a/contexts/data/lib/closure-library/closure/goog/labs/testing/stringmatcher_test.html b/contexts/data/lib/closure-library/closure/goog/labs/testing/stringmatcher_test.html
new file mode 100644
index 0000000..9ad6dd4
--- /dev/null
+++ b/contexts/data/lib/closure-library/closure/goog/labs/testing/stringmatcher_test.html
@@ -0,0 +1,91 @@
+<!DOCTYPE html>
+<html>
+<!--
+Copyright 2012 The Closure Library Authors. All Rights Reserved.
+
+Use of this source code is governed by the Apache License, Version 2.0.
+See the COPYING file for details.
+-->
+<head>
+<title>Closure Unit Tests - String matchers</title>
+<script src="../../base.js"></script>
+<script>
+
+goog.require('goog.labs.testing.ContainsStringMatcher');
+goog.require('goog.labs.testing.EndsWithMatcher');
+goog.require('goog.labs.testing.EqualsMatcher');
+goog.require('goog.labs.testing.EqualToIgnoringWhitespaceMatcher');
+goog.require('goog.labs.testing.StartsWithMatcher');
+goog.require('goog.labs.testing.StringContainsInOrderMatcher');
+goog.require('goog.labs.testing.assertThat');
+goog.require('goog.testing.jsunit');
+
+</script>
+</head>
+<body>
+<script>
+
+function testContainsString() {
+ goog.labs.testing.assertThat('hello', containsString('ell'),
+ 'hello contains ell');
+
+ assertMatcherError(function() {
+ goog.labs.testing.assertThat('hello', containsString('world!'));
+ }, 'containsString should throw exception when it fails');
+}
+
+function testEndsWith() {
+ goog.labs.testing.assertThat('hello', endsWith('llo'), 'hello ends with llo');
+
+ assertMatcherError(function() {
+ goog.labs.testing.assertThat('minutes', endsWith('midnight'));
+ }, 'endsWith should throw exception when it fails');
+}
+
+function testEqualToIgnoringWhitespace() {
+ goog.labs.testing.assertThat(' h\n EL L\tO',
+ equalToIgnoringWhitespace("h el l o"),
+ '" h EL L\tO " is equal to "h el l o"');
+
+ assertMatcherError(function() {
+ goog.labs.testing.assertThat('hybrid', equalToIgnoringWhitespace('theory'));
+ }, 'equalToIgnoringWhitespace should throw exception when it fails');
+}
+
+function testEquals() {
+ goog.labs.testing.assertThat('hello', equals('hello'),
+ 'hello equals hello');
+
+ assertMatcherError(function() {
+ goog.labs.testing.assertThat('thousand', equals('suns'));
+ }, 'equals should throw exception when it fails');
+}
+
+function testStartsWith() {
+ goog.labs.testing.assertThat('hello', startsWith('hel'),
+ 'hello starts with hel');
+
+ assertMatcherError(function() {
+ goog.labs.testing.assertThat('linkin', startsWith('park'));
+ }, 'startsWith should throw exception when it fails');
+}
+
+function testStringContainsInOrder() {
+ goog.labs.testing.assertThat('hello',
+ stringContainsInOrder(['h', 'el', 'el', 'l', 'o']),
+ 'hello contains in order: [h, el, l, o]');
+
+ assertMatcherError(function() {
+ goog.labs.testing.assertThat('hybrid', stringContainsInOrder(['hy', 'brid',
+ 'theory']));
+ }, 'stringContainsInOrder should throw exception when it fails');
+}
+
+function assertMatcherError(callable, errorString) {
+ var e = assertThrows(errorString || 'callable throws exception', callable);
+ assertTrue(e instanceof goog.labs.testing.MatcherError);
+}
+
+</script>
+</body>
+</html>