Updated
This commit is contained in:
@@ -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.labs.result.SimpleResult');
|
||||
goog.require('goog.net.EventType');
|
||||
goog.require('goog.userAgent');
|
||||
|
||||
|
||||
/**
|
||||
* Loads a single image. Useful for preloading images. May be combined with
|
||||
* goog.labs.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.labs.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.labs.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;
|
||||
};
|
||||
@@ -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)
|
||||
*/
|
||||
|
||||
|
||||
goog.provide('goog.labs.net.imageTest');
|
||||
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.labs.net.image');
|
||||
goog.require('goog.labs.result');
|
||||
goog.require('goog.labs.result.Result');
|
||||
goog.require('goog.net.EventType');
|
||||
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.labs.result.waitOnSuccess(result, function(value) {
|
||||
|
||||
assertEquals(goog.labs.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.labs.result.wait(result, function(result) {
|
||||
|
||||
assertEquals(goog.labs.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.labs.result.waitOnSuccess(result, function(value) {
|
||||
assertEquals(goog.labs.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.labs.result.waitOnSuccess(result, function(value) {
|
||||
assertEquals(goog.labs.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();
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,444 @@
|
||||
// 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, results 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.debug.Error');
|
||||
goog.require('goog.json');
|
||||
goog.require('goog.labs.result');
|
||||
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 Result = goog.labs.result.Result;
|
||||
var SimpleResult = goog.labs.result.SimpleResult;
|
||||
var Wait = goog.labs.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;
|
||||
|
||||
|
||||
/**
|
||||
* 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 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.labs.result.transform(result,
|
||||
_.getResponseText_);
|
||||
return transformedResult;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 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.labs.result.transform(result,
|
||||
_.getResponseText_);
|
||||
return transformedResult;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 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 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;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 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.labs.result.Result.
|
||||
goog.labs.result.waitOnError(result, function(result) {
|
||||
if (result.isCanceled()) {
|
||||
xhr.abort();
|
||||
xhr.onreadystatechange = goog.nullFunction;
|
||||
}
|
||||
});
|
||||
|
||||
function callback(data) {
|
||||
result.setValue(data);
|
||||
}
|
||||
|
||||
function errback(err) {
|
||||
result.setError(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));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 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 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;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 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.labs.result.transform(result,
|
||||
_.getResponseText_);
|
||||
var prefixStrippedResult = resultWithResponseText;
|
||||
if (options && options.xssiPrefix) {
|
||||
prefixStrippedResult = goog.labs.result.transform(resultWithResponseText,
|
||||
goog.partial(_.stripXssiPrefix_, options.xssiPrefix));
|
||||
}
|
||||
|
||||
var jsonParsedResult = goog.labs.result.transform(prefixStrippedResult,
|
||||
goog.json.parse);
|
||||
return jsonParsedResult;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
|
||||
/** @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);
|
||||
|
||||
/**
|
||||
* The HTTP status code for the error.
|
||||
* @type {number}
|
||||
*/
|
||||
this.status = status;
|
||||
};
|
||||
goog.inherits(_.HttpError, _.Error);
|
||||
|
||||
|
||||
/** @override */
|
||||
_.HttpError.prototype.name = 'XhrHttpError';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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_;
|
||||
};
|
||||
@@ -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) {};
|
||||
@@ -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);
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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_;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -0,0 +1,57 @@
|
||||
// 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 adaptor from a Result to a Deferred.
|
||||
*
|
||||
* TODO (vbhasin): cancel() support.
|
||||
* TODO (vbhasin): See if we can make this a static.
|
||||
* TODO (gboyer, vbhasin): Rename to "Adapter" once this graduates; this is the
|
||||
* proper programmer spelling.
|
||||
*/
|
||||
|
||||
|
||||
goog.provide('goog.labs.result.DeferredAdaptor');
|
||||
|
||||
goog.require('goog.async.Deferred');
|
||||
goog.require('goog.labs.result');
|
||||
goog.require('goog.labs.result.Result');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* An adaptor from Result to a Deferred, for use with existing Deferred chains.
|
||||
*
|
||||
* @param {!goog.labs.result.Result} result A result.
|
||||
* @constructor
|
||||
* @extends {goog.async.Deferred}
|
||||
*/
|
||||
goog.labs.result.DeferredAdaptor = function(result) {
|
||||
goog.base(this);
|
||||
goog.labs.result.wait(result, function(result) {
|
||||
if (this.hasFired()) {
|
||||
return;
|
||||
}
|
||||
if (result.getState() == goog.labs.result.Result.State.SUCCESS) {
|
||||
this.callback(result.getValue());
|
||||
} else if (result.getState() == goog.labs.result.Result.State.ERROR) {
|
||||
if (result.getError() instanceof goog.labs.result.Result.CancelError) {
|
||||
this.cancel();
|
||||
} else {
|
||||
this.errback(result.getError());
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
};
|
||||
goog.inherits(goog.labs.result.DeferredAdaptor, goog.async.Deferred);
|
||||
@@ -0,0 +1,108 @@
|
||||
// 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 Defines an interface that represents a Result.
|
||||
*/
|
||||
|
||||
goog.provide('goog.labs.result.Result');
|
||||
|
||||
goog.require('goog.debug.Error');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A Result object represents a value returned by an asynchronous
|
||||
* operation at some point in the future (e.g. a network fetch). This is akin
|
||||
* to a 'Promise' or a 'Future' in other languages and frameworks.
|
||||
*
|
||||
* @interface
|
||||
*/
|
||||
goog.labs.result.Result = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* Attaches handlers to be called when the value of this Result is available.
|
||||
*
|
||||
* @param {!function(!goog.labs.result.Result)} handler The function called when
|
||||
* the value is available. The function is passed the Result object as the
|
||||
* only argument.
|
||||
*/
|
||||
goog.labs.result.Result.prototype.wait = function(handler) {};
|
||||
|
||||
|
||||
/**
|
||||
* The States this object can be in.
|
||||
*
|
||||
* @enum {string}
|
||||
*/
|
||||
goog.labs.result.Result.State = {
|
||||
/** The operation was a success and the value is available. */
|
||||
SUCCESS: 'success',
|
||||
|
||||
/** The operation resulted in an error. */
|
||||
ERROR: 'error',
|
||||
|
||||
/** The operation is incomplete and the value is not yet available. */
|
||||
PENDING: 'pending'
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {!goog.labs.result.Result.State} The state of this Result.
|
||||
*/
|
||||
goog.labs.result.Result.prototype.getState = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* @return {*} The value of this Result. Will return undefined if the Result is
|
||||
* pending or was an error.
|
||||
*/
|
||||
goog.labs.result.Result.prototype.getValue = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* @return {*} The error slug for this Result. Will return undefined if the
|
||||
* Result was a success, the error slug was not set, or if the Result is
|
||||
* pending.
|
||||
*/
|
||||
goog.labs.result.Result.prototype.getError = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* Cancels the current Result, invoking the canceler function, if set.
|
||||
*
|
||||
* @return {boolean} Whether the Result was canceled.
|
||||
*/
|
||||
goog.labs.result.Result.prototype.cancel = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* @return {boolean} Whether this Result was canceled.
|
||||
*/
|
||||
goog.labs.result.Result.prototype.isCanceled = function() {};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The value to be passed to the error handlers invoked upon cancellation.
|
||||
* @constructor
|
||||
* @param {string=} opt_msg The error message for CancelError.
|
||||
* @extends {goog.debug.Error}
|
||||
*/
|
||||
goog.labs.result.Result.CancelError = function(opt_msg) {
|
||||
var msg = opt_msg || 'Result canceled';
|
||||
goog.base(this, msg);
|
||||
};
|
||||
goog.inherits(goog.labs.result.Result.CancelError, goog.debug.Error);
|
||||
@@ -0,0 +1,382 @@
|
||||
// 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 This file provides primitives and tools (wait, transform,
|
||||
* chain, combine) that make it easier to work with Results. This section
|
||||
* gives an overview of their functionality along with some examples and the
|
||||
* actual definitions have detailed descriptions next to them.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.labs.result');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.labs.result.Result');
|
||||
goog.require('goog.labs.result.SimpleResult');
|
||||
|
||||
|
||||
/**
|
||||
* Calls the handler on resolution of the result (success or failure).
|
||||
* The handler is passed the result object as the only parameter. The call will
|
||||
* be immediate if the result is no longer pending.
|
||||
*
|
||||
* Example:
|
||||
* <pre>
|
||||
*
|
||||
* var result = xhr.get('testdata/xhr_test_text.data');
|
||||
*
|
||||
* // Wait for the result to be resolved and alert it's state.
|
||||
* goog.labs.result.wait(result, function(result) {
|
||||
* alert('State: ' + result.getState());
|
||||
* });
|
||||
* </pre>
|
||||
*
|
||||
* @param {!goog.labs.result.Result} result The result to install the handlers.
|
||||
* @param {!function(!goog.labs.result.Result)} handler The handler to be
|
||||
* called. The handler is passed the result object as the only parameter.
|
||||
* @param {!Object=} opt_scope Optional scope for the handler.
|
||||
*/
|
||||
goog.labs.result.wait = function(result, handler, opt_scope) {
|
||||
result.wait(opt_scope ? goog.bind(handler, opt_scope) : handler);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Calls the handler if the result succeeds. The result object is the only
|
||||
* parameter passed to the handler. The call will be immediate if the result
|
||||
* has already succeeded.
|
||||
*
|
||||
* Example:
|
||||
* <pre>
|
||||
*
|
||||
* var result = xhr.get('testdata/xhr_test_text.data');
|
||||
*
|
||||
* // attach a success handler.
|
||||
* goog.labs.result.waitOnSuccess(result, function(result) {
|
||||
* var datavalue = result.getvalue();
|
||||
* alert('value : ' + datavalue);
|
||||
* });
|
||||
* </pre>
|
||||
*
|
||||
* @param {!goog.labs.result.Result} result The result to install the handlers.
|
||||
* @param {!function(*, !goog.labs.result.Result)} handler The handler to be
|
||||
* called. The handler is passed the result value and the result as
|
||||
* parameters.
|
||||
* @param {!Object=} opt_scope Optional scope for the handler.
|
||||
*/
|
||||
goog.labs.result.waitOnSuccess = function(result, handler, opt_scope) {
|
||||
goog.labs.result.wait(result, function(res) {
|
||||
if (res.getState() == goog.labs.result.Result.State.SUCCESS) {
|
||||
// 'this' refers to opt_scope
|
||||
handler.call(this, res.getValue(), res);
|
||||
}
|
||||
}, opt_scope);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Calls the handler if the result action errors. The result object is passed as
|
||||
* the only parameter to the handler. The call will be immediate if the result
|
||||
* object has already resolved to an error.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
* var result = xhr.get('testdata/xhr_test_text.data');
|
||||
*
|
||||
* // Attach a failure handler.
|
||||
* goog.labs.result.waitOnError(result, function(error) {
|
||||
* // Failed asynchronous call!
|
||||
* });
|
||||
* </pre>
|
||||
*
|
||||
* @param {!goog.labs.result.Result} result The result to install the handlers.
|
||||
* @param {!function(!goog.labs.result.Result)} handler The handler to be
|
||||
* called. The handler is passed the result object as the only parameter.
|
||||
* @param {!Object=} opt_scope Optional scope for the handler.
|
||||
*/
|
||||
goog.labs.result.waitOnError = function(result, handler, opt_scope) {
|
||||
goog.labs.result.wait(result, function(res) {
|
||||
if (res.getState() == goog.labs.result.Result.State.ERROR) {
|
||||
// 'this' refers to opt_scope
|
||||
handler.call(this, res);
|
||||
}
|
||||
}, opt_scope);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Given a result and a transform function, returns a new result whose value,
|
||||
* on success, will be the value of the given result after having been passed
|
||||
* through the transform function.
|
||||
*
|
||||
* If the given result is an error, the returned result is also an error and the
|
||||
* transform will not be called.
|
||||
*
|
||||
* Example:
|
||||
* <pre>
|
||||
*
|
||||
* var result = xhr.getJson('testdata/xhr_test_json.data');
|
||||
*
|
||||
* // Transform contents of returned data using 'processJson' and create a
|
||||
* // transformed result to use returned JSON.
|
||||
* var transformedResult = goog.labs.result.transform(result, processJson);
|
||||
*
|
||||
* // Attach success and failure handlers to the tranformed result.
|
||||
* goog.labs.result.waitOnSuccess(transformedResult, function(result) {
|
||||
* var jsonData = result.getValue();
|
||||
* assertEquals('ok', jsonData['stat']);
|
||||
* });
|
||||
*
|
||||
* goog.labs.result.waitOnError(transformedResult, function(error) {
|
||||
* // Failed getJson call
|
||||
* });
|
||||
* </pre>
|
||||
*
|
||||
* @param {!goog.labs.result.Result} result The result whose value will be
|
||||
* transformed.
|
||||
* @param {!Function} transformer The transformer
|
||||
* function. The return value of this function will become the value of the
|
||||
* returned result.
|
||||
*
|
||||
* @return {!goog.labs.result.Result} A new Result whose eventual value will be
|
||||
* the returned value of the transformer function.
|
||||
*/
|
||||
goog.labs.result.transform = function(result, transformer) {
|
||||
var returnedResult = new goog.labs.result.SimpleResult();
|
||||
|
||||
goog.labs.result.wait(result, function(res) {
|
||||
if (res.getState() == goog.labs.result.Result.State.SUCCESS) {
|
||||
returnedResult.setValue(transformer(res.getValue()));
|
||||
} else {
|
||||
returnedResult.setError(res.getError());
|
||||
}
|
||||
});
|
||||
|
||||
return returnedResult;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The chain function aids in chaining of asynchronous Results. This provides a
|
||||
* convenience for use cases where asynchronous operations must happen serially
|
||||
* i.e. subsequent asynchronous operations are dependent on data returned by
|
||||
* prior asynchronous operations.
|
||||
*
|
||||
* It accepts a result and an action callback as arguments and returns a
|
||||
* result. The action callback is called when the first result succeeds and is
|
||||
* supposed to return a second result. The returned result is resolved when one
|
||||
* of both of the results resolve (depending on their success or failure.) The
|
||||
* state and value of the returned result in the various cases is documented
|
||||
* below:
|
||||
*
|
||||
* First Result State: Second Result State: Returned Result State:
|
||||
* SUCCESS SUCCESS SUCCESS
|
||||
* SUCCESS ERROR ERROR
|
||||
* ERROR Not created ERROR
|
||||
*
|
||||
* The value of the returned result, in the case both results succeed, is the
|
||||
* value of the second result (the result returned by the action callback.)
|
||||
*
|
||||
* Example:
|
||||
* <pre>
|
||||
*
|
||||
* var testDataResult = xhr.get('testdata/xhr_test_text.data');
|
||||
*
|
||||
* // Chain this result to perform another asynchronous operation when this
|
||||
* // Result is resolved.
|
||||
* var chainedResult = goog.labs.result.chain(testDataResult,
|
||||
* function(testDataResult) {
|
||||
*
|
||||
* // The result value of testDataResult is the URL for JSON data.
|
||||
* var jsonDataUrl = testDataResult.getValue();
|
||||
*
|
||||
* // Create a new Result object when the original result is resolved.
|
||||
* var jsonResult = xhr.getJson(jsonDataUrl);
|
||||
*
|
||||
* // Return the newly created Result.
|
||||
* return jsonResult;
|
||||
* });
|
||||
*
|
||||
* // The chained result resolves to success when both results resolve to
|
||||
* // success.
|
||||
* goog.labs.result.waitOnSuccess(chainedResult, function(result) {
|
||||
*
|
||||
* // At this point, both results have succeeded and we can use the JSON
|
||||
* // data returned by the second asynchronous call.
|
||||
* var jsonData = result.getValue();
|
||||
* assertEquals('ok', jsonData['stat']);
|
||||
* });
|
||||
*
|
||||
* // Attach the error handler to be called when either Result fails.
|
||||
* goog.labs.result.waitOnError(chainedResult, function(result) {
|
||||
* alert('chained result failed!');
|
||||
* });
|
||||
* </pre>
|
||||
*
|
||||
* @param {!goog.labs.result.Result} result The result to chain.
|
||||
* @param {!function(!goog.labs.result.Result):!goog.labs.result.Result}
|
||||
* actionCallback The callback called when the result is resolved. This
|
||||
* callback must return a Result.
|
||||
*
|
||||
* @return {!goog.labs.result.Result} A result that is resolved when both
|
||||
* the given Result and the Result returned by the actionCallback have
|
||||
* resolved.
|
||||
*/
|
||||
goog.labs.result.chain = function(result, actionCallback) {
|
||||
var returnedResult = new goog.labs.result.SimpleResult();
|
||||
|
||||
// Wait for the first action.
|
||||
goog.labs.result.wait(result, function(result) {
|
||||
if (result.getState() == goog.labs.result.Result.State.SUCCESS) {
|
||||
|
||||
// The first action succeeded. Chain the dependent action.
|
||||
var dependentResult = actionCallback(result);
|
||||
goog.labs.result.wait(dependentResult, function(dependentResult) {
|
||||
|
||||
// The dependent action completed. Set the returned result based on the
|
||||
// dependent action's outcome.
|
||||
if (dependentResult.getState() ==
|
||||
goog.labs.result.Result.State.SUCCESS) {
|
||||
returnedResult.setValue(dependentResult.getValue());
|
||||
} else {
|
||||
returnedResult.setError(dependentResult.getError());
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// First action failed, the returned result should also fail.
|
||||
returnedResult.setError(result.getError());
|
||||
}
|
||||
});
|
||||
|
||||
return returnedResult;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns a result that waits on all given results to resolve. Once all have
|
||||
* resolved, the returned result will succeed (and never error).
|
||||
*
|
||||
* Example:
|
||||
* <pre>
|
||||
*
|
||||
* var result1 = xhr.get('testdata/xhr_test_text.data');
|
||||
*
|
||||
* // Get a second independent Result.
|
||||
* var result2 = xhr.getJson('testdata/xhr_test_json.data');
|
||||
*
|
||||
* // Create a Result that resolves when both prior results resolve.
|
||||
* var combinedResult = goog.labs.result.combine(result1, result2);
|
||||
*
|
||||
* // Process data after resolution of both results.
|
||||
* goog.labs.result.waitOnSuccess(combinedResult, function(results) {
|
||||
* goog.array.forEach(results, function(result) {
|
||||
* alert(result.getState());
|
||||
* });
|
||||
* });
|
||||
* </pre>
|
||||
*
|
||||
* @param {...!goog.labs.result.Result} var_args The results to wait on.
|
||||
*
|
||||
* @return {!goog.labs.result.Result} A new Result whose eventual value will be
|
||||
* the resolved given Result objects.
|
||||
*/
|
||||
goog.labs.result.combine = function(var_args) {
|
||||
var results = goog.array.clone(arguments);
|
||||
var combinedResult = new goog.labs.result.SimpleResult();
|
||||
|
||||
var isResolved = function(res) {
|
||||
return res.getState() != goog.labs.result.Result.State.PENDING;
|
||||
};
|
||||
|
||||
var checkResults = function() {
|
||||
if (goog.array.every(results, isResolved)) {
|
||||
combinedResult.setValue(results);
|
||||
}
|
||||
};
|
||||
|
||||
goog.array.forEach(results, function(result) {
|
||||
goog.labs.result.wait(result, checkResults);
|
||||
});
|
||||
|
||||
return combinedResult;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns a result that waits on all given results to resolve. Once all have
|
||||
* resolved, the returned result will succeed if and only if all given results
|
||||
* succeeded. Otherwise it will error.
|
||||
*
|
||||
* Example:
|
||||
* <pre>
|
||||
*
|
||||
* var result1 = xhr.get('testdata/xhr_test_text.data');
|
||||
*
|
||||
* // Get a second independent Result.
|
||||
* var result2 = xhr.getJson('testdata/xhr_test_json.data');
|
||||
*
|
||||
* // Create a Result that resolves when both prior results resolve.
|
||||
* var combinedResult = goog.labs.result.combineOnSuccess(result1, result2);
|
||||
*
|
||||
* // Process data after successful resolution of both results.
|
||||
* goog.labs.result.waitOnSuccess(combinedResult, function(results) {
|
||||
* var textData = results[0].getValue();
|
||||
* var jsonData = results[1].getValue();
|
||||
* assertEquals('Just some data.', textData);
|
||||
* assertEquals('ok', jsonData['stat']);
|
||||
* });
|
||||
*
|
||||
* // Handle errors when either or both results failed.
|
||||
* goog.labs.result.waitOnError(combinedResult, function(combined) {
|
||||
* var results = combined.getError();
|
||||
*
|
||||
* if (results[0].getState() == goog.labs.result.Result.State.ERROR) {
|
||||
* alert('result1 failed');
|
||||
* }
|
||||
*
|
||||
* if (results[1].getState() == goog.labs.result.Result.State.ERROR) {
|
||||
* alert('result2 failed');
|
||||
* }
|
||||
* });
|
||||
* </pre>
|
||||
*
|
||||
* @param {...!goog.labs.result.Result} var_args The results to wait on.
|
||||
*
|
||||
* @return {!goog.labs.result.Result} A new Result whose eventual value will be
|
||||
* an array of values of the given Result objects.
|
||||
*/
|
||||
goog.labs.result.combineOnSuccess = function(var_args) {
|
||||
var combinedResult = new goog.labs.result.SimpleResult();
|
||||
|
||||
var resolvedSuccessfully = function(res) {
|
||||
return res.getState() == goog.labs.result.Result.State.SUCCESS;
|
||||
};
|
||||
|
||||
goog.labs.result.wait(
|
||||
goog.labs.result.combine.apply(goog.labs.result.combine, arguments),
|
||||
// The combined result never ERRORs
|
||||
function(res) {
|
||||
var results = /** @type {Array} */ (res.getValue());
|
||||
if (goog.array.every(results, resolvedSuccessfully)) {
|
||||
combinedResult.setValue(results);
|
||||
} else {
|
||||
combinedResult.setError(results);
|
||||
}
|
||||
});
|
||||
|
||||
return combinedResult;
|
||||
};
|
||||
@@ -0,0 +1,197 @@
|
||||
// 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 SimpleResult object that implements goog.labs.result.Result.
|
||||
* See below for a more detailed description.
|
||||
*/
|
||||
|
||||
goog.provide('goog.labs.result.SimpleResult');
|
||||
goog.provide('goog.labs.result.SimpleResult.StateError');
|
||||
|
||||
goog.require('goog.debug.Error');
|
||||
goog.require('goog.labs.result.Result');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A SimpleResult object is a basic implementation of the
|
||||
* goog.labs.result.Result interface. This could be subclassed(e.g. XHRResult)
|
||||
* or instantiated and returned by another class as a form of result. The caller
|
||||
* receiving the result could then attach handlers to be called when the result
|
||||
* is resolved(success or error).
|
||||
*
|
||||
* @constructor
|
||||
* @implements {goog.labs.result.Result}
|
||||
*/
|
||||
goog.labs.result.SimpleResult = function() {
|
||||
/**
|
||||
* The current state of this Result.
|
||||
* @type {goog.labs.result.Result.State}
|
||||
* @private
|
||||
*/
|
||||
this.state_ = goog.labs.result.Result.State.PENDING;
|
||||
|
||||
/**
|
||||
* The list of handlers to call when this Result is resolved.
|
||||
* @type {!Array.<!function(goog.labs.result.SimpleResult)>}
|
||||
* @private
|
||||
*/
|
||||
this.handlers_ = [];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The 'value' of this Result.
|
||||
* @type {*}
|
||||
* @private
|
||||
*/
|
||||
goog.labs.result.SimpleResult.prototype.value_;
|
||||
|
||||
|
||||
/**
|
||||
* The error slug for this Result.
|
||||
* @type {*}
|
||||
* @private
|
||||
*/
|
||||
goog.labs.result.SimpleResult.prototype.error_;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Error thrown if there is an attempt to set the value or error for this result
|
||||
* more than once.
|
||||
*
|
||||
* @constructor
|
||||
* @extends {goog.debug.Error}
|
||||
*/
|
||||
goog.labs.result.SimpleResult.StateError = function() {
|
||||
goog.base(this, 'Multiple attempts to set the state of this Result');
|
||||
};
|
||||
goog.inherits(goog.labs.result.SimpleResult.StateError, goog.debug.Error);
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.labs.result.SimpleResult.prototype.getState = function() {
|
||||
return this.state_;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.labs.result.SimpleResult.prototype.getValue = function() {
|
||||
return this.value_;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.labs.result.SimpleResult.prototype.getError = function() {
|
||||
return this.error_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Attaches handlers to be called when the value of this Result is available.
|
||||
*
|
||||
* @param {!function(!goog.labs.result.SimpleResult)} handler The function
|
||||
* called when the value is available. The function is passed the Result
|
||||
* object as the only argument.
|
||||
* @override
|
||||
*/
|
||||
goog.labs.result.SimpleResult.prototype.wait = function(handler) {
|
||||
if (this.isPending_()) {
|
||||
this.handlers_.push(handler);
|
||||
} else {
|
||||
handler(this);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the value of this Result, changing the state.
|
||||
*
|
||||
* @param {*} value The value to set for this Result.
|
||||
*/
|
||||
goog.labs.result.SimpleResult.prototype.setValue = function(value) {
|
||||
if (this.isPending_()) {
|
||||
this.value_ = value;
|
||||
this.state_ = goog.labs.result.Result.State.SUCCESS;
|
||||
this.callHandlers_();
|
||||
} else if (!this.isCanceled()) {
|
||||
// setValue is a no-op if this Result has been canceled.
|
||||
throw new goog.labs.result.SimpleResult.StateError();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the Result to be an error Result.
|
||||
*
|
||||
* @param {*=} opt_error Optional error slug to set for this Result.
|
||||
*/
|
||||
goog.labs.result.SimpleResult.prototype.setError = function(opt_error) {
|
||||
if (this.isPending_()) {
|
||||
this.error_ = opt_error;
|
||||
this.state_ = goog.labs.result.Result.State.ERROR;
|
||||
this.callHandlers_();
|
||||
} else if (!this.isCanceled()) {
|
||||
// setError is a no-op if this Result has been canceled.
|
||||
throw new goog.labs.result.SimpleResult.StateError();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Calls the handlers registered for this Result.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
goog.labs.result.SimpleResult.prototype.callHandlers_ = function() {
|
||||
while (this.handlers_.length) {
|
||||
var callback = this.handlers_.shift();
|
||||
callback(this);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {boolean} Whether the Result is pending.
|
||||
* @private
|
||||
*/
|
||||
goog.labs.result.SimpleResult.prototype.isPending_ = function() {
|
||||
return this.state_ == goog.labs.result.Result.State.PENDING;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Cancels the Result.
|
||||
*
|
||||
* @return {boolean} Whether the result was canceled. It will not be canceled if
|
||||
* the result was already canceled or has already resolved.
|
||||
* @override
|
||||
*/
|
||||
goog.labs.result.SimpleResult.prototype.cancel = function() {
|
||||
// cancel is a no-op if the result has been resolved.
|
||||
if (this.isPending_()) {
|
||||
this.setError(new goog.labs.result.Result.CancelError());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.labs.result.SimpleResult.prototype.isCanceled = function() {
|
||||
return this.state_ == goog.labs.result.Result.State.ERROR &&
|
||||
this.error_ instanceof goog.labs.result.Result.CancelError;
|
||||
};
|
||||
@@ -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
|
||||
};
|
||||
@@ -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
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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) {};
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user