574047c394958db35473a475198f5be446435d72
[platform/framework/web/crosswalk.git] / src / third_party / trace-viewer / third_party / tvcm / src / tvcm / __init__.js
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 'use strict';
6
7
8 /**
9  * The global object.
10  * @type {!Object}
11  * @const
12  */
13 var global = this;
14
15
16 /** Platform, package, object property, and Event support. */
17 this.tvcm = (function() {
18   function mLog(text, opt_indentLevel) {
19     if (true)
20       return;
21
22     var spacing = '';
23     var indentLevel = opt_indentLevel || 0;
24     for (var i = 0; i < indentLevel; i++)
25       spacing += ' ';
26     console.log(spacing + text);
27   }
28
29   /**
30    * Builds an object structure for the provided namespace path,
31    * ensuring that names that already exist are not overwritten. For
32    * example:
33    * 'a.b.c' -> a = {};a.b={};a.b.c={};
34    * @param {string} name Name of the object that this file defines.
35    * @param {*=} opt_object The object to expose at the end of the path.
36    * @param {Object=} opt_objectToExportTo The object to add the path to;
37    *     default is {@code global}.
38    * @private
39    */
40   function exportPath(name, opt_object, opt_objectToExportTo) {
41     var parts = name.split('.');
42     var cur = opt_objectToExportTo || global;
43
44     for (var part; parts.length && (part = parts.shift());) {
45       if (!parts.length && opt_object !== undefined) {
46         // last part and we have an object; use it
47         cur[part] = opt_object;
48       } else if (part in cur) {
49         cur = cur[part];
50       } else {
51         cur = cur[part] = {};
52       }
53     }
54     return cur;
55   };
56
57   var didLoadModules = false;
58   var moduleDependencies = {};
59   var moduleStylesheets = {};
60   var moduleRawScripts = {};
61   var resourceFileNames = {};
62
63   function setResourceFileName(moduleName, relativeFileName) {
64     if (resourceFileNames[moduleName] !== undefined)
65       throw new Error('Cannot set file name twice!');
66     resourceFileNames[moduleName] = relativeFileName;
67   }
68
69   function addModuleDependency(moduleName, dependentModuleName) {
70     if (!moduleDependencies[moduleName])
71       moduleDependencies[moduleName] = [];
72
73     var dependentModules = moduleDependencies[moduleName];
74     var found = false;
75     for (var i = 0; i < dependentModules.length; i++)
76       if (dependentModules[i] == dependentModuleName)
77         found = true;
78       if (!found)
79         dependentModules.push(dependentModuleName);
80   }
81
82   function addModuleRawScriptDependency(moduleName, rawScriptFilename) {
83     if (!moduleRawScripts[moduleName])
84       moduleRawScripts[moduleName] = [];
85
86     var dependentRawScripts = moduleRawScripts[moduleName];
87     var found = false;
88     for (var i = 0; i < moduleRawScripts.length; i++)
89       if (dependentRawScripts[i] == rawScriptFilename)
90         found = true;
91       if (!found)
92         dependentRawScripts.push(rawScriptFilename);
93   }
94
95   function addModuleStylesheet(moduleName, stylesheetName) {
96     if (!moduleStylesheets[moduleName])
97       moduleStylesheets[moduleName] = [];
98
99     var stylesheets = moduleStylesheets[moduleName];
100     var found = false;
101     for (var i = 0; i < stylesheets.length; i++)
102       if (stylesheets[i] == stylesheetName)
103         found = true;
104       if (!found)
105         stylesheets.push(stylesheetName);
106   }
107
108   function ensureDepsLoaded() {
109     if (window.FLATTENED)
110       return;
111
112     if (didLoadModules)
113       return;
114     didLoadModules = true;
115
116     var req = new XMLHttpRequest();
117     var src = '/tvcm/deps.js';
118     req.open('GET', src, false);
119     req.send(null);
120     if (req.status != 200) {
121       var serverSideException = JSON.parse(req.responseText);
122       var msg = 'You have a module problem: ' +
123           serverSideException.message;
124       showPanic(serverSideException.message,
125                 serverSideException.details);
126       throw new Error(msg);
127     }
128
129     tvcm.setResourceFileName = setResourceFileName;
130     tvcm.addModuleDependency = addModuleDependency;
131     tvcm.addModuleRawScriptDependency = addModuleRawScriptDependency;
132     tvcm.addModuleStylesheet = addModuleStylesheet;
133     try {
134       // By construction, the deps should call addModuleDependency.
135       eval(req.responseText);
136     } catch (e) {
137       throw new Error('When loading deps, got ' +
138                       e.stack ? e.stack : e.message);
139     }
140     delete tvcm.setResourceFileName;
141     delete tvcm.addModuleStylesheet;
142     delete tvcm.addModuleRawScriptDependency;
143     delete tvcm.addModuleDependency;
144   }
145
146   var panicElement = undefined;
147   var rawPanicMessages = [];
148   function showPanicElementIfNeeded() {
149     if (panicElement)
150       return;
151
152     var panicOverlay = document.createElement('div');
153     panicOverlay.style.backgroundColor = 'white';
154     panicOverlay.style.border = '3px solid red';
155     panicOverlay.style.boxSizing = 'border-box';
156     panicOverlay.style.color = 'black';
157     panicOverlay.style.display = '-webkit-flex';
158     panicOverlay.style.height = '100%';
159     panicOverlay.style.left = 0;
160     panicOverlay.style.padding = '8px';
161     panicOverlay.style.position = 'fixed';
162     panicOverlay.style.top = 0;
163     panicOverlay.style.webkitFlexDirection = 'column';
164     panicOverlay.style.width = '100%';
165
166     panicElement = document.createElement('div');
167     panicElement.style.webkitFlex = '1 1 auto';
168     panicElement.style.overflow = 'auto';
169     panicOverlay.appendChild(panicElement);
170
171     if (!document.body) {
172       setTimeout(function() {
173         document.body.appendChild(panicOverlay);
174       }, 150);
175     } else {
176       document.body.appendChild(panicOverlay);
177     }
178   }
179
180   function showPanic(panicTitle, panicDetails) {
181     showPanicElementIfNeeded();
182     var panicMessageEl = document.createElement('div');
183     panicMessageEl.innerHTML =
184         '<h2 id="message"></h2>' +
185         '<pre id="details"></pre>';
186     panicMessageEl.querySelector('#message').textContent = panicTitle;
187     panicMessageEl.querySelector('#details').textContent = panicDetails;
188     panicElement.appendChild(panicMessageEl);
189
190     rawPanicMessages.push({
191       title: panicTitle,
192       details: panicDetails
193     });
194   }
195
196   function hasPanic() {
197     return rawPanicMessages.length !== 0;
198   }
199   function getPanicText() {
200     return rawPanicMessages.map(function(msg) {
201       return msg.title;
202     }).join(', ');
203   }
204
205   // TODO(dsinclair): Remove this when HTML imports land as the templates
206   // will be pulled in by the requireTemplate calls.
207   var templatesLoaded_ = false;
208   function ensureTemplatesLoaded() {
209     if (templatesLoaded_ || window.FLATTENED)
210       return;
211     templatesLoaded_ = true;
212
213     var req = new XMLHttpRequest();
214     req.open('GET', '/tvcm/all_templates.html', false);
215     req.send(null);
216
217     var elem = document.createElement('div');
218     elem.innerHTML = req.responseText;
219     while (elem.hasChildNodes())
220       document.head.appendChild(elem.removeChild(elem.firstChild));
221   }
222
223   var moduleLoadStatus = {};
224   var rawScriptLoadStatus = {};
225   function require(modules, opt_indentLevel) {
226     var indentLevel = opt_indentLevel || 0;
227     var dependentModules = modules;
228     if (!(modules instanceof Array))
229       dependentModules = [modules];
230
231     ensureDepsLoaded();
232     ensureTemplatesLoaded();
233
234     dependentModules.forEach(function(module) {
235       requireModule(module, indentLevel);
236     });
237   }
238
239   var modulesWaiting = [];
240   function requireModule(dependentModuleName, indentLevel) {
241     if (dependentModuleName == 'tvcm')
242       return;
243
244     if (window.FLATTENED) {
245       if (!window.FLATTENED[dependentModuleName]) {
246         throw new Error('Somehow, module ' + dependentModuleName +
247                         ' didn\'t get stored in the flattened js file! ' +
248                         'You have likely found a tvcm bug.');
249       }
250       return;
251     }
252
253     if (moduleLoadStatus[dependentModuleName] == 'APPENDED')
254       return;
255
256     if (moduleLoadStatus[dependentModuleName] == 'RESOLVING')
257       return;
258
259     mLog('require(' + dependentModuleName + ')', indentLevel);
260     moduleLoadStatus[dependentModuleName] = 'RESOLVING';
261     requireDependencies(dependentModuleName, indentLevel);
262
263     if (resourceFileNames[dependentModuleName] === undefined)
264       throw new Error('Not sure what filename is for ' + dependentModuleName);
265     loadScript(resourceFileNames[dependentModuleName]);
266     moduleLoadStatus[name] = 'APPENDED';
267   }
268
269   function requireDependencies(dependentModuleName, indentLevel) {
270     // Load the module's dependent scripts after.
271     var dependentModules = moduleDependencies[dependentModuleName] || [];
272     require(dependentModules, indentLevel + 1);
273
274     // Load the module stylesheet first.
275     var stylesheets = moduleStylesheets[dependentModuleName] || [];
276     for (var i = 0; i < stylesheets.length; i++)
277       requireStylesheet(stylesheets[i]);
278
279     // Load the module raw scripts next
280     var rawScripts = moduleRawScripts[dependentModuleName] || [];
281     for (var i = 0; i < rawScripts.length; i++) {
282       var rawScriptFilename = rawScripts[i];
283       if (rawScriptLoadStatus[rawScriptFilename])
284         continue;
285
286       loadScript(rawScriptFilename);
287       mLog('load(' + rawScriptFilename + ')', indentLevel);
288       rawScriptLoadStatus[rawScriptFilename] = 'APPENDED';
289     }
290   }
291
292   function loadScript(path) {
293     var scriptEl = document.createElement('script');
294     scriptEl.src = '/' + path;
295     scriptEl.type = 'text/javascript';
296     scriptEl.defer = true;
297     scriptEl.async = false;
298     tvcm.doc.head.appendChild(scriptEl);
299   }
300
301   /**
302    * Adds a dependency on a raw javascript file, e.g. a third party
303    * library.
304    * @param {String} rawScriptFilename The path to the script file, relative to
305    * the calling file. rawScriptFilename must be in the data path used by the
306    * calling project, typically a third_party directory.
307    */
308   function requireRawScript(relativeRawScriptPath) {
309     if (window.FLATTENED_RAW_SCRIPTS) {
310       if (!window.FLATTENED_RAW_SCRIPTS[relativeRawScriptPath]) {
311         throw new Error('Somehow, ' + relativeRawScriptPath +
312             ' didn\'t get stored in the flattened js file! ' +
313             'You have probably found a tvcm bug.');
314       }
315       return;
316     }
317
318     if (rawScriptLoadStatus[relativeRawScriptPath])
319       return;
320     throw new Error(
321         relativeRawScriptPath + ' should already have been loaded.' +
322         'You have probably found a tvcm bug.');
323   }
324
325   var stylesheetLoadStatus = {};
326   function requireStylesheet(dependentStylesheetName) {
327     if (window.FLATTENED)
328       return;
329
330     if (stylesheetLoadStatus[dependentStylesheetName])
331       return;
332     stylesheetLoadStatus[dependentStylesheetName] = true;
333
334     var localPath = dependentStylesheetName.replace(/\./g, '/') + '.css';
335     var stylesheetPath = '/' + localPath;
336
337     var linkEl = document.createElement('link');
338     linkEl.setAttribute('rel', 'stylesheet');
339     linkEl.setAttribute('href', stylesheetPath);
340     tvcm.doc.head.appendChild(linkEl);
341   }
342
343   var templateLoadStatus = {};
344   function requireTemplate(template) {
345     if (window.FLATTENED)
346       return;
347
348     if (templateLoadStatus[template])
349       return;
350     templateLoadStatus[template] = true;
351
352     var localPath = template.replace(/\./g, '/') + '.html';
353     var importPath = localPath;
354
355     var linkEl = document.createElement('link');
356     linkEl.setAttribute('rel', 'import');
357     linkEl.setAttribute('href', importPath);
358     // TODO(dsinclair): Enable when HTML imports are available.
359     //tvcm.doc.head.appendChild(linkEl);
360   }
361
362   function exportTo(namespace, fn) {
363     var obj = exportPath(namespace);
364     try {
365       var exports = fn();
366     } catch (e) {
367       console.log('While running exports for ', namespace, ':');
368       console.log(e.stack || e);
369       return;
370     }
371
372     for (var propertyName in exports) {
373       // Maybe we should check the prototype chain here? The current usage
374       // pattern is always using an object literal so we only care about own
375       // properties.
376       var propertyDescriptor = Object.getOwnPropertyDescriptor(exports,
377                                                                propertyName);
378       if (propertyDescriptor) {
379         Object.defineProperty(obj, propertyName, propertyDescriptor);
380         mLog('  +' + propertyName);
381       }
382     }
383   };
384
385   /**
386    * Initialization which must be deferred until run-time.
387    */
388   function initialize() {
389     // If 'document' isn't defined, then we must be being pre-compiled,
390     // so set a trap so that we're initialized on first access at run-time.
391     if (!global.document) {
392       var originalTVCM = tvcm;
393
394       Object.defineProperty(global, 'tvcm', {
395         get: function() {
396           Object.defineProperty(global, 'tvcm', {value: originalTVCM});
397           originalTVCM.initialize();
398           return originalTVCM;
399         },
400         configurable: true
401       });
402
403       return;
404     }
405
406     tvcm.doc = document;
407
408     tvcm.isMac = /Mac/.test(navigator.platform);
409     tvcm.isWindows = /Win/.test(navigator.platform);
410     tvcm.isChromeOS = /CrOS/.test(navigator.userAgent);
411     tvcm.isLinux = /Linux/.test(navigator.userAgent);
412     tvcm.isGTK = /GTK/.test(chrome.toolkit);
413     tvcm.isViews = /views/.test(chrome.toolkit);
414   }
415
416   return {
417     initialize: initialize,
418
419     require: require,
420     requireStylesheet: requireStylesheet,
421     requireRawScript: requireRawScript,
422     requireTemplate: requireTemplate,
423     exportTo: exportTo,
424     showPanic: showPanic,
425     hasPanic: hasPanic,
426     getPanicText: getPanicText
427   };
428 })();
429
430 tvcm.initialize();