UNPKG

cram

Version:

An AMD-compatible build tool.

1,564 lines (1,357 loc) 288 kB
/** @license MIT License (c) copyright B Cavalier & J Hann */ /** * curl (cujo resource loader) * An AMD-compliant javascript module and resource loader * * curl 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 * */ (function (global) { //"use strict"; don't restore this until the config routine is refactored var version = '0.7.3', curlName = 'curl', defineName = 'define', userCfg, prevCurl, prevDefine, doc = global.document, head = doc && (doc['head'] || doc.getElementsByTagName('head')[0]), // to keep IE from crying, we need to put scripts before any // <base> elements, but after any <meta>. this should do it: insertBeforeEl = head && head.getElementsByTagName('base')[0] || null, // constants / flags msgUsingExports = {}, msgFactoryExecuted = {}, // this is the list of scripts that IE is loading. one of these will // be the "interactive" script. too bad IE doesn't send a readystatechange // event to tell us exactly which one. activeScripts = {}, // readyStates for IE6-9 readyStates = 'addEventListener' in global ? {} : { 'loaded': 1, 'complete': 1 }, // these are always handy :) cleanPrototype = {}, toString = cleanPrototype.toString, undef, // local cache of resource definitions (lightweight promises) cache = {}, // local url cache urlCache = {}, // preload are files that must be loaded before any others preload = false, // net to catch anonymous define calls' arguments (non-IE browsers) argsNet, // RegExp's used later, pre-compiled here dontAddExtRx = /\?|\.js\b/, absUrlRx = /^\/|^[^:]+:\/\//, findDotsRx = /(\.)(\.?)(?:$|\/([^\.\/]+.*)?)/g, removeCommentsRx = /\/\*[\s\S]*?\*\/|\/\/.*?[\n\r]/g, findRValueRequiresRx = /require\s*\(\s*(["'])(.*?[^\\])\1\s*\)|[^\\]?(["'])/g, cjsGetters, core; function noop () {} function isType (obj, type) { return toString.call(obj).indexOf('[object ' + type) == 0; } function normalizePkgDescriptor (descriptor) { var main; descriptor.path = removeEndSlash(descriptor['path'] || descriptor['location'] || ''); main = descriptor['main'] || './main'; if (!isRelUrl(main)) main = './' + main; // trailing slashes trick reduceLeadingDots to see them as base ids descriptor.main = reduceLeadingDots(main, descriptor.name + '/'); //if (isRelUrl(descriptor.main)) throw new Error('invalid main (' + main + ') in ' + descriptor.name); descriptor.config = descriptor['config']; return descriptor; } function isRelUrl (it) { return it.charAt(0) == '.'; } function isAbsUrl (it) { return absUrlRx.test(it); } function joinPath (path, file) { return removeEndSlash(path) + '/' + file; } function removeEndSlash (path) { return path && path.charAt(path.length - 1) == '/' ? path.substr(0, path.length - 1) : path; } function reduceLeadingDots (childId, baseId) { // this algorithm is similar to dojo's compactPath, which interprets // module ids of "." and ".." as meaning "grab the module whose name is // the same as my folder or parent folder". These special module ids // are not included in the AMD spec but seem to work in node.js, too. var removeLevels, normId, levels, isRelative, diff; removeLevels = 1; normId = childId; // remove leading dots and count levels if (isRelUrl(normId)) { isRelative = true; normId = normId.replace(findDotsRx, function (m, dot, doubleDot, remainder) { if (doubleDot) removeLevels++; return remainder || ''; }); } if (isRelative) { levels = baseId.split('/'); diff = levels.length - removeLevels; if (diff < 0) { // this is an attempt to navigate above parent module. // maybe dev wants a url or something. punt and return url; return childId; } levels.splice(diff, removeLevels); // normId || [] prevents concat from adding extra "/" when // normId is reduced to a blank string return levels.concat(normId || []).join('/'); } else { return normId; } } function pluginParts (id) { var delPos = id.indexOf('!'); return { resourceId: id.substr(delPos + 1), // resourceId can be zero length pluginId: delPos >= 0 && id.substr(0, delPos) }; } function Begetter () {} function beget (parent, mixin) { Begetter.prototype = parent || cleanPrototype; var child = new Begetter(); Begetter.prototype = cleanPrototype; for (var p in mixin) child[p] = mixin[p]; return child; } function Promise () { var self, thens, complete; self = this; thens = []; function then (resolved, rejected, progressed) { // capture calls to callbacks thens.push([resolved, rejected, progressed]); } function notify (which, arg) { // complete all callbacks var aThen, cb, i = 0; while ((aThen = thens[i++])) { cb = aThen[which]; if (cb) cb(arg); } } complete = function promiseComplete (success, arg) { // switch over to sync then() then = success ? function (resolved, rejected) { resolved && resolved(arg); } : function (resolved, rejected) { rejected && rejected(arg); }; // we no longer throw during multiple calls to resolve or reject // since we don't really provide useful information anyways. complete = noop; // complete all callbacks notify(success ? 0 : 1, arg); // no more notifications notify = noop; // release memory thens = undef; }; this.then = function (resolved, rejected, progressed) { then(resolved, rejected, progressed); return self; }; this.resolve = function (val) { self.resolved = val; complete(true, val); }; this.reject = function (ex) { self.rejected = ex; complete(false, ex); }; this.progress = function (msg) { notify(2, msg); } } function isPromise (o) { return o instanceof Promise || o instanceof CurlApi; } function when (promiseOrValue, callback, errback, progback) { // we can't just sniff for then(). if we do, resources that have a // then() method will make dependencies wait! if (isPromise(promiseOrValue)) { return promiseOrValue.then(callback, errback, progback); } else { return callback(promiseOrValue); } } /** * Returns a function that when executed, executes a lambda function, * but only executes it the number of times stated by howMany. * When done executing, it executes the completed function. Each callback * function receives the same parameters that are supplied to the * returned function each time it executes. In other words, they * are passed through. * @private * @param howMany {Number} must be greater than zero * @param lambda {Function} executed each time * @param completed {Function} only executes once when the counter * reaches zero * @returns {Function} */ function countdown (howMany, lambda, completed) { var result; return function () { if (--howMany >= 0 && lambda) result = lambda.apply(undef, arguments); // we want ==, not <=, since some callers expect call-once functionality if (howMany == 0 && completed) completed(result); return result; } } core = { /** * * reduceLeadingDots of id against parentId * - if there are too many dots (path goes beyond parent), it's a url * - return reduceLeadingDots of id against baseUrl + parentId; * * if id is a url (starts with dots or slash or protocol) * - pathInfo = { config: userCfg, url: url } * * if not a url, id-to-id transform here. * - main module expansion * - plugin prefix expansion * - coordinate main module expansion with plugin expansion * - main module expansion happens first * - future: other transforms? * @param id * @param parentId * @param cfg * @return {*} */ toAbsId: function (id, parentId, cfg) { var absId, pluginId, parts; absId = reduceLeadingDots(id, parentId); // if this is still a relative path, it must be a url // so just punt, otherwise... if (isRelUrl(absId)) return absId; // plugin id split parts = pluginParts(absId); pluginId = parts.pluginId; absId = pluginId || parts.resourceId; // main id expansion if (absId in cfg.pathMap) { absId = cfg.pathMap[absId].main || absId; } // plugin id expansion if (pluginId) { if (pluginId.indexOf('/') < 0 && !(pluginId in cfg.pathMap)) { absId = joinPath(cfg.pluginPath, pluginId); } absId = absId + '!' + parts.resourceId; } return absId; }, createContext: function (cfg, baseId, depNames, isPreload) { var def; def = new Promise(); def.id = baseId || ''; // '' == global def.isPreload = isPreload; def.depNames = depNames; def.config = cfg; // functions that dependencies will use: function toAbsId (childId) { return core.toAbsId(childId, def.id, cfg); } function toUrl (n) { // the AMD spec states that we should not append an extension // in this function since it could already be appended. // we need to use toAbsId in case this is a module id. return core.resolvePathInfo(toAbsId(n), cfg).url; } function localRequire (ids, callback, errback) { var cb, rvid, childDef, earlyExport; // this is public, so send pure function // also fixes issue #41 cb = callback && function () { callback.apply(undef, arguments[0]); }; // RValue require (CommonJS) if (isType(ids, 'String')) { if (cb) { throw new Error('require(id, callback) not allowed'); } // return resource rvid = toAbsId(ids); childDef = cache[rvid]; if (!(rvid in cache)) { // this should only happen when devs attempt their own // manual wrapping of cjs modules or get confused with // the callback syntax: throw new Error('Module not resolved: ' + rvid); } earlyExport = isPromise(childDef) && childDef.exports; return earlyExport || childDef; } else { when(core.getDeps(core.createContext(cfg, def.id, ids, isPreload)), cb, errback); } } def.require = localRequire; localRequire['toUrl'] = toUrl; def.toAbsId = toAbsId; return def; }, createResourceDef: function (cfg, id, isPreload) { var def, origResolve, execute; def = core.createContext(cfg, id, undef, isPreload); origResolve = def.resolve; // using countdown to only execute definition function once execute = countdown(1, function (deps) { def.deps = deps; try { return core.executeDefFunc(def); } catch (ex) { def.reject(ex); } }); // intercept resolve function to execute definition function // before resolving def.resolve = function resolve (deps) { when(isPreload || preload, function () { origResolve((cache[def.id] = urlCache[def.url] = execute(deps))); }); }; // track exports def.exportsReady = function executeFactory (deps) { when(isPreload || preload, function () { // only resolve early if we also use exports (to avoid // circular dependencies). def.exports will have already // been set by the getDeps loop before we get here. if (def.exports) { execute(deps); def.progress(msgFactoryExecuted); } }); }; return def; }, createPluginDef: function (cfg, id, resId, isPreload) { var def; // use resource id for local require and toAbsId def = core.createContext(cfg, resId, undef, isPreload); return def; }, getCjsRequire: function (def) { return def.require; }, getCjsExports: function (def) { return def.exports || (def.exports = {}); }, getCjsModule: function (def) { var module = def.module; if (!module) { module = def.module = { 'id': def.id, 'uri': core.getDefUrl(def), 'exports': core.getCjsExports(def), 'config': function () { return def.config; } }; module.exports = module['exports']; // oh closure compiler! } return module; }, getDefUrl: function (def) { // note: this is used by cjs module.uri return def.url || (def.url = core.checkToAddJsExt(def.require['toUrl'](def.id), def.config)); }, /** * Sets the curl() and define() APIs. * @param [cfg] {Object|Null} set of config params. If missing or null, * this function will set the default API! */ setApi: function (cfg) { /* scenarios: 1. global config sets apiName: "require" - first call to config sets api - second and later calls are ignored - prevCurl cannot exist 2. no global config, first call to config() sets api - first call to config has no api info - second call to config sets api - third and later calls must be ignored 3. global config that doesn't set api, first call does though - same as #2 4. api info is never set - how to know when to stop ignoring? objectives: 1. fail before mistakenly overwriting global[curlName] 2. allow rename/relocate of curl() and define() 3. restore curl() if we overwrote it */ var apiName, defName, apiObj, defObj, failMsg, okToOverwrite; apiName = curlName; defName = defineName; apiObj = defObj = global; failMsg = ' already exists'; // if we're not setting defaults if (cfg) { // is it ok to overwrite existing api functions? okToOverwrite = cfg['overwriteApi'] || cfg.overwriteApi; // allow dev to rename/relocate curl() to another object apiName = cfg['apiName'] || cfg.apiName || apiName; apiObj = cfg['apiContext'] || cfg.apiContext || apiObj; // define() too defName = cfg['defineName'] || cfg.defineName || defName; defObj = cfg['defineContext'] || cfg.defineContext || defObj; // curl() already existed, restore it if this is not a // setDefaults pass. dev must be a good citizen and set // apiName/apiContext (see below). if (prevCurl && isType(prevCurl, 'Function')) { // restore previous curl() global[curlName] = prevCurl; } prevCurl = null; // don't check ever again // ditto for define() if (prevDefine && isType(prevDefine, 'Function')) { // restore previous curl() global[defineName] = prevDefine; } prevDefine = null; // don't check ever again // check if we're mistakenly overwriting either api // if we're configuring, and there's a curl(), and it's not // ours -- and we're not explicitly overwriting -- throw! // Note: if we're setting defaults, we *must* overwrite curl // so that dev can configure it. This is no different than // noConflict()-type methods. if (!okToOverwrite) { if (apiObj[apiName] && apiObj[apiName] != _curl) { throw new Error(apiName + failMsg); } // check if we're overwriting amd api if (defObj[defName] && defObj[defName] != define) { throw new Error(defName + failMsg); } } } // set curl api apiObj[apiName] = _curl; // set AMD public api: define() defObj[defName] = define; }, config: function (cfg) { var prevCfg, newCfg, pluginCfgs, p; // convert from closure-safe names if ('baseUrl' in cfg) cfg.baseUrl = cfg['baseUrl']; if ('main' in cfg) cfg.main = cfg['main']; if ('preloads' in cfg) cfg.preloads = cfg['preloads']; if ('pluginPath' in cfg) cfg.pluginPath = cfg['pluginPath']; if ('dontAddFileExt' in cfg || cfg.dontAddFileExt) { cfg.dontAddFileExt = new RegExp(cfg['dontAddFileExt'] || cfg.dontAddFileExt); } prevCfg = userCfg; newCfg = beget(prevCfg, cfg); // create object to hold path map. // each plugin and package will have its own pathMap, too. newCfg.pathMap = beget(prevCfg.pathMap); pluginCfgs = cfg['plugins'] || {}; newCfg.plugins = beget(prevCfg.plugins); newCfg.paths = beget(prevCfg.paths, cfg.paths); newCfg.packages = beget(prevCfg.packages, cfg.packages); // temporary arrays of paths. this will be converted to // a regexp for fast path parsing. newCfg.pathList = []; // normalizes path/package info and places info on either // the global cfg.pathMap or on a plugin-specific altCfg.pathMap. // also populates a pathList on cfg or plugin configs. function fixAndPushPaths (coll, isPkg) { var id, pluginId, data, parts, currCfg, info; for (var name in coll) { data = coll[name]; if (isType(data, 'String')) data = { path: coll[name] }; // grab the package id, if specified. default to // property name, if missing. data.name = data.name || name; currCfg = newCfg; // check if this is a plugin-specific path parts = pluginParts(removeEndSlash(data.name)); id = parts.resourceId; pluginId = parts.pluginId; if (pluginId) { // plugin-specific path currCfg = pluginCfgs[pluginId]; if (!currCfg) { currCfg = pluginCfgs[pluginId] = beget(newCfg); currCfg.pathMap = beget(newCfg.pathMap); currCfg.pathList = []; } // remove plugin-specific path from coll delete coll[name]; } if (isPkg) { info = normalizePkgDescriptor(data); if (info.config) info.config = beget(newCfg, info.config); } else { info = { path: removeEndSlash(data.path) }; } info.specificity = id.split('/').length; if (id) { currCfg.pathMap[id] = info; currCfg.pathList.push(id); } else { // naked plugin name signifies baseUrl for plugin // resources. baseUrl could be relative to global // baseUrl. currCfg.baseUrl = core.resolveUrl(data.path, newCfg); } } } // adds the path matching regexp onto the cfg or plugin cfgs. function convertPathMatcher (cfg) { var pathMap = cfg.pathMap; cfg.pathRx = new RegExp('^(' + cfg.pathList.sort(function (a, b) { return pathMap[b].specificity - pathMap[a].specificity; } ) .join('|') .replace(/\/|\./g, '\\$&') + ')(?=\\/|$)' ); delete cfg.pathList; } // fix all new packages, then paths (in case there are // plugin-specific paths for a main module, such as wire!) fixAndPushPaths(cfg['packages'], true); fixAndPushPaths(cfg['paths'], false); // process plugins after packages in case we already perform an // id transform on a plugin (i.e. it's a package.main) for (p in pluginCfgs) { var absId = core.toAbsId(p + '!', '', newCfg); newCfg.plugins[absId.substr(0, absId.length - 1)] = pluginCfgs[p]; } pluginCfgs = newCfg.plugins; // create search regex for each path map for (p in pluginCfgs) { // inherit full config pluginCfgs[p] = beget(newCfg, pluginCfgs[p]); var pathList = pluginCfgs[p].pathList; if (pathList) { pluginCfgs[p].pathList = pathList.concat(newCfg.pathList); convertPathMatcher(pluginCfgs[p]); } } // ugh, this is ugly, but necessary until we refactor this function // copy previous pathMap items onto pathList for (p in prevCfg.pathMap) { if (!newCfg.pathMap.hasOwnProperty(p)) newCfg.pathList.push(p); } convertPathMatcher(newCfg); return newCfg; }, resolvePathInfo: function (absId, cfg) { // searches through the configured path mappings and packages var pathMap, pathInfo, path, pkgCfg; pathMap = cfg.pathMap; if (!isAbsUrl(absId)) { path = absId.replace(cfg.pathRx, function (match) { // TODO: remove fallbacks here since they should never need to happen pathInfo = pathMap[match] || {}; pkgCfg = pathInfo.config; return pathInfo.path || ''; }); } else { path = absId; } return { config: pkgCfg || userCfg, url: core.resolveUrl(path, cfg) }; }, resolveUrl: function (path, cfg) { var baseUrl = cfg.baseUrl; return baseUrl && !isAbsUrl(path) ? joinPath(baseUrl, path) : path; }, checkToAddJsExt: function (url, cfg) { // don't add extension if a ? is found in the url (query params) // i'd like to move this feature to a moduleLoader return url + ((cfg || userCfg).dontAddFileExt.test(url) ? '' : '.js'); }, loadScript: function (def, success, failure) { // script processing rules learned from RequireJS // insert script var el = doc.createElement('script'); // initial script processing function process (ev) { ev = ev || global.event; // detect when it's done loading // ev.type == 'load' is for all browsers except IE6-9 // IE6-9 need to use onreadystatechange and look for // el.readyState in {loaded, complete} (yes, we need both) if (ev.type == 'load' || readyStates[el.readyState]) { delete activeScripts[def.id]; // release event listeners el.onload = el.onreadystatechange = el.onerror = ''; // ie cries if we use undefined success(); } } function fail (e) { // some browsers send an event, others send a string, // but none of them send anything useful, so just say we failed: failure(new Error('Syntax or http error: ' + def.url)); } // set type first since setting other properties could // prevent us from setting this later // actually, we don't even need to set this at all //el.type = 'text/javascript'; // using dom0 event handlers instead of wordy w3c/ms el.onload = el.onreadystatechange = process; el.onerror = fail; // js! plugin uses alternate mimetypes el.type = def.mimetype || 'text/javascript'; // TODO: support other charsets? el.charset = 'utf-8'; el.async = !def.order; el.src = def.url; // loading will start when the script is inserted into the dom. // IE will load the script sync if it's in the cache, so // indicate the current resource definition if this happens. activeScripts[def.id] = el; head.insertBefore(el, insertBeforeEl); // the js! plugin uses this return el; }, extractCjsDeps: function (defFunc) { // Note: ignores require() inside strings and comments var source, ids = [], currQuote; // prefer toSource (FF) since it strips comments source = typeof defFunc == 'string' ? defFunc : defFunc.toSource ? defFunc.toSource() : defFunc.toString(); // remove comments, then look for require() or quotes source.replace(removeCommentsRx, '').replace(findRValueRequiresRx, function (m, rq, id, qq) { // if we encounter a string in the source, don't look for require() if (qq) { currQuote = currQuote == qq ? undef : currQuote; } // if we're not inside a quoted string else if (!currQuote) { ids.push(id); } return ''; // uses least RAM/CPU }); return ids; }, fixArgs: function (args) { // resolve args // valid combinations for define: // (string, array, object|function) sax|saf // (array, object|function) ax|af // (string, object|function) sx|sf // (object|function) x|f var id, deps, defFunc, defFuncArity, len, cjs; len = args.length; defFunc = args[len - 1]; defFuncArity = isType(defFunc, 'Function') ? defFunc.length : -1; if (len == 2) { if (isType(args[0], 'Array')) { deps = args[0]; } else { id = args[0]; } } else if (len == 3) { id = args[0]; deps = args[1]; } // Hybrid format: assume that a definition function with zero // dependencies and non-zero arity is a wrapped CommonJS module if (!deps && defFuncArity > 0) { cjs = true; deps = ['require', 'exports', 'module'].slice(0, defFuncArity).concat(core.extractCjsDeps(defFunc)); } return { id: id, deps: deps || [], res: defFuncArity >= 0 ? defFunc : function () { return defFunc; }, cjs: cjs }; }, executeDefFunc: function (def) { var resource, moduleThis; // the force of AMD is strong so anything returned // overrides exports. // node.js assumes `this` === `exports` so we do that // for all cjs-wrapped modules, just in case. // also, use module.exports if that was set // (node.js convention). // note: if .module exists, .exports exists. moduleThis = def.cjs ? def.exports : undef; resource = def.res.apply(moduleThis, def.deps); if (resource === undef && def.exports) { // note: exports will equal module.exports unless // module.exports was reassigned inside module. resource = def.module ? (def.exports = def.module.exports) : def.exports; } return resource; }, defineResource: function (def, args) { def.res = args.res; def.cjs = args.cjs; def.depNames = args.deps; core.getDeps(def); }, getDeps: function (parentDef) { var i, names, deps, len, dep, completed, name, exportCollector, resolveCollector; deps = []; names = parentDef.depNames; len = names.length; if (names.length == 0) allResolved(); function collect (dep, index, alsoExport) { deps[index] = dep; if (alsoExport) exportCollector(dep, index); } // reducer-collectors exportCollector = countdown(len, collect, allExportsReady); resolveCollector = countdown(len, collect, allResolved); // initiate the resolution of all dependencies // Note: the correct handling of early exports relies on the // fact that the exports pseudo-dependency is always listed // before other module dependencies. for (i = 0; i < len; i++) { name = names[i]; // is this "require", "exports", or "module"? if (name in cjsGetters) { // a side-effect of cjsGetters is that the cjs // property is also set on the def. resolveCollector(cjsGetters[name](parentDef), i, true); // if we are using the `module` or `exports` cjs variables, // signal any waiters/parents that we can export // early (see progress callback in getDep below). // note: this may fire for `require` as well, if it // is listed after `module` or `exports` in the deps list, // but that is okay since all waiters will only record // it once. if (parentDef.exports) { parentDef.progress(msgUsingExports); } } // check for blanks. fixes #32. // this helps support yepnope.js, has.js, and the has! plugin else if (!name) { resolveCollector(undef, i, true); } // normal module or plugin resource else { getDep(name, i); } } return parentDef; function getDep (name, index) { var resolveOnce, exportOnce, childDef, earlyExport; resolveOnce = countdown(1, function (dep) { exportOnce(dep); resolveCollector(dep, index); }); exportOnce = countdown(1, function (dep) { exportCollector(dep, index); }); // get child def / dep childDef = core.fetchDep(name, parentDef); // check if childDef can export. if it can, then // we missed the notification and it will never fire in the // when() below. earlyExport = isPromise(childDef) && childDef.exports; if (earlyExport) { exportOnce(earlyExport); } when(childDef, resolveOnce, parentDef.reject, parentDef.exports && function (msg) { // messages are only sent from childDefs that support // exports, and we only notify parents that understand // exports too. if (childDef.exports) { if (msg == msgUsingExports) { // if we're using exports cjs variable on both sides exportOnce(childDef.exports); } else if (msg == msgFactoryExecuted) { resolveOnce(childDef.exports); } } } ); } function allResolved () { parentDef.resolve(deps); } function allExportsReady () { parentDef.exportsReady && parentDef.exportsReady(deps); } }, fetchResDef: function (def) { // ensure url is computed core.getDefUrl(def); core.loadScript(def, function () { var args = argsNet; argsNet = undef; // reset it before we get deps // if our resource was not explicitly defined with an id (anonymous) // Note: if it did have an id, it will be resolved in the define() if (def.useNet !== false) { // if !args, nothing was added to the argsNet if (!args || args.ex) { def.reject(new Error(((args && args.ex) || 'define() missing or duplicated: ' + def.url))); } else { core.defineResource(def, args); } } }, def.reject ); return def; }, fetchDep: function (depName, parentDef) { var toAbsId, isPreload, cfg, parts, mainId, loaderId, pluginId, resId, pathInfo, def, tempDef, resCfg; toAbsId = parentDef.toAbsId; isPreload = parentDef.isPreload; cfg = parentDef.config || userCfg; // is this fallback necessary? // check for plugin loaderId // TODO: this runs pluginParts() twice. how to run it just once? parts = pluginParts(toAbsId(depName)); resId = parts.resourceId; // get id of first resource to load (which could be a plugin) mainId = parts.pluginId || resId; pathInfo = core.resolvePathInfo(mainId, cfg); // get custom module loader from package config if not a plugin if (parts.pluginId) { loaderId = mainId; } else { // TODO: move config.moduleLoader to config.transform loaderId = pathInfo.config['moduleLoader'] || pathInfo.config.moduleLoader; if (loaderId) { // TODO: allow transforms to have relative module ids? // (we could do this by returning package location from // resolvePathInfo. why not return all package info?) resId = mainId; mainId = loaderId; pathInfo = core.resolvePathInfo(loaderId, cfg); } } if (mainId in cache) { def = cache[mainId]; } else if (pathInfo.url in urlCache) { def = cache[mainId] = urlCache[pathInfo.url]; } else { def = core.createResourceDef(pathInfo.config, mainId, isPreload); // TODO: can this go inside createResourceDef? // TODO: can we pass pathInfo.url to createResourceDef instead? def.url = core.checkToAddJsExt(pathInfo.url, pathInfo.config); cache[mainId] = urlCache[pathInfo.url] = def; core.fetchResDef(def); } // plugin or transformer if (mainId == loaderId) { // we need to use an anonymous promise until plugin tells // us normalized id. then, we need to consolidate the promises // below. Note: exports objects will be different between // pre-normalized and post-normalized defs! does this matter? // don't put this resource def in the cache because if the // resId doesn't change, the check if this is a new // normalizedDef (below) will think it's already being loaded. tempDef = new Promise(); // note: this means moduleLoaders can store config info in the // plugins config, too. resCfg = cfg.plugins[loaderId] || cfg; // wait for plugin resource def when(def, function(plugin) { var normalizedDef, fullId, dynamic; dynamic = plugin['dynamic']; // check if plugin supports the normalize method if ('normalize' in plugin) { // note: dojo/has may return falsey values (0, actually) resId = plugin['normalize'](resId, toAbsId, def.config) || ''; } else { resId = toAbsId(resId); } // use the full id (loaderId + id) to id plugin resources // so multiple plugins may each process the same resource // resId could be blank if the plugin doesn't require any (e.g. "domReady!") fullId = loaderId + '!' + resId; normalizedDef = cache[fullId]; // if this is our first time fetching this (normalized) def if (!(fullId in cache)) { // because we're using resId, plugins, such as wire!, // can use paths relative to the resource normalizedDef = core.createPluginDef(resCfg, fullId, resId, isPreload); // don't cache non-determinate "dynamic" resources if (!dynamic) { cache[fullId] = normalizedDef; } // curl's plugins prefer to receive a deferred, // but to be compatible with AMD spec, we have to // piggy-back on the callback function parameter: var loaded = function (res) { normalizedDef.resolve(res); if (!dynamic) cache[fullId] = res; }; loaded['resolve'] = loaded; loaded['reject'] = loaded['error'] = normalizedDef.reject; // load the resource! plugin.load(resId, normalizedDef.require, loaded, resCfg); } // chain defs (resolve when plugin.load executes) if (tempDef != normalizedDef) { when(normalizedDef, tempDef.resolve, tempDef.reject, tempDef.progress); } }, tempDef.reject); } // return tempDef if this is a plugin-based resource return tempDef || def; }, getCurrentDefName: function () { // IE6-9 mark the currently executing thread as "interactive" // Note: Opera lies about which scripts are "interactive", so we // just have to test for it. Opera provides a true browser test, not // a UA sniff, thankfully. // learned this trick from James Burke's RequireJS var def; if (!isType(global.opera, 'Opera')) { for (var d in activeScripts) { if (activeScripts[d].readyState == 'interactive') { def = d; break; } } } return def; } }; // hook-up cjs free variable getters cjsGetters = {'require': core.getCjsRequire, 'exports': core.getCjsExports, 'module': core.getCjsModule}; function _curl (/* various */) { var args = [].slice.call(arguments), cfg; // extract config, if it's specified if (isType(args[0], 'Object')) { cfg = args.shift(); _config(cfg); } return new CurlApi(args[0], args[1], args[2]); } function _config (cfg) { if (cfg) { core.setApi(cfg); userCfg = core.config(cfg); // check for preloads if ('preloads' in cfg) { preload = new CurlApi(cfg['preloads'], undef, undef, preload, true); } // check for main module(s) if ('main' in cfg) { new CurlApi(cfg['main']) } } } // thanks to Joop Ringelberg for helping troubleshoot the API function CurlApi (ids, callback, errback, waitFor, isPreload) { var then, ctx; ctx = core.createContext(userCfg, undef, [].concat(ids), isPreload); this['then'] = then = function (resolved, rejected) { when(ctx, // return the dependencies as arguments, not an array function (deps) { if (resolved) resolved.apply(undef, deps); }, // just throw if the dev didn't specify an error handler function (ex) { if (rejected) rejected(ex); else throw ex; } ); return this; }; this['next'] = function (ids, cb, eb) { // chain api return new CurlApi(ids, cb, eb, ctx); }; this['config'] = _config; if (callback || errback) then(callback, errback); // ensure next-turn for builds setTimeout(function () { when(isPreload || preload, function () { when(waitFor, function () { core.getDeps(ctx); }); }); }, 0); } _curl['version'] = version; _curl['config'] = _config; function _define (args) { var id, def, pathInfo; id = args.id; if (id == undef) { if (argsNet !== undef) { argsNet = { ex: 'Multiple anonymous defines in url' }; } else if (!(id = core.getCurrentDefName())/* intentional assignment */) { // anonymous define(), defer processing until after script loads argsNet = args; } } if (id != undef) { // named define(), it is in the cache if we are loading a dependency // (could also be a secondary define() appearing in a built file, etc.) def = cache[id]; if (!(id in cache)) { // id is an absolute id in this case, so we can get the config. pathInfo = core.resolvePathInfo(id, userCfg); def = core.createResourceDef(pathInfo.config, id); cache[id] = def; } if (!isPromise(def)) throw new Error('duplicate define: ' + id); // check if this resource has already been resolved def.useNet = false; core.defineResource(def, args); } } function define () { // wrap inner _define so it can be replaced without losing define.amd var args = core.fixArgs(arguments); _define(args); } // indicate our capabilities: define['amd'] = { 'plugins': true, 'jQuery': true, 'curl': version }; // default configs userCfg = { baseUrl: '', pluginPath: 'curl/plugin', dontAddFileExt: dontAddExtRx, paths: {}, packages: {}, plugins: {}, pathMap: {}, pathRx: /$^/ }; // handle pre-existing global prevCurl = global[curlName]; prevDefine = global[defineName]; if (!prevCurl || isType(prevCurl, 'Function')) { // set default api core.setApi(); } else { // remove global curl object global[curlName] = undef; // can't use delete in IE 6-8 // configure curl _config(prevCurl); } // allow curl to be a dependency cache[curlName] = _curl; // expose curl core for special plugins and modules // Note: core overrides will only work in either of two scenarios: // 1. the files are running un-compressed (Google Closure or Uglify) // 2. the overriding module was compressed into the same file as curl.js // Compiling curl and the overriding module separately won't work. cache['curl/_privileged'] = { 'core': core, 'cache': cache, 'config': function () { return userCfg; }, '_define': _define, '_curl': _curl, 'Promise': Promise }; }(this.window || (typeof global != 'undefined' && global) || this)); ;(function () { var config = { baseUrl: '', paths: { curl: 'support/curl/src/curl', "test-js": 'test/data', "sizzle": "../sizzle/sizzle" }, packages: [ // note: i realize this only works if wire repo is a peer to cram. { name: 'wire', location: '../wire', main: './wire' }, { name: 'meld', location: '../meld', main: './meld' }, { name: 'poly', location: '../poly', main: './poly' } ], pluginPath: 'curl/plugin', preloads: ['poly/all'], main: 'wire!test-js/spec' }; curl(config); }()); /** * XHR polyfill / shims * * (c) copyright 2011-2012 Brian Cavalier and John Hann * * This module 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 * */ define('poly/xhr', function () { var progIds; // find XHR implementation if (typeof XMLHttpRequest == 'undefined') { // create xhr impl that will fail if called. assignCtor(function () { throw new Error("poly/xhr: XMLHttpRequest not available"); }); // keep trying progIds until we find the correct one, progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0']; while (progIds.length && tryProgId(progIds.shift())) {} } function assignCtor (ctor) { // assign window.XMLHttpRequest function window.XMLHttpRequest = ctor; } function tryProgId (progId) { try { new ActiveXObject(progId); assignCtor(function () { return new ActiveXObject(progId); }); return true; } catch (ex) {} } }); /** * poly common functions * * (c) copyright 2011-2012 Brian Cavalier and John Hann * * This module 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 * */ define('poly/lib/_base', ['require', 'exports', 'module'], function (require, exports, module) { var toString; toString = ({}).toString; exports.isFunction = function (o) { return typeof o == 'function'; }; exports.isString = function (o) { return toString.call(o) == '[object String]'; }; exports.toString = function (o) { return toString.apply(o); }; exports.createCaster = function (caster, name) { return function cast (o) { if (o == null) throw new TypeError(name + ' method called on null or undefined'); return caster(o); } } }); /*! JSON v3.2.4 | http://bestiejs.github.com/json3 | Copyright 2012, Kit Cambridge | http://kit.mit-license.org */ ;(function () { // Convenience aliases. var getClass = {}.toString, isProperty, forEach, undef; // Detect the `define` function exposed by asynchronous module loaders. The // strict `define` check is necessary for compatibility with `r.js`. var isLoader = typeof define === "function" && define.amd, JSON3 = !isLoader && typeof exports == "object" && exports; if (JSON3 || isLoader) { if (typeof JSON == "object" && JSON) { // Delegate to the native `stringify` and `parse` implementations in // asynchronous module loaders and CommonJS environments. if (isLoader) { JSON3 = JSON; } else { JSON3.stringify = JSON.stringify; JSON3.parse = JSON.parse; } } else if (isLoader) { JSON3 = this.JSON = {}; } } else { // Export for web browsers and JavaScript engines. JSON3 = this.JSON || (this.JSON = {}); } // Local variables. var Escapes, toPaddedString, quote, serialize; var fromCharCode, Unescapes, abort, lex, get, walk, update, Index, Source; // Test the `Date#getUTC*` methods. Based on work by @Yaffle. var isExtended = new Date(-3509827334573292), floor, Months, getDay; try { // The `getUTCFullYear`, `Month`, and `Date` methods return nonsensical // results for certain dates in Opera >= 10.53. isExtended = isExtended.getUTCFullYear() == -109252 && isExtended.getUTCMonth() === 0 && isExtended.getUTCDate() == 1 && // Safari < 2.0.2 stores the internal millisecond time value correctly, // but clips the values returned by the date methods to the range of // signed 32-bit integers ([-2 ** 31, 2 ** 31 - 1]). isExtended.getUTCHours() == 10 && isExtended.getUTCMinutes() == 37 && isExtended.getUTCSeconds() == 6 && isExtended.getUTCMilliseconds() == 708; } catch (exception) {} // Internal: Determines whether the native `JSON.stringify` and `parse` // implementations are spec-compliant. Based on work by Ken Snyder. function has(name) { var stringifySupported, parseSupported, value, serialized = '{"A":[1,true,false,null,"\\u0000\\b\\n\\f\\r\\t"]}', all = name == "json"; if (all || name == "json-stringify" || name == "json-parse") { // Test `JSON.stringify`. if (name == "json-stringify" || all) { if ((stringifySupported = typeof JSON3.stringify == "function" && isExtended)) { // A test function object with a custom `toJSON` method. (value = function () { return 1; }).toJSON = value; try { stringifySupported = // Firefox 3.1b1 and b2 serialize string, number, and boolean // primitives as object literals. JSON3.stringify(0) === "0" && // FF 3.1b1, b2, and JSON 2 serialize wrapped primitives as object // literals. JSON3.stringify(new Number()) === "0" && JSON3.stringify(new String()) == '""' && // FF 3.1b1, 2 throw an error if the value is `null`, `undefined`, or // does not define a canonical JSON representation (this applies to // objects with `toJSON` properties as well, *unless* they are nested // within an object or array). JSON3.stringify(getClass) === undef && // IE 8 serializes `undefined` as `"undefined"`. Safari <= 5.1.7 and // FF 3.1b3 pass this test. JSON3.stringify(undef) === undef && // Safari <= 5.1.7 and FF 3.1b3 throw `Error`s and `TypeError`s, // respectively, if the value is omitted entirely. JSON3.stringify() === undef && // FF 3.1b1, 2 throw an error if the given value is not a number, // string, array, object, Boolean, or `null` literal. This applies to // objects with custom `toJSON` methods as well, unless they are nested // inside object or array literals. YUI 3.0.0b1 ignores custom `toJSON` // methods entirely. JSON3.stringify(value) === "1" && JSON3.stringify([value]) == "[1]" && // Prototype <= 1.6.1 serializes `[undefined]` as `"[]"` instead of // `"[null]"`. JSON3.stringify([undef]) == "[null]" && // YUI 3.0.0b1 fails to serialize `null` literals. JSON3.stringify(null) == "null" && // FF 3.1b1, 2 halts serialization if an array contains a function: // `[1, true, getClass, 1]` serializes as "[1,true,],". These versions // of Firefox also allow trailing commas in JSON objects and arrays. // FF 3.1b3 elides non-JSON values from objects and arrays, unless they // define custom `toJSON` methods. JSON3.stringify([undef, getClass, null]) == "[null,null,null]" && // Simple serialization test. FF 3.1b1 uses Unicode escape sequences // where character escape codes are expected (e.g., `\b` => `\u0008`). JSON3.stringify({ "A": [value, true, false, null, "\0\b\n\f\r\t"] }) == serialized && // FF 3.1b1 and b2 ignore the `filter` and `width` arguments. JSON3.stringify(null, value) === "1" && JSON3.stringify([1, 2], null, 1) == "[\n 1,\n 2\n]" && // JSON 2, Prototype <= 1.7, and older WebKit builds incorrectly // serialize extended years. JSON3.stringify(new Date(-8.64e15)) == '"-271821-04-20T00:00:00.000Z"' && // The milliseconds are optional in ES 5, but required in 5.1. JSON3.stringify(new Date(8.64e15)) == '"+275760-09-13T00:00:00.000Z"' && // Firefox <= 11.0 incorrectly serializes years prior to 0 as negative // four-digit years instead of six-digit years. Credits: @Yaffle. JSON3.stringify(new Date(-621987552e5)) == '"-000001-01-01T00:00:00.000Z"' && // Safari <= 5.1.5 and Opera >= 10.53 incorrectly serialize millisecond // values less than 1000. Credits: @Yaffle. JSON3.stringify(new Date(-1)) == '"1969-12-31T23:59:59.999Z"'; } catch (exception) { stringifySupported = false; } } if (!all) { return stringifySupported; } } // Test `JSON.parse`. if (name == "json-parse" || all) { if (typeof JSON3.parse == "function") { try { // FF 3.1b1, b2 will throw an exception if a bare literal is provided. // Conforming implementations should also coerce the initial argument to // a string prior to parsing. if (JSON3.parse("0") === 0 && !JSON3.parse(false)) { // Simple parsing test. value = JSON3.parse(serialized); if ((parseSupported = value.A.length == 5 && value.A[0] == 1)) { try { // Safari <= 5.1.2 and FF 3.1b1 allow unescaped tabs in strings. parseSupported = !JSON3.parse('"\t"'); } catch (exception) {} if (parseSupported) { try { // FF 4.0 and 4.0.1 allow leading `+` signs, and leading and // trailing decimal points. FF 4.0, 4.0.1, and IE 9-10 also // allow certain octal literals. parseSupported = JSON3.parse("01") != 1; } catch (exception) {} } } } } catch (exception) { parseSupported = false; } } if (!all) { return parseSupported; } } return stringifySupported && parseSupported; } } if (!has("json")) { // Define additional utility methods if the `Date` methods are buggy. if (!isExtended) { floor = Math.floor; // A mapping between the months of the year and the number of days between // January 1st and the first of the respective month. Months = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]; // Internal: Calculates the number of days between the Unix epoch and the // first day of the given month. getDay = function (year, month) { return Months[month] + 365 * (year - 1970) + floor((year - 1969 + (month = +(month > 1))) / 4) - floor((year - 1901 + month) / 100) + floor((year - 1601 + month) / 400); }; } // Internal: Determines if a property is a direct property of the given // object. Delegates to the native `Object#hasOwnProperty` method. if (!(isProperty = {}.hasOwnProperty)) { isProperty = function (property) { var members = {}, constructor; if ((members.__proto__ = null, members.__proto__ = { // The *proto* property cannot be set multiple times in recent // versions of Firefox and SeaMonkey. "toString": 1 }, members).toString != getClass) { // Safari <= 2.0.3 doesn't implement `Object#hasOwnProperty`, but // supports the mutable *proto* property. isProperty = function (property) { // Capture and break the object's prototype chain (see section 8.6.2 // of the ES 5.1 spec). The parenthesized expression prevents an // unsafe transformation by the Closure Compiler. var original = this.__proto__, result = property in (this.__proto__ = null, this); // Restore the original prototype chain. this.__proto__ = original; return result; }; } else { // Capture a reference to the top-level `Object` constructor. constructor = members.constructor; // Use the `constructor` property to simulate `Object#hasOwnProperty` in // other environments. isProperty = function (property) { var parent = (this.constructor || constructor).prototype; return property in this && !(property in parent && this[property] === parent[property]); }; } members = null; return isProperty.call(this, property); }; } // Internal: Normalizes the `for...in` iteration algorithm across // env