// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. 'use strict'; /** * The global object. * @type {!Object} * @const */ var global = this; /** Platform, package, object property, and Event support. */ this.base = (function() { /** * Base path for modules. Used to form URLs for module 'require' requests. */ var moduleBasePath = '.'; function setModuleBasePath(path) { if (path[path.length - 1] == '/') path = path.substring(0, path.length - 1); moduleBasePath = path; } function mLog(text, opt_indentLevel) { if (true) return; var spacing = ''; var indentLevel = opt_indentLevel || 0; for (var i = 0; i < indentLevel; i++) spacing += ' '; console.log(spacing + text); } /** * Builds an object structure for the provided namespace path, * ensuring that names that already exist are not overwritten. For * example: * 'a.b.c' -> a = {};a.b={};a.b.c={}; * @param {string} name Name of the object that this file defines. * @param {*=} opt_object The object to expose at the end of the path. * @param {Object=} opt_objectToExportTo The object to add the path to; * default is {@code global}. * @private */ function exportPath(name, opt_object, opt_objectToExportTo) { var parts = name.split('.'); var cur = opt_objectToExportTo || global; for (var part; parts.length && (part = parts.shift());) { if (!parts.length && opt_object !== undefined) { // last part and we have an object; use it cur[part] = opt_object; } else if (part in cur) { cur = cur[part]; } else { cur = cur[part] = {}; } } return cur; }; var didLoadModules = false; var moduleDependencies = {}; var moduleStylesheets = {}; var moduleRawScripts = {}; function addModuleDependency(moduleName, dependentModuleName) { if (!moduleDependencies[moduleName]) moduleDependencies[moduleName] = []; var dependentModules = moduleDependencies[moduleName]; var found = false; for (var i = 0; i < dependentModules.length; i++) if (dependentModules[i] == dependentModuleName) found = true; if (!found) dependentModules.push(dependentModuleName); } function addModuleRawScriptDependency(moduleName, rawScriptName) { if (!moduleRawScripts[moduleName]) moduleRawScripts[moduleName] = []; var dependentRawScripts = moduleRawScripts[moduleName]; var found = false; for (var i = 0; i < moduleRawScripts.length; i++) if (dependentRawScripts[i] == rawScriptName) found = true; if (!found) dependentRawScripts.push(rawScriptName); } function addModuleStylesheet(moduleName, stylesheetName) { if (!moduleStylesheets[moduleName]) moduleStylesheets[moduleName] = []; var stylesheets = moduleStylesheets[moduleName]; var found = false; for (var i = 0; i < stylesheets.length; i++) if (stylesheets[i] == stylesheetName) found = true; if (!found) stylesheets.push(stylesheetName); } function ensureDepsLoaded() { if (window.FLATTENED) return; if (didLoadModules) return; didLoadModules = true; var req = new XMLHttpRequest(); var src = '/deps.js'; req.open('GET', src, false); req.send(null); if (req.status != 200) { var serverSideException = JSON.parse(req.responseText); var msg = 'You have a module problem: ' + serverSideException.message; var baseWarningEl = document.createElement('div'); baseWarningEl.style.position = 'fixed'; baseWarningEl.style.border = '3px solid red'; baseWarningEl.style.color = 'black'; baseWarningEl.style.padding = '8px'; baseWarningEl.innerHTML = '<h2>Module parsing problem</h2>' + '<div id="message"></div>' + '<pre id="details"></pre>'; baseWarningEl.querySelector('#message').textContent = serverSideException.message; var detailsEl = baseWarningEl.querySelector('#details'); detailsEl.textContent = serverSideException.details; detailsEl.style.maxWidth = '800px'; detailsEl.style.overflow = 'auto'; if (!document.body) { setTimeout(function() { document.body.appendChild(baseWarningEl); }, 150); } else { document.body.appendChild(baseWarningEl); } throw new Error(msg); } base.addModuleDependency = addModuleDependency; base.addModuleRawScriptDependency = addModuleRawScriptDependency; base.addModuleStylesheet = addModuleStylesheet; try { // By construction, the deps should call addModuleDependency. eval(req.responseText); } catch (e) { throw new Error('When loading deps, got ' + e.stack ? e.stack : e.message); } delete base.addModuleStylesheet; delete base.addModuleRawScriptDependency; delete base.addModuleDependency; } // TODO(dsinclair): Remove this when HTML imports land as the templates // will be pulled in by the requireTemplate calls. var templatesLoaded_ = false; function ensureTemplatesLoaded() { if (templatesLoaded_ || window.FLATTENED) return; templatesLoaded_ = true; var req = new XMLHttpRequest(); req.open('GET', '/templates', false); req.send(null); var elem = document.createElement('div'); elem.innerHTML = req.responseText; while (elem.hasChildNodes()) document.head.appendChild(elem.removeChild(elem.firstChild)); } var moduleLoadStatus = {}; var rawScriptLoadStatus = {}; function require(modules, opt_indentLevel) { var indentLevel = opt_indentLevel || 0; var dependentModules = modules; if (!(modules instanceof Array)) dependentModules = [modules]; ensureDepsLoaded(); ensureTemplatesLoaded(); dependentModules.forEach(function(module) { requireModule(module, indentLevel); }); } var modulesWaiting = []; function requireModule(dependentModuleName, indentLevel) { if (window.FLATTENED) { if (!window.FLATTENED[dependentModuleName]) { throw new Error('Somehow, module ' + dependentModuleName + ' didn\'t get stored in the flattened js file! ' + 'You may need to rerun ' + 'build/generate_about_tracing_contents.py'); } return; } if (moduleLoadStatus[dependentModuleName] == 'APPENDED') return; if (moduleLoadStatus[dependentModuleName] == 'RESOLVING') return; mLog('require(' + dependentModuleName + ')', indentLevel); moduleLoadStatus[dependentModuleName] = 'RESOLVING'; requireDependencies(dependentModuleName, indentLevel); loadScript(dependentModuleName.replace(/\./g, '/') + '.js'); moduleLoadStatus[name] = 'APPENDED'; } function requireDependencies(dependentModuleName, indentLevel) { // Load the module's dependent scripts after. var dependentModules = moduleDependencies[dependentModuleName] || []; require(dependentModules, indentLevel + 1); // Load the module stylesheet first. var stylesheets = moduleStylesheets[dependentModuleName] || []; for (var i = 0; i < stylesheets.length; i++) requireStylesheet(stylesheets[i]); // Load the module raw scripts next var rawScripts = moduleRawScripts[dependentModuleName] || []; for (var i = 0; i < rawScripts.length; i++) { var rawScriptName = rawScripts[i]; if (rawScriptLoadStatus[rawScriptName]) continue; loadScript(rawScriptName); mLog('load(' + rawScriptName + ')', indentLevel); rawScriptLoadStatus[rawScriptName] = 'APPENDED'; } } function loadScript(path) { var scriptEl = document.createElement('script'); scriptEl.src = moduleBasePath + '/' + path; scriptEl.type = 'text/javascript'; scriptEl.defer = true; scriptEl.async = false; base.doc.head.appendChild(scriptEl); } /** * Adds a dependency on a raw javascript file, e.g. a third party * library. * @param {String} rawScriptName The path to the script file, relative to * moduleBasePath. */ function requireRawScript(rawScriptPath) { if (window.FLATTENED_RAW_SCRIPTS) { if (!window.FLATTENED_RAW_SCRIPTS[rawScriptPath]) { throw new Error('Somehow, ' + rawScriptPath + ' didn\'t get stored in the flattened js file! ' + 'You may need to rerun build/generate_about_tracing_contents.py'); } return; } if (rawScriptLoadStatus[rawScriptPath]) return; throw new Error(rawScriptPath + ' should already have been loaded.' + ' Did you forget to run build/generate_about_tracing_contents.py?'); } var stylesheetLoadStatus = {}; function requireStylesheet(dependentStylesheetName) { if (window.FLATTENED) return; if (stylesheetLoadStatus[dependentStylesheetName]) return; stylesheetLoadStatus[dependentStylesheetName] = true; var localPath = dependentStylesheetName.replace(/\./g, '/') + '.css'; var stylesheetPath = moduleBasePath + '/' + localPath; var linkEl = document.createElement('link'); linkEl.setAttribute('rel', 'stylesheet'); linkEl.setAttribute('href', stylesheetPath); base.doc.head.appendChild(linkEl); } var templateLoadStatus = {}; function requireTemplate(template) { if (window.FLATTENED) return; if (templateLoadStatus[template]) return; templateLoadStatus[template] = true; var localPath = template.replace(/\./g, '/') + '.html'; var importPath = moduleBasePath + '/' + localPath; var linkEl = document.createElement('link'); linkEl.setAttribute('rel', 'import'); linkEl.setAttribute('href', importPath); // TODO(dsinclair): Enable when HTML imports are available. //base.doc.head.appendChild(linkEl); } function exportTo(namespace, fn) { var obj = exportPath(namespace); try { var exports = fn(); } catch (e) { console.log('While running exports for ', namespace, ':'); console.log(e.stack || e); return; } for (var propertyName in exports) { // Maybe we should check the prototype chain here? The current usage // pattern is always using an object literal so we only care about own // properties. var propertyDescriptor = Object.getOwnPropertyDescriptor(exports, propertyName); if (propertyDescriptor) { Object.defineProperty(obj, propertyName, propertyDescriptor); mLog(' +' + propertyName); } } }; /** * Initialization which must be deferred until run-time. */ function initialize() { // If 'document' isn't defined, then we must be being pre-compiled, // so set a trap so that we're initialized on first access at run-time. if (!global.document) { var originalBase = base; Object.defineProperty(global, 'base', { get: function() { Object.defineProperty(global, 'base', {value: originalBase}); originalBase.initialize(); return originalBase; }, configurable: true }); return; } base.doc = document; base.isMac = /Mac/.test(navigator.platform); base.isWindows = /Win/.test(navigator.platform); base.isChromeOS = /CrOS/.test(navigator.userAgent); base.isLinux = /Linux/.test(navigator.userAgent); base.isGTK = /GTK/.test(chrome.toolkit); base.isViews = /views/.test(chrome.toolkit); setModuleBasePath('/src'); } return { set moduleBasePath(path) { setModuleBasePath(path); }, get moduleBasePath() { return moduleBasePath; }, initialize: initialize, require: require, requireStylesheet: requireStylesheet, requireRawScript: requireRawScript, requireTemplate: requireTemplate, exportTo: exportTo }; })(); base.initialize();