UNPKG

cesium

Version:

CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.

1,605 lines (1,404 loc) 10.2 MB
/** * Cesium - https://github.com/AnalyticalGraphicsInc/cesium * * Copyright 2011-2017 Cesium Contributors * * 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. * * Columbus View (Pat. Pend.) * * Portions licensed separately. * See https://github.com/AnalyticalGraphicsInc/cesium/blob/master/LICENSE.md for full licensing details. */ (function () { /** * @license almond 0.3.3 Copyright jQuery Foundation and other contributors. * Released under MIT license, http://github.com/requirejs/almond/LICENSE */ //Going sloppy to avoid 'use strict' string cost, but strict practices should //be followed. /*global setTimeout: false */ var requirejs, require, define; (function (undef) { var main, req, makeMap, handlers, defined = {}, waiting = {}, config = {}, defining = {}, hasOwn = Object.prototype.hasOwnProperty, aps = [].slice, jsSuffixRegExp = /\.js$/; function hasProp(obj, prop) { return hasOwn.call(obj, prop); } /** * Given a relative module name, like ./something, normalize it to * a real name that can be mapped to a path. * @param {String} name the relative name * @param {String} baseName a real name that the name arg is relative * to. * @returns {String} normalized name */ function normalize(name, baseName) { var nameParts, nameSegment, mapValue, foundMap, lastIndex, foundI, foundStarMap, starI, i, j, part, normalizedBaseParts, baseParts = baseName && baseName.split("/"), map = config.map, starMap = (map && map['*']) || {}; //Adjust any relative paths. if (name) { name = name.split('/'); lastIndex = name.length - 1; // If wanting node ID compatibility, strip .js from end // of IDs. Have to do this here, and not in nameToUrl // because node allows either .js or non .js to map // to same file. if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) { name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, ''); } // Starts with a '.' so need the baseName if (name[0].charAt(0) === '.' && baseParts) { //Convert baseName to array, and lop off the last part, //so that . matches that 'directory' and not name of the baseName's //module. For instance, baseName of 'one/two/three', maps to //'one/two/three.js', but we want the directory, 'one/two' for //this normalization. normalizedBaseParts = baseParts.slice(0, baseParts.length - 1); name = normalizedBaseParts.concat(name); } //start trimDots for (i = 0; i < name.length; i++) { part = name[i]; if (part === '.') { name.splice(i, 1); i -= 1; } else if (part === '..') { // If at the start, or previous value is still .., // keep them so that when converted to a path it may // still work when converted to a path, even though // as an ID it is less than ideal. In larger point // releases, may be better to just kick out an error. if (i === 0 || (i === 1 && name[2] === '..') || name[i - 1] === '..') { continue; } else if (i > 0) { name.splice(i - 1, 2); i -= 2; } } } //end trimDots name = name.join('/'); } //Apply map config if available. if ((baseParts || starMap) && map) { nameParts = name.split('/'); for (i = nameParts.length; i > 0; i -= 1) { nameSegment = nameParts.slice(0, i).join("/"); if (baseParts) { //Find the longest baseName segment match in the config. //So, do joins on the biggest to smallest lengths of baseParts. for (j = baseParts.length; j > 0; j -= 1) { mapValue = map[baseParts.slice(0, j).join('/')]; //baseName segment has config, find if it has one for //this name. if (mapValue) { mapValue = mapValue[nameSegment]; if (mapValue) { //Match, update name to the new value. foundMap = mapValue; foundI = i; break; } } } } if (foundMap) { break; } //Check for a star map match, but just hold on to it, //if there is a shorter segment match later in a matching //config, then favor over this star map. if (!foundStarMap && starMap && starMap[nameSegment]) { foundStarMap = starMap[nameSegment]; starI = i; } } if (!foundMap && foundStarMap) { foundMap = foundStarMap; foundI = starI; } if (foundMap) { nameParts.splice(0, foundI, foundMap); name = nameParts.join('/'); } } return name; } function makeRequire(relName, forceSync) { return function () { //A version of a require function that passes a moduleName //value for items that may need to //look up paths relative to the moduleName var args = aps.call(arguments, 0); //If first arg is not require('string'), and there is only //one arg, it is the array form without a callback. Insert //a null so that the following concat is correct. if (typeof args[0] !== 'string' && args.length === 1) { args.push(null); } return req.apply(undef, args.concat([relName, forceSync])); }; } function makeNormalize(relName) { return function (name) { return normalize(name, relName); }; } function makeLoad(depName) { return function (value) { defined[depName] = value; }; } function callDep(name) { if (hasProp(waiting, name)) { var args = waiting[name]; delete waiting[name]; defining[name] = true; main.apply(undef, args); } if (!hasProp(defined, name) && !hasProp(defining, name)) { throw new Error('No ' + name); } return defined[name]; } //Turns a plugin!resource to [plugin, resource] //with the plugin being undefined if the name //did not have a plugin prefix. function splitPrefix(name) { var prefix, index = name ? name.indexOf('!') : -1; if (index > -1) { prefix = name.substring(0, index); name = name.substring(index + 1, name.length); } return [prefix, name]; } //Creates a parts array for a relName where first part is plugin ID, //second part is resource ID. Assumes relName has already been normalized. function makeRelParts(relName) { return relName ? splitPrefix(relName) : []; } /** * Makes a name map, normalizing the name, and using a plugin * for normalization if necessary. Grabs a ref to plugin * too, as an optimization. */ makeMap = function (name, relParts) { var plugin, parts = splitPrefix(name), prefix = parts[0], relResourceName = relParts[1]; name = parts[1]; if (prefix) { prefix = normalize(prefix, relResourceName); plugin = callDep(prefix); } //Normalize according if (prefix) { if (plugin && plugin.normalize) { name = plugin.normalize(name, makeNormalize(relResourceName)); } else { name = normalize(name, relResourceName); } } else { name = normalize(name, relResourceName); parts = splitPrefix(name); prefix = parts[0]; name = parts[1]; if (prefix) { plugin = callDep(prefix); } } //Using ridiculous property names for space reasons return { f: prefix ? prefix + '!' + name : name, //fullName n: name, pr: prefix, p: plugin }; }; function makeConfig(name) { return function () { return (config && config.config && config.config[name]) || {}; }; } handlers = { require: function (name) { return makeRequire(name); }, exports: function (name) { var e = defined[name]; if (typeof e !== 'undefined') { return e; } else { return (defined[name] = {}); } }, module: function (name) { return { id: name, uri: '', exports: defined[name], config: makeConfig(name) }; } }; main = function (name, deps, callback, relName) { var cjsModule, depName, ret, map, i, relParts, args = [], callbackType = typeof callback, usingExports; //Use name if no relName relName = relName || name; relParts = makeRelParts(relName); //Call the callback to define the module, if necessary. if (callbackType === 'undefined' || callbackType === 'function') { //Pull out the defined dependencies and pass the ordered //values to the callback. //Default to [require, exports, module] if no deps deps = !deps.length && callback.length ? ['require', 'exports', 'module'] : deps; for (i = 0; i < deps.length; i += 1) { map = makeMap(deps[i], relParts); depName = map.f; //Fast path CommonJS standard dependencies. if (depName === "require") { args[i] = handlers.require(name); } else if (depName === "exports") { //CommonJS module spec 1.1 args[i] = handlers.exports(name); usingExports = true; } else if (depName === "module") { //CommonJS module spec 1.1 cjsModule = args[i] = handlers.module(name); } else if (hasProp(defined, depName) || hasProp(waiting, depName) || hasProp(defining, depName)) { args[i] = callDep(depName); } else if (map.p) { map.p.load(map.n, makeRequire(relName, true), makeLoad(depName), {}); args[i] = defined[depName]; } else { throw new Error(name + ' missing ' + depName); } } ret = callback ? callback.apply(defined[name], args) : undefined; if (name) { //If setting exports via "module" is in play, //favor that over return value and exports. After that, //favor a non-undefined return value over exports use. if (cjsModule && cjsModule.exports !== undef && cjsModule.exports !== defined[name]) { defined[name] = cjsModule.exports; } else if (ret !== undef || !usingExports) { //Use the return value from the function. defined[name] = ret; } } } else if (name) { //May just be an object definition for the module. Only //worry about defining if have a module name. defined[name] = callback; } }; requirejs = require = req = function (deps, callback, relName, forceSync, alt) { if (typeof deps === "string") { if (handlers[deps]) { //callback in this case is really relName return handlers[deps](callback); } //Just return the module wanted. In this scenario, the //deps arg is the module name, and second arg (if passed) //is just the relName. //Normalize module name, if it contains . or .. return callDep(makeMap(deps, makeRelParts(callback)).f); } else if (!deps.splice) { //deps is a config object, not an array. config = deps; if (config.deps) { req(config.deps, config.callback); } if (!callback) { return; } if (callback.splice) { //callback is an array, which means it is a dependency list. //Adjust args if there are dependencies deps = callback; callback = relName; relName = null; } else { deps = undef; } } //Support require(['a']) callback = callback || function () {}; //If relName is a function, it is an errback handler, //so remove it. if (typeof relName === 'function') { relName = forceSync; forceSync = alt; } //Simulate async callback; if (forceSync) { main(undef, deps, callback, relName); } else { //Using a non-zero value because of concern for what old browsers //do, and latest browsers "upgrade" to 4 if lower value is used: //http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#dom-windowtimers-settimeout: //If want a value immediately, use require('id') instead -- something //that works in almond on the global level, but not guaranteed and //unlikely to work in other AMD implementations. setTimeout(function () { main(undef, deps, callback, relName); }, 4); } return req; }; /** * Just drops the config on the floor, but returns req in case * the config return value is used. */ req.config = function (cfg) { return req(cfg); }; /** * Expose module registry for debugging and tooling */ requirejs._defined = defined; define = function (name, deps, callback) { if (typeof name !== 'string') { throw new Error('See almond README: incorrect module build, no module name'); } //This module may not have dependencies if (!deps.splice) { //deps is not an array, so probably means //an object literal or factory function for //the value. Adjust args. callback = deps; deps = []; } if (!hasProp(defined, name) && !hasProp(waiting, name)) { waiting[name] = [name, deps, callback]; } }; define.amd = { jQuery: true }; }()); define('Core/appendForwardSlash',[],function() { 'use strict'; /** * @private */ function appendForwardSlash(url) { if (url.length === 0 || url[url.length - 1] !== '/') { url = url + '/'; } return url; } return appendForwardSlash; }); define('Core/defined',[],function() { 'use strict'; /** * @exports defined * * @param {*} value The object. * @returns {Boolean} Returns true if the object is defined, returns false otherwise. * * @example * if (Cesium.defined(positions)) { * doSomething(); * } else { * doSomethingElse(); * } */ function defined(value) { return value !== undefined && value !== null; } return defined; }); define('Core/DeveloperError',[ './defined' ], function( defined) { 'use strict'; /** * Constructs an exception object that is thrown due to a developer error, e.g., invalid argument, * argument out of range, etc. This exception should only be thrown during development; * it usually indicates a bug in the calling code. This exception should never be * caught; instead the calling code should strive not to generate it. * <br /><br /> * On the other hand, a {@link RuntimeError} indicates an exception that may * be thrown at runtime, e.g., out of memory, that the calling code should be prepared * to catch. * * @alias DeveloperError * @constructor * @extends Error * * @param {String} [message] The error message for this exception. * * @see RuntimeError */ function DeveloperError(message) { /** * 'DeveloperError' indicating that this exception was thrown due to a developer error. * @type {String} * @readonly */ this.name = 'DeveloperError'; /** * The explanation for why this exception was thrown. * @type {String} * @readonly */ this.message = message; //Browsers such as IE don't have a stack property until you actually throw the error. var stack; try { throw new Error(); } catch (e) { stack = e.stack; } /** * The stack trace of this exception, if available. * @type {String} * @readonly */ this.stack = stack; } if (defined(Object.create)) { DeveloperError.prototype = Object.create(Error.prototype); DeveloperError.prototype.constructor = DeveloperError; } DeveloperError.prototype.toString = function() { var str = this.name + ': ' + this.message; if (defined(this.stack)) { str += '\n' + this.stack.toString(); } return str; }; /** * @private */ DeveloperError.throwInstantiationError = function() { throw new DeveloperError('This function defines an interface and should not be called directly.'); }; return DeveloperError; }); /** * @license * * Grauw URI utilities * * See: http://hg.grauw.nl/grauw-lib/file/tip/src/uri.js * * @author Laurens Holst (http://www.grauw.nl/) * * Copyright 2012 Laurens Holst * * 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. * */ define('ThirdParty/Uri',[],function() { /** * Constructs a URI object. * @constructor * @class Implementation of URI parsing and base URI resolving algorithm in RFC 3986. * @param {string|URI} uri A string or URI object to create the object from. */ function URI(uri) { if (uri instanceof URI) { // copy constructor this.scheme = uri.scheme; this.authority = uri.authority; this.path = uri.path; this.query = uri.query; this.fragment = uri.fragment; } else if (uri) { // uri is URI string or cast to string var c = parseRegex.exec(uri); this.scheme = c[1]; this.authority = c[2]; this.path = c[3]; this.query = c[4]; this.fragment = c[5]; } } // Initial values on the prototype URI.prototype.scheme = null; URI.prototype.authority = null; URI.prototype.path = ''; URI.prototype.query = null; URI.prototype.fragment = null; // Regular expression from RFC 3986 appendix B var parseRegex = new RegExp('^(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*)(?:\\?([^#]*))?(?:#(.*))?$'); /** * Returns the scheme part of the URI. * In "http://example.com:80/a/b?x#y" this is "http". */ URI.prototype.getScheme = function() { return this.scheme; }; /** * Returns the authority part of the URI. * In "http://example.com:80/a/b?x#y" this is "example.com:80". */ URI.prototype.getAuthority = function() { return this.authority; }; /** * Returns the path part of the URI. * In "http://example.com:80/a/b?x#y" this is "/a/b". * In "mailto:mike@example.com" this is "mike@example.com". */ URI.prototype.getPath = function() { return this.path; }; /** * Returns the query part of the URI. * In "http://example.com:80/a/b?x#y" this is "x". */ URI.prototype.getQuery = function() { return this.query; }; /** * Returns the fragment part of the URI. * In "http://example.com:80/a/b?x#y" this is "y". */ URI.prototype.getFragment = function() { return this.fragment; }; /** * Tests whether the URI is an absolute URI. * See RFC 3986 section 4.3. */ URI.prototype.isAbsolute = function() { return !!this.scheme && !this.fragment; }; ///** //* Extensive validation of the URI against the ABNF in RFC 3986 //*/ //URI.prototype.validate /** * Tests whether the URI is a same-document reference. * See RFC 3986 section 4.4. * * To perform more thorough comparison, you can normalise the URI objects. */ URI.prototype.isSameDocumentAs = function(uri) { return uri.scheme == this.scheme && uri.authority == this.authority && uri.path == this.path && uri.query == this.query; }; /** * Simple String Comparison of two URIs. * See RFC 3986 section 6.2.1. * * To perform more thorough comparison, you can normalise the URI objects. */ URI.prototype.equals = function(uri) { return this.isSameDocumentAs(uri) && uri.fragment == this.fragment; }; /** * Normalizes the URI using syntax-based normalization. * This includes case normalization, percent-encoding normalization and path segment normalization. * XXX: Percent-encoding normalization does not escape characters that need to be escaped. * (Although that would not be a valid URI in the first place. See validate().) * See RFC 3986 section 6.2.2. */ URI.prototype.normalize = function() { this.removeDotSegments(); if (this.scheme) this.scheme = this.scheme.toLowerCase(); if (this.authority) this.authority = this.authority.replace(authorityRegex, replaceAuthority). replace(caseRegex, replaceCase); if (this.path) this.path = this.path.replace(caseRegex, replaceCase); if (this.query) this.query = this.query.replace(caseRegex, replaceCase); if (this.fragment) this.fragment = this.fragment.replace(caseRegex, replaceCase); }; var caseRegex = /%[0-9a-z]{2}/gi; var percentRegex = /[a-zA-Z0-9\-\._~]/; var authorityRegex = /(.*@)?([^@:]*)(:.*)?/; function replaceCase(str) { var dec = unescape(str); return percentRegex.test(dec) ? dec : str.toUpperCase(); } function replaceAuthority(str, p1, p2, p3) { return (p1 || '') + p2.toLowerCase() + (p3 || ''); } /** * Resolve a relative URI (this) against a base URI. * The base URI must be an absolute URI. * See RFC 3986 section 5.2 */ URI.prototype.resolve = function(baseURI) { var uri = new URI(); if (this.scheme) { uri.scheme = this.scheme; uri.authority = this.authority; uri.path = this.path; uri.query = this.query; } else { uri.scheme = baseURI.scheme; if (this.authority) { uri.authority = this.authority; uri.path = this.path; uri.query = this.query; } else { uri.authority = baseURI.authority; if (this.path == '') { uri.path = baseURI.path; uri.query = this.query || baseURI.query; } else { if (this.path.charAt(0) == '/') { uri.path = this.path; uri.removeDotSegments(); } else { if (baseURI.authority && baseURI.path == '') { uri.path = '/' + this.path; } else { uri.path = baseURI.path.substring(0, baseURI.path.lastIndexOf('/') + 1) + this.path; } uri.removeDotSegments(); } uri.query = this.query; } } } uri.fragment = this.fragment; return uri; }; /** * Remove dot segments from path. * See RFC 3986 section 5.2.4 * @private */ URI.prototype.removeDotSegments = function() { var input = this.path.split('/'), output = [], segment, absPath = input[0] == ''; if (absPath) input.shift(); var sFirst = input[0] == '' ? input.shift() : null; while (input.length) { segment = input.shift(); if (segment == '..') { output.pop(); } else if (segment != '.') { output.push(segment); } } if (segment == '.' || segment == '..') output.push(''); if (absPath) output.unshift(''); this.path = output.join('/'); }; // We don't like this function because it builds up a cache that is never cleared. // /** // * Resolves a relative URI against an absolute base URI. // * Convenience method. // * @param {String} uri the relative URI to resolve // * @param {String} baseURI the base URI (must be absolute) to resolve against // */ // URI.resolve = function(sURI, sBaseURI) { // var uri = cache[sURI] || (cache[sURI] = new URI(sURI)); // var baseURI = cache[sBaseURI] || (cache[sBaseURI] = new URI(sBaseURI)); // return uri.resolve(baseURI).toString(); // }; // var cache = {}; /** * Serialises the URI to a string. */ URI.prototype.toString = function() { var result = ''; if (this.scheme) result += this.scheme + ':'; if (this.authority) result += '//' + this.authority; result += this.path; if (this.query) result += '?' + this.query; if (this.fragment) result += '#' + this.fragment; return result; }; return URI; }); /** @license when.js - https://github.com/cujojs/when MIT License (c) copyright B Cavalier & J Hann * A lightweight CommonJS Promises/A and when() implementation * when is part of the cujo.js family of libraries (http://cujojs.com/) * * Licensed under the MIT License at: * http://www.opensource.org/licenses/mit-license.php * * @version 1.7.1 */ (function(define) { 'use strict'; define('ThirdParty/when',[],function () { var reduceArray, slice, undef; // // Public API // when.defer = defer; // Create a deferred when.resolve = resolve; // Create a resolved promise when.reject = reject; // Create a rejected promise when.join = join; // Join 2 or more promises when.all = all; // Resolve a list of promises when.map = map; // Array.map() for promises when.reduce = reduce; // Array.reduce() for promises when.any = any; // One-winner race when.some = some; // Multi-winner race when.chain = chain; // Make a promise trigger another resolver when.isPromise = isPromise; // Determine if a thing is a promise /** * Register an observer for a promise or immediate value. * * @param {*} promiseOrValue * @param {function?} [onFulfilled] callback to be called when promiseOrValue is * successfully fulfilled. If promiseOrValue is an immediate value, callback * will be invoked immediately. * @param {function?} [onRejected] callback to be called when promiseOrValue is * rejected. * @param {function?} [onProgress] callback to be called when progress updates * are issued for promiseOrValue. * @returns {Promise} a new {@link Promise} that will complete with the return * value of callback or errback or the completion value of promiseOrValue if * callback and/or errback is not supplied. */ function when(promiseOrValue, onFulfilled, onRejected, onProgress) { // Get a trusted promise for the input promiseOrValue, and then // register promise handlers return resolve(promiseOrValue).then(onFulfilled, onRejected, onProgress); } /** * Returns promiseOrValue if promiseOrValue is a {@link Promise}, a new Promise if * promiseOrValue is a foreign promise, or a new, already-fulfilled {@link Promise} * whose value is promiseOrValue if promiseOrValue is an immediate value. * * @param {*} promiseOrValue * @returns Guaranteed to return a trusted Promise. If promiseOrValue is a when.js {@link Promise} * returns promiseOrValue, otherwise, returns a new, already-resolved, when.js {@link Promise} * whose resolution value is: * * the resolution value of promiseOrValue if it's a foreign promise, or * * promiseOrValue if it's a value */ function resolve(promiseOrValue) { var promise, deferred; if(promiseOrValue instanceof Promise) { // It's a when.js promise, so we trust it promise = promiseOrValue; } else { // It's not a when.js promise. See if it's a foreign promise or a value. if(isPromise(promiseOrValue)) { // It's a thenable, but we don't know where it came from, so don't trust // its implementation entirely. Introduce a trusted middleman when.js promise deferred = defer(); // IMPORTANT: This is the only place when.js should ever call .then() on an // untrusted promise. Don't expose the return value to the untrusted promise promiseOrValue.then( function(value) { deferred.resolve(value); }, function(reason) { deferred.reject(reason); }, function(update) { deferred.progress(update); } ); promise = deferred.promise; } else { // It's a value, not a promise. Create a resolved promise for it. promise = fulfilled(promiseOrValue); } } return promise; } /** * Returns a rejected promise for the supplied promiseOrValue. The returned * promise will be rejected with: * - promiseOrValue, if it is a value, or * - if promiseOrValue is a promise * - promiseOrValue's value after it is fulfilled * - promiseOrValue's reason after it is rejected * @param {*} promiseOrValue the rejected value of the returned {@link Promise} * @returns {Promise} rejected {@link Promise} */ function reject(promiseOrValue) { return when(promiseOrValue, rejected); } /** * Trusted Promise constructor. A Promise created from this constructor is * a trusted when.js promise. Any other duck-typed promise is considered * untrusted. * @constructor * @name Promise */ function Promise(then) { this.then = then; } Promise.prototype = { /** * Register a callback that will be called when a promise is * fulfilled or rejected. Optionally also register a progress handler. * Shortcut for .then(onFulfilledOrRejected, onFulfilledOrRejected, onProgress) * @param {function?} [onFulfilledOrRejected] * @param {function?} [onProgress] * @returns {Promise} */ always: function(onFulfilledOrRejected, onProgress) { return this.then(onFulfilledOrRejected, onFulfilledOrRejected, onProgress); }, /** * Register a rejection handler. Shortcut for .then(undefined, onRejected) * @param {function?} onRejected * @returns {Promise} */ otherwise: function(onRejected) { return this.then(undef, onRejected); }, /** * Shortcut for .then(function() { return value; }) * @param {*} value * @returns {Promise} a promise that: * - is fulfilled if value is not a promise, or * - if value is a promise, will fulfill with its value, or reject * with its reason. */ yield: function(value) { return this.then(function() { return value; }); }, /** * Assumes that this promise will fulfill with an array, and arranges * for the onFulfilled to be called with the array as its argument list * i.e. onFulfilled.spread(undefined, array). * @param {function} onFulfilled function to receive spread arguments * @returns {Promise} */ spread: function(onFulfilled) { return this.then(function(array) { // array may contain promises, so resolve its contents. return all(array, function(array) { return onFulfilled.apply(undef, array); }); }); } }; /** * Create an already-resolved promise for the supplied value * @private * * @param {*} value * @returns {Promise} fulfilled promise */ function fulfilled(value) { var p = new Promise(function(onFulfilled) { // TODO: Promises/A+ check typeof onFulfilled try { return resolve(onFulfilled ? onFulfilled(value) : value); } catch(e) { return rejected(e); } }); return p; } /** * Create an already-rejected {@link Promise} with the supplied * rejection reason. * @private * * @param {*} reason * @returns {Promise} rejected promise */ function rejected(reason) { var p = new Promise(function(_, onRejected) { // TODO: Promises/A+ check typeof onRejected try { return onRejected ? resolve(onRejected(reason)) : rejected(reason); } catch(e) { return rejected(e); } }); return p; } /** * Creates a new, Deferred with fully isolated resolver and promise parts, * either or both of which may be given out safely to consumers. * The Deferred itself has the full API: resolve, reject, progress, and * then. The resolver has resolve, reject, and progress. The promise * only has then. * * @returns {Deferred} */ function defer() { var deferred, promise, handlers, progressHandlers, _then, _progress, _resolve; /** * The promise for the new deferred * @type {Promise} */ promise = new Promise(then); /** * The full Deferred object, with {@link Promise} and {@link Resolver} parts * @class Deferred * @name Deferred */ deferred = { then: then, // DEPRECATED: use deferred.promise.then resolve: promiseResolve, reject: promiseReject, // TODO: Consider renaming progress() to notify() progress: promiseProgress, promise: promise, resolver: { resolve: promiseResolve, reject: promiseReject, progress: promiseProgress } }; handlers = []; progressHandlers = []; /** * Pre-resolution then() that adds the supplied callback, errback, and progback * functions to the registered listeners * @private * * @param {function?} [onFulfilled] resolution handler * @param {function?} [onRejected] rejection handler * @param {function?} [onProgress] progress handler */ _then = function(onFulfilled, onRejected, onProgress) { // TODO: Promises/A+ check typeof onFulfilled, onRejected, onProgress var deferred, progressHandler; deferred = defer(); progressHandler = typeof onProgress === 'function' ? function(update) { try { // Allow progress handler to transform progress event deferred.progress(onProgress(update)); } catch(e) { // Use caught value as progress deferred.progress(e); } } : function(update) { deferred.progress(update); }; handlers.push(function(promise) { promise.then(onFulfilled, onRejected) .then(deferred.resolve, deferred.reject, progressHandler); }); progressHandlers.push(progressHandler); return deferred.promise; }; /** * Issue a progress event, notifying all progress listeners * @private * @param {*} update progress event payload to pass to all listeners */ _progress = function(update) { processQueue(progressHandlers, update); return update; }; /** * Transition from pre-resolution state to post-resolution state, notifying * all listeners of the resolution or rejection * @private * @param {*} value the value of this deferred */ _resolve = function(value) { value = resolve(value); // Replace _then with one that directly notifies with the result. _then = value.then; // Replace _resolve so that this Deferred can only be resolved once _resolve = resolve; // Make _progress a noop, to disallow progress for the resolved promise. _progress = noop; // Notify handlers processQueue(handlers, value); // Free progressHandlers array since we'll never issue progress events progressHandlers = handlers = undef; return value; }; return deferred; /** * Wrapper to allow _then to be replaced safely * @param {function?} [onFulfilled] resolution handler * @param {function?} [onRejected] rejection handler * @param {function?} [onProgress] progress handler * @returns {Promise} new promise */ function then(onFulfilled, onRejected, onProgress) { // TODO: Promises/A+ check typeof onFulfilled, onRejected, onProgress return _then(onFulfilled, onRejected, onProgress); } /** * Wrapper to allow _resolve to be replaced */ function promiseResolve(val) { return _resolve(val); } /** * Wrapper to allow _reject to be replaced */ function promiseReject(err) { return _resolve(rejected(err)); } /** * Wrapper to allow _progress to be replaced */ function promiseProgress(update) { return _progress(update); } } /** * Determines if promiseOrValue is a promise or not. Uses the feature * test from http://wiki.commonjs.org/wiki/Promises/A to determine if * promiseOrValue is a promise. * * @param {*} promiseOrValue anything * @returns {boolean} true if promiseOrValue is a {@link Promise} */ function isPromise(promiseOrValue) { return promiseOrValue && typeof promiseOrValue.then === 'function'; } /** * Initiates a competitive race, returning a promise that will resolve when * howMany of the supplied promisesOrValues have resolved, or will reject when * it becomes impossible for howMany to resolve, for example, when * (promisesOrValues.length - howMany) + 1 input promises reject. * * @param {Array} promisesOrValues array of anything, may contain a mix * of promises and values * @param howMany {number} number of promisesOrValues to resolve * @param {function?} [onFulfilled] resolution handler * @param {function?} [onRejected] rejection handler * @param {function?} [onProgress] progress handler * @returns {Promise} promise that will resolve to an array of howMany values that * resolved first, or will reject with an array of (promisesOrValues.length - howMany) + 1 * rejection reasons. */ function some(promisesOrValues, howMany, onFulfilled, onRejected, onProgress) { checkCallbacks(2, arguments); return when(promisesOrValues, function(promisesOrValues) { var toResolve, toReject, values, reasons, deferred, fulfillOne, rejectOne, progress, len, i; len = promisesOrValues.length >>> 0; toResolve = Math.max(0, Math.min(howMany, len)); values = []; toReject = (len - toResolve) + 1; reasons = []; deferred = defer(); // No items in the input, resolve immediately if (!toResolve) { deferred.resolve(values); } else { progress = deferred.progress; rejectOne = function(reason) { reasons.push(reason); if(!--toReject) { fulfillOne = rejectOne = noop; deferred.reject(reasons); } }; fulfillOne = function(val) { // This orders the values based on promise resolution order // Another strategy would be to use the original position of // the corresponding promise. values.push(val); if (!--toResolve) { fulfillOne = rejectOne = noop; deferred.resolve(values); } }; for(i = 0; i < len; ++i) { if(i in promisesOrValues) { when(promisesOrValues[i], fulfiller, rejecter, progress); } } } return deferred.then(onFulfilled, onRejected, onProgress); function rejecter(reason) { rejectOne(reason); } function fulfiller(val) { fulfillOne(val); } }); } /** * Initiates a competitive race, returning a promise that will resolve when * any one of the supplied promisesOrValues has resolved or will reject when * *all* promisesOrValues have rejected. * * @param {Array|Promise} promisesOrValues array of anything, may contain a mix * of {@link Promise}s and values * @param {function?} [onFulfilled] resolution handler * @param {function?} [onRejected] rejection handler * @param {function?} [onProgress] progress handler * @returns {Promise} promise that will resolve to the value that resolved first, or * will reject with an array of all rejected inputs. */ function any(promisesOrValues, onFulfilled, onRejected, onProgress) { function unwrapSingleResult(val) { return onFulfilled ? onFulfilled(val[0]) : val[0]; } return some(promisesOrValues, 1, unwrapSingleResult, onRejected, onProgress); } /** * Return a promise that will resolve only once all the supplied promisesOrValues * have resolved. The resolution value of the returned promise will be an array * containing the resolution values of each of the promisesOrValues. * @memberOf when * * @param {Array|Promise} promisesOrValues array of anything, may contain a mix * of {@link Promise}s and values * @param {function?} [onFulfilled] resolution handler * @param {function?} [onRejected] rejection handler * @param {function?} [onProgress] progress handler * @returns {Promise} */ function all(promisesOrValues, onFulfilled, onRejected, onProgress) { checkCallbacks(1, arguments); return map(promisesOrValues, identity).then(onFulfilled, onRejected, onProgress); } /** * Joins multiple promises into a single returned promise. * @returns {Promise} a promise that will fulfill when *all* the input promises * have fulfilled, or will reject when *any one* of the input promises rejects. */ function join(/* ...promises */) { return map(arguments, identity); } /** * Traditional map function, similar to `Array.prototype.map()`, but allows * input to contain {@link Promise}s and/or values, and mapFunc may return * either a value or a {@link Promise} * * @param {Array|Promise} promise array of anything, may contain a mix * of {@link Promise}s and values * @param {function} mapFunc mapping function mapFunc(value) which may return * either a {@link Promise} or value * @returns {Promise} a {@link Promise} that will resolve to an array containing * the mapped output values. */ function map(promise, mapFunc) { return when(promise, function(array) { var results, len, toResolve, resolve, i, d; // Since we know the resulting length, we can preallocate the results // array to avoid array expansions. toResolve = len = array.length >>> 0; results = []; d = defer(); if(!toResolve) { d.resolve(results); } else { resolve = function resolveOne(item, i) { when(item, mapFunc).then(function(mapped) { results[i] = mapped; if(!--toResolve) { d.resolve(results); } }, d.reject); }; // Since mapFunc may be async, get all invocations of it into flight for(i = 0; i < len; i++) { if(i in array) { resolve(array[i], i); } else { --toResolve; } } } return d.promise; }); } /** * Traditional reduce function, similar to `Array.prototype.reduce()`, but * input may contain promises and/or values, and reduceFunc * may return either a value or a promise, *and* initialValue may * be a promise for the starting value. * * @param {Array|Promise} promise array or promise for an array of anything, * may contain a mix of promises and values. * @param {function} reduceFunc reduce function reduce(currentValue, nextValue, index, total), * where total is the total number of items being reduced, and will be the same * in each call to reduceFunc. * @returns {Promise} that will resolve to the final reduced value */ function reduce(promise, reduceFunc /*, initialValue */) { var args = slice.call(arguments, 1); return when(promise, function(array) { var total; total = array.length; // Wrap the supplied reduceFunc with one that handles promises and then // delegates to the supplied. args[0] = function (current, val, i) { return when(current, function (c) { return when(val, function (value) { return reduceFunc(c, value, i, total); }); }); }; return reduceArray.apply(array, args); }); } /** * Ensure that resolution of promiseOrValue will trigger resolver with the * value or reason of promiseOrValue, or instead with resolveValue if it is provided. * * @param promiseOrValue * @param {Object} resolver * @param {function} resolver.resolve * @param {function} resolver.reject * @param {*} [resolveValue] * @returns {Promise} */ function chain(promiseOrValue, resolver, resolveValue) { var useResolveValue = arguments.length > 2; return when(promiseOrValue, function(val) { val = useResolveValue ? resolveValue : val; resolver.resolve(val); return val; }, function(reason) { resolver.reject(reason); return rejected(reason); }, resolver.progress ); } // // Utility functions // /** * Apply all functions in queue to value * @param {Array} queue array of functions to execute * @param {*} value argument passed to each function */ function processQueue(queue, value) { var handler, i = 0; while (handler = queue[i++]) { handler(value); } } /** * Helper that checks arrayOfCallbacks to ensure that each element is either * a function, or null or undefined. * @private * @param {number} start index at which to start checking items in arrayOfCallbacks * @param {Array} arrayOfCallbacks array to check * @throws {Error} if any element of arrayOfCallbacks is something other than * a functions, null, or undefined. */ function checkCallbacks(start, arrayOfCallbacks) { // TODO: Promises/A+ update type checking and docs var arg, i = arrayOfCallbacks.length; while(i > start) { arg = arrayOfCallbacks[--i]; if (arg != null && typeof arg != 'function') { throw new Error('arg '+i+' must be a function'); } } } /** * No-Op function used in method replacement * @private */ function noop() {} slice = [].slice; // ES5 reduce implementation if native not available // See: http://es5.github.com/#x15.4.4.21 as there are many // specifics and edge cases. reduceArray = [].reduce || function(reduceFunc /*, initialValue */) { /*jshint maxcomplexity: 7*/ // ES5 dictates that reduce.length === 1 // This implementation deviates from ES5 spec in the following ways: // 1. It does not check if reduceFunc is a Callable var arr, args, reduced, len, i; i = 0; // This generates a jshint warning, despite being valid // "Missing 'new' prefix when invoking a constructor." // See https://github.com/jshint/jshint/issues/392 arr = Object(this); len = arr.length >>> 0; args = arguments; // If no initialValue, use first item of array (we know length !== 0 here) // and adjust i to start at second item if(args.length <= 1) { // Skip to the first real element in the array for(;;) { if(i in arr) { reduced = arr[i++]; break; } // If we reached the end of the array without finding any real // elements, it's a TypeError if(++i >= len) { throw new TypeError(); } } } else { // If initialValue provided, use it reduced = args[1]; } // Do the actual reduce for(;i < len; ++i) { // Skip holes if(i in arr) { reduced = reduceFunc(reduced, arr[i], i, arr); } } return reduced; }; function identity(x) { return x; } return when; }); })(typeof define == 'function' && define.amd ? define : function (factory) { typeof exports === 'object' ? (module.exports = factory()) : (this.when = factory()); } // Boilerplate for AMD, Node, and browser global ); define('Core/Check',[ './defined', './Deve