/* <copyright> This file contains proprietary software owned by Motorola Mobility, Inc.<br/> No rights, expressed or implied, whatsoever to this software are provided by Motorola Mobility, Inc. hereunder.<br/> (c) Copyright 2011 Motorola Mobility, Inc. All Rights Reserved. </copyright> */ document._montageTiming = {} document._montageTiming.loadStartTime = Date.now(); // Give a threshold before we decide we need to show the bootstrapper progress // Applications that use our loader will interact with this timeout // and class name to coordinate a nice loading experience. Applications that do not will // just go about business as usual and draw their content as soon as possible. window.addEventListener("DOMContentLoaded", function() { var bootstrappingDelay = 1000; document._montageStartBootstrappingTimeout = setTimeout(function() { document._montageStartBootstrappingTimeout = null; var root = document.documentElement; if(!!root.classList) { root.classList.add("montage-app-bootstrapping"); } else { root.className = root.className + " montage-app-bootstrapping"; } document._montageTiming.bootstrappingStartTime = Date.now(); }, bootstrappingDelay); }); (function (definition) { if (typeof require !== "undefined") { // CommonJS / NodeJS definition(require, exports, module); } else { // <script> definition({}, {}, {}); } })(function (require, exports, module) { // The global context object, works for the browser and for node. // XXX Will not work in strict mode var global = (function() { return this; })(); /** * Initializes Montage and creates the application singleton if necessary. * @param options * @param callback */ exports.initMontage = function () { var platform = exports.getPlatform(); var params = platform.getParams(); var config = platform.getConfig(); // Platform dependent platform.loadCJS(function (CJS, Q, URL) { // setup the reel loader config.makeLoader = function (config) { return exports.ReelLoader(config, CJS.DefaultLoaderConstructor(config)); }; // setup serialization compiler config.makeCompiler = function (config) { return exports.TemplateCompiler(config, exports.SerializationCompiler(config, CJS.DefaultCompilerConstructor(config))); }; var location = URL.resolve(window.location, params["package"] || "."); CJS.PackageSandbox(params.montageBase, config) .then(function (montageRequire) { montageRequire.config.modules["core/promise"] = {exports: Q}; montageRequire.config.modules["core/url"] = {exports: URL}; montageRequire.config.modules["core/shim/timers"] = {exports: {}}; return montageRequire.loadPackage(location) .then(function (applicationRequire) { global.require = applicationRequire; global.montageRequire = montageRequire; platform.initMontage(montageRequire, applicationRequire, params); }) }) .end(); }); }; /** Adds "_montage_metadata" property to all objects and function attached to the exports object. @see Compiler middleware in require/require.js @param config @param compiler */ exports.SerializationCompiler = function(config, compiler) { return function(def) { def = compiler(def); var defaultFactory = def.factory; def.factory = function(require, exports, module) { defaultFactory.call(this, require, exports, module); for (var symbol in exports) { // avoid attempting to reinitialize an aliased property if ( Object.prototype.hasOwnProperty.call( exports[symbol], "_montage_metadata" ) ) { exports[symbol]._montage_metadata.aliases.push(symbol); exports[symbol]._montage_metadata.objectName = symbol; } else if (!Object.isSealed(exports[symbol])) { Object.defineProperty( exports[symbol], "_montage_metadata", { value: { require: require, moduleId: module.id, objectName: symbol, aliases: [symbol], isInstance: false } } ); } } }; return def; }; }; /** * Allows reel directories to load the contained eponymous JavaScript * module. * @see Loader middleware in require/require.js * @param config * @param loader the next loader in the chain */ var reelExpression = /([^\/]+)\.reel$/; exports.ReelLoader = function (config, loader) { return function (id, callback) { var match = reelExpression.exec(id); if (match) { return loader(id + "/" + match[1], callback); } else { return loader(id, callback); } }; }; /** Allows the reel's html file to be loaded via require. @see Compiler middleware in require/require.js @param config @param compiler */ exports.TemplateCompiler = function(config, compiler) { return function(def) { var root = def.path.match(/(.*\/)?(?=[^\/]+\.html$)/); if (root) { def.dependencies = def.dependencies || []; var originalFactory = def.factory; def.factory = function(require, exports, module) { if (originalFactory) { originalFactory(require, exports, module); } // Use module.exports in case originalFactory changed it. module.exports.root = module.exports.root || root; module.exports.content = module.exports.content || def.text; }; return def; } else { return compiler(def); } }; }; // Bootstrapping for multiple-platforms exports.getPlatform = function () { if (typeof window !== "undefined" && window && window.document) { return browser; } else { throw new Error("Platform not supported."); } }; var browser = { getConfig: function() { return { lib: ".", base: window.location, // Disable XHR loader for file:// xhr: window.location.protocol.indexOf("file:") !== 0 }; }, getParams: function() { var i, j, match, script, attr, name; if (!this._params) { this._params = {}; // Find the <script> that loads us, so we can divine our // parameters from its attributes. var scripts = document.getElementsByTagName("script"); for (i = 0; i < scripts.length; i++) { script = scripts[i]; if (script.src && (match = script.src.match(/^(.*)montage.js(?:[\?\.]|$)/i))) { this._params.montageBase = match[1]; if (script.dataset) { for (name in script.dataset) { this._params[name] = script.dataset[name]; } } else if (script.attributes) { for (j = 0; j < script.attributes.length; j++) { attr = script.attributes[j]; match = attr.name.match(/^data-(.*)$/); if (match) { this._params[match[1]] = attr.value; } } } // Permits multiple montage.js <scripts>; by // removing as they are discovered, next one // finds itself. script.parentNode.removeChild(script); break; } } } return this._params; }, loadCJS: function (callback) { var base, CJS, DOM, Q, URL; var params = this.getParams(); // observe dom loading and load scripts in parallel // observe dom loaded function domLoad() { document.removeEventListener("DOMContentLoaded", domLoad, true); DOM = true; callbackIfReady(); } // this permits montage.js to be injected after domready if (document.readyState === "complete") { domLoad(); } else { document.addEventListener("DOMContentLoaded", domLoad, true); } // determine which scripts to load var pending = [ "require/require", "require/browser", "core/promise", "core/url", "core/jshint" ]; if (typeof setImmediate === "undefined") pending.push("core/shim/timers"); // load in parallel pending.forEach(function(name) { var url = params.montageBase + name + ".js"; var script = document.createElement("script"); script.src = url; script.onload = function () { // remove clutter script.parentNode.removeChild(script); }; document.getElementsByTagName("head")[0].appendChild(script); }); // register module definitions for deferred, // serial execution var definitions = {}; global.bootstrap = function (id, factory) { definitions[id] = factory; var at = pending.indexOf(id); pending.splice(at, 1); if (pending.length === 0) { allModulesLoaded(); } }; // miniature module system var bootModules = {}; function bootRequire(id) { if (!bootModules[id] && definitions[id]) { var exports = bootModules[id] = {}; definitions[id](bootRequire, exports); } return bootModules[id]; } // execute bootstrap scripts function allModulesLoaded() { Q = bootRequire("core/promise"); URL = bootRequire("core/url"); CJS = bootRequire("require/require"); bootRequire("require/browser"); delete global.bootstrap; callbackIfReady(); } function callbackIfReady() { if (DOM && CJS) { callback(CJS, Q, URL); } } }, initMontage: function (montageRequire, applicationRequire, options) { // If a module was specified in the config then we initialize it now if (options.module) { applicationRequire.async(options.module) .end(); } else { // otherwise we load the application montageRequire.async("ui/application", function(exports) { montageRequire.async("core/event/event-manager", function(eventManagerExports) { var defaultEventManager = eventManagerExports.defaultEventManager; // montageWillLoad is mostly for testing purposes if (typeof global.montageWillLoad === "function") { global.montageWillLoad(); } exports.Application.load(function(application) { exports.application = application; window.document.application = application; defaultEventManager.application = application; application.eventManager = defaultEventManager; }); }); }); } } }; if (global.__MONTAGE_LOADED__) { console.warn("Montage already loaded!"); } else { global.__MONTAGE_LOADED__ = true; exports.initMontage(); } });