summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js/boot.js
diff options
context:
space:
mode:
authorqsm-odoo <qsm@odoo.com>2019-03-29 10:59:30 +0000
committerqsm-odoo <qsm@odoo.com>2019-06-04 07:57:20 +0000
commit8132884840b1bee4303b5a4d2ffc12918a259979 (patch)
tree2c8095dfdc8f9ed53ca854102b17a915ea19cf31 /addons/web/static/src/js/boot.js
parentbddac4b602e1c8884c740004fa77f511a3a169e9 (diff)
[REF] web: make boot.js independent of libraries
This is a first step towards lazy loading the majority of the JS: the boot JS code should not depend on any libraries (underscore.js and jQuery). Even without lazy loading in mind, this would be a nice-to-have. Part of https://github.com/odoo/odoo/pull/32181 task-1961045
Diffstat (limited to 'addons/web/static/src/js/boot.js')
-rw-r--r--addons/web/static/src/js/boot.js490
1 files changed, 265 insertions, 225 deletions
diff --git a/addons/web/static/src/js/boot.js b/addons/web/static/src/js/boot.js
index cf1cbaba180..8498bf598cf 100644
--- a/addons/web/static/src/js/boot.js
+++ b/addons/web/static/src/js/boot.js
@@ -1,10 +1,7 @@
-/*---------------------------------------------------------
- * OpenERP Web Boostrap Code
- *---------------------------------------------------------*/
-
/**
- * @name openerp
- * @namespace openerp
+ *------------------------------------------------------------------------------
+ * Odoo Web Boostrap Code
+ *------------------------------------------------------------------------------
*
* Each module can return a promise. In that case, the module is marked as loaded
* only when the promise is resolved, and its value is equal to the resolved value.
@@ -26,274 +23,317 @@
* Debug:
* Non loaded or failed module informations for debugging
*/
-
(function () {
"use strict";
+ var jobUID = Date.now();
+
var jobs = [];
var factories = Object.create(null);
- var job_names = [];
- var job_deps = [];
- var job_promise = [];
-
+ var jobDeps = [];
+ var jobPromises = [];
var services = Object.create({});
var commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg;
var cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g;
- var debug = ($.deparam($.param.querystring()).debug !== undefined);
+ var debug = /\bdebug\b/.test(window.location.search);
+
+ if (!window.odoo) {
+ window.odoo = {};
+ }
+ var odoo = window.odoo;
- var odoo = window.odoo = window.odoo || {};
var didLogInfoResolve;
var didLogInfoPromise = new Promise(function (resolve) {
didLogInfoResolve = resolve;
});
- _.extend(odoo, {
- testing: typeof QUnit === "object",
- debug: debug,
- remaining_jobs: jobs,
-
- __DEBUG__: {
- didLogInfo: didLogInfoPromise,
- get_dependencies: function (name, transitive) {
- var deps = name instanceof Array ? name : [name],
- changed;
- do {
- changed = false;
- _.each(job_deps, function (dep) {
- if (_.contains(deps, dep.to) && (!_.contains(deps, dep.from))) {
- deps.push(dep.from);
- changed = true;
- }
- });
- } while (changed && transitive);
- return deps;
- },
- get_dependents: function (name) {
- return _.pluck(_.where(job_deps, { from: name }), 'to');
- },
- get_waited_jobs: function () {
- return _.uniq(_.map(jobs, function (job) { return job.name; }));
- },
- get_missing_jobs: function () {
- var self = this;
- var waited = this.get_waited_jobs();
- var missing = [];
- _.each(waited, function (job) {
- _.each(self.get_dependencies(job), function (job) {
- if (!(job in self.services)) {
- missing.push(job);
- }
- });
+ odoo.testing = typeof QUnit === 'object';
+ odoo.debug = debug;
+ odoo.remainingJobs = jobs;
+ odoo.__DEBUG__ = {
+ didLogInfo: didLogInfoPromise,
+ getDependencies: function (name, transitive) {
+ var deps = name instanceof Array ? name : [name];
+ var changed;
+ do {
+ changed = false;
+ jobDeps.forEach(function (dep) {
+ if (deps.indexOf(dep.to) >= 0 && deps.indexOf(dep.from) < 0) {
+ deps.push(dep.from);
+ changed = true;
+ }
});
- return _.filter(_.difference(_.uniq(missing), waited), function (job) { return !job.error; });
- },
- get_failed_jobs: function () {
- return _.filter(jobs, function (job) { return !!job.error; });
- },
- factories: factories,
- services: services,
+ } while (changed && transitive);
+ return deps;
},
- define: function () {
- var args = Array.prototype.slice.call(arguments);
- var name = typeof args[0] === 'string' ? args.shift() : _.uniqueId('__job');
- var factory = args[args.length - 1];
- var deps;
- if (args[0] instanceof Array) {
- deps = args[0];
- } else {
- deps = [];
- factory.toString()
- .replace(commentRegExp, '')
- .replace(cjsRequireRegExp, function (match, dep) {
- deps.push(dep);
- });
- }
+ getDependents: function (name) {
+ return jobDeps.filter(function (dep) {
+ return dep.from === name;
+ }).map(function (dep) {
+ return dep.to;
+ });
+ },
+ getWaitedJobs: function () {
+ return jobs.map(function (job) {
+ return job.name;
+ }).filter(function (item, index, self) { // uniq
+ return self.indexOf(item) === index;
+ });
+ },
+ getMissingJobs: function () {
+ var self = this;
+ var waited = this.getWaitedJobs();
+ var missing = [];
+ waited.forEach(function (job) {
+ self.getDependencies(job).forEach(function (job) {
+ if (!(job in self.services)) {
+ missing.push(job);
+ }
+ });
+ });
+ return missing.filter(function (item, index, self) {
+ return self.indexOf(item) === index;
+ }).filter(function (item) {
+ return waited.indexOf(item) < 0;
+ }).filter(function (job) {
+ return !job.error;
+ });
+ },
+ getFailedJobs: function () {
+ return jobs.filter(function (job) {
+ return !!job.error;
+ });
+ },
+ factories: factories,
+ services: services,
+ };
+ odoo.define = function () {
+ var args = Array.prototype.slice.call(arguments);
+ var name = typeof args[0] === 'string' ? args.shift() : ('__odoo_job' + (jobUID++));
+ var factory = args[args.length - 1];
+ var deps;
+ if (args[0] instanceof Array) {
+ deps = args[0];
+ } else {
+ deps = [];
+ factory.toString()
+ .replace(commentRegExp, '')
+ .replace(cjsRequireRegExp, function (match, dep) {
+ deps.push(dep);
+ });
+ }
- if (odoo.debug) {
- if (!(deps instanceof Array)) {
- throw new Error('Dependencies should be defined by an array', deps);
- }
- if (typeof factory !== 'function') {
- throw new Error('Factory should be defined by a function', factory);
- }
- if (typeof name !== 'string') {
- throw new Error("Invalid name definition (should be a string", name);
- }
- if (name in factories) {
- throw new Error("Service " + name + " already defined");
- }
+ if (odoo.debug) {
+ if (!(deps instanceof Array)) {
+ throw new Error('Dependencies should be defined by an array', deps);
}
+ if (typeof factory !== 'function') {
+ throw new Error('Factory should be defined by a function', factory);
+ }
+ if (typeof name !== 'string') {
+ throw new Error("Invalid name definition (should be a string", name);
+ }
+ if (name in factories) {
+ throw new Error("Service " + name + " already defined");
+ }
+ }
- factory.deps = deps;
- factories[name] = factory;
+ factory.deps = deps;
+ factories[name] = factory;
- jobs.push({
- name: name,
- factory: factory,
- deps: deps,
- });
+ jobs.push({
+ name: name,
+ factory: factory,
+ deps: deps,
+ });
- job_names.push(name);
- _.each(deps, function (dep) {
- job_deps.push({ from: dep, to: name });
- });
+ deps.forEach(function (dep) {
+ jobDeps.push({from: dep, to: name});
+ });
- this.process_jobs(jobs, services);
- },
- log: function () {
- var missing = [];
- var failed = [];
+ this.processJobs(jobs, services);
+ };
+ odoo.log = function () {
+ var missing = [];
+ var failed = [];
- if (jobs.length) {
- var debug_jobs = {};
- var rejected = [];
- var rejected_linked = [];
- var job;
- var jobdep;
+ if (jobs.length) {
+ var debugJobs = {};
+ var rejected = [];
+ var rejectedLinked = [];
+ var job;
+ var jobdep;
- for (var k = 0; k < jobs.length; k++) {
- debug_jobs[jobs[k].name] = job = {
- dependencies: jobs[k].deps,
- dependents: odoo.__DEBUG__.get_dependents(jobs[k].name),
- name: jobs[k].name
- };
- if (jobs[k].error) {
- job.error = jobs[k].error;
- }
- if (jobs[k].rejected) {
- job.rejected = jobs[k].rejected;
- rejected.push(job.name);
- }
- var deps = odoo.__DEBUG__.get_dependencies(job.name);
- for (var i = 0; i < deps.length; i++) {
- if (job.name !== deps[i] && !(deps[i] in services)) {
- jobdep = debug_jobs[deps[i]] || (deps[i] in factories && _.find(jobs, function (job) { return job.name === deps[i]; }));
- if (jobdep && jobdep.rejected) {
- if (!job.rejected) {
- job.rejected = [];
- rejected_linked.push(job.name);
- }
- job.rejected.push(deps[i]);
- } else {
- if (!job.missing) {
- job.missing = [];
+ for (var k = 0; k < jobs.length; k++) {
+ debugJobs[jobs[k].name] = job = {
+ dependencies: jobs[k].deps,
+ dependents: odoo.__DEBUG__.getDependents(jobs[k].name),
+ name: jobs[k].name
+ };
+ if (jobs[k].error) {
+ job.error = jobs[k].error;
+ }
+ if (jobs[k].rejected) {
+ job.rejected = jobs[k].rejected;
+ rejected.push(job.name);
+ }
+ var deps = odoo.__DEBUG__.getDependencies(job.name);
+ for (var i = 0; i < deps.length; i++) {
+ if (job.name !== deps[i] && !(deps[i] in services)) {
+ jobdep = debugJobs[deps[i]];
+ if (!jobdep && deps[i] in factories) {
+ for (var j = 0; j < jobs.length; j++) {
+ if (jobs[j].name === deps[i]) {
+ jobdep = jobs[j];
+ break;
}
- job.missing.push(deps[i]);
}
}
+ if (jobdep && jobdep.rejected) {
+ if (!job.rejected) {
+ job.rejected = [];
+ rejectedLinked.push(job.name);
+ }
+ job.rejected.push(deps[i]);
+ } else {
+ if (!job.missing) {
+ job.missing = [];
+ }
+ job.missing.push(deps[i]);
+ }
}
}
- missing = odoo.__DEBUG__.get_missing_jobs();
- failed = odoo.__DEBUG__.get_failed_jobs();
- var unloaded = _.filter(debug_jobs, function (job) { return job.missing; });
+ }
+ missing = odoo.__DEBUG__.getMissingJobs();
+ failed = odoo.__DEBUG__.getFailedJobs();
+ var unloaded = Object.keys(debugJobs) // Object.values is not supported
+ .map(function (key) {
+ return debugJobs[key];
+ }).filter(function (job) {
+ return job.missing;
+ });
- if (odoo.debug || !_.isEmpty(failed) || !_.isEmpty(unloaded)) {
- var log = console[_.isEmpty(failed) || _.isEmpty(unloaded) ? 'info' : 'error'].bind(console);
- log((_.isEmpty(failed) ? (_.isEmpty(unloaded) ? 'info' : 'warning') : 'error') + ': Some modules could not be started');
- if (missing.length) {
- log('Missing dependencies: ', missing);
- }
- if (!_.isEmpty(failed)) {
- log('Failed modules: ', _.pluck(failed, 'name'));
- }
- if (!_.isEmpty(rejected)) {
- log('Rejected modules: ', rejected);
- }
- if (!_.isEmpty(rejected_linked)) {
- log('Rejected linked modules: ', rejected_linked);
- }
- if (!_.isEmpty(unloaded)) {
- log('Non loaded modules: ', _.pluck(unloaded, 'name'));
- }
- if (odoo.debug && !_.isEmpty(debug_jobs)) {
- log('Debug: ', debug_jobs);
- }
+ if (odoo.debug || failed.length || unloaded.length) {
+ var log = window.console[!failed.length || !unloaded.length ? 'info' : 'error'].bind(window.console);
+ log((failed.length ? 'error' : (unloaded.length ? 'warning' : 'info')) + ': Some modules could not be started');
+ if (missing.length) {
+ log('Missing dependencies: ', missing);
+ }
+ if (failed.length) {
+ log('Failed modules: ', failed.map(function (fail) {
+ return fail.name;
+ }));
+ }
+ if (rejected.length) {
+ log('Rejected modules: ', rejected);
+ }
+ if (rejectedLinked.length) {
+ log('Rejected linked modules: ', rejectedLinked);
+ }
+ if (unloaded.length) {
+ log('Non loaded modules: ', unloaded.map(function (unload) {
+ return unload.name;
+ }));
+ }
+ if (odoo.debug && Object.keys(debugJobs).length) {
+ log('Debug: ', debugJobs);
}
}
- odoo.__DEBUG__.js_modules = {
- missing: missing,
- failed: _.pluck(failed, 'name'),
- };
- didLogInfoResolve();
- },
- process_jobs: function (jobs, services) {
- var job;
+ }
+ odoo.__DEBUG__.jsModules = {
+ missing: missing,
+ failed: failed.map(function (fail) {
+ return fail.name;
+ }),
+ };
+ didLogInfoResolve();
+ };
+ odoo.processJobs = function (jobs, services) {
+ var job;
- function process_job(job) {
- var require = make_require(job);
+ function processJob(job) {
+ var require = makeRequire(job);
- var job_exec;
- var def = new Promise(function (resolve) {
- try {
- job_exec = job.factory.call(null, require);
- jobs.splice(jobs.indexOf(job), 1);
- } catch (e) {
- job.error = e;
- console.error('Error while loading ' + job.name);
- console.error(e.stack);
- }
- if (!job.error) {
- Promise.resolve(job_exec).then(
- function (data) {
- services[job.name] = data;
- resolve();
- odoo.process_jobs(jobs, services);
- }).guardedCatch(function (e) {
- job.rejected = e || true;
- jobs.push(job);
- resolve();
- }
- );
- }
- });
- job_promise.push(def);
- }
+ var jobExec;
+ var def = new Promise(function (resolve) {
+ try {
+ jobExec = job.factory.call(null, require);
+ jobs.splice(jobs.indexOf(job), 1);
+ } catch (e) {
+ job.error = e;
+ console.error('Error while loading ' + job.name);
+ console.error(e.stack);
+ }
+ if (!job.error) {
+ Promise.resolve(jobExec).then(
+ function (data) {
+ services[job.name] = data;
+ resolve();
+ odoo.processJobs(jobs, services);
+ }).guardedCatch(function (e) {
+ job.rejected = e || true;
+ jobs.push(job);
+ resolve();
+ }
+ );
+ }
+ });
+ jobPromises.push(def);
+ }
- function is_ready(job) {
- return !job.error && !job.rejected && _.every(job.factory.deps, function (name) { return name in services; });
- }
+ function isReady(job) {
+ return !job.error && !job.rejected && job.factory.deps.every(function (name) {
+ return name in services;
+ });
+ }
- function make_require(job) {
- var deps = _.pick(services, job.deps);
+ function makeRequire(job) {
+ var deps = {};
+ Object.keys(services).filter(function (item) {
+ return job.deps.indexOf(item) >= 0;
+ }).forEach(function (key) {
+ deps[key] = services[key];
+ });
- function require(name) {
- if (!(name in deps)) {
- console.error('Undefined dependency: ', name);
- } else {
- require.__require_calls++;
- }
- return deps[name];
+ return function require(name) {
+ if (!(name in deps)) {
+ console.error('Undefined dependency: ', name);
}
+ return deps[name];
+ };
+ }
- require.__require_calls = 0;
- return require;
+ while (jobs.length) {
+ job = undefined;
+ for (var i = 0; i < jobs.length; i++) {
+ if (isReady(jobs[i])) {
+ job = jobs[i];
+ break;
+ }
}
-
- while (jobs.length && (job = _.find(jobs, is_ready))) {
- process_job(job);
+ if (!job) {
+ break;
}
-
- return services;
+ processJob(job);
}
- });
+ return services;
+ };
- // automatically log errors detected when loading modules
- var log_when_loaded = function () {
- _.delay(function () {
- var len = job_promise.length;
- Promise.all(job_promise).then(function () {
- if (len === job_promise.length) {
+ // Automatically log errors detected when loading modules
+ window.addEventListener('load', function logWhenLoaded() {
+ setTimeout(function () {
+ var len = jobPromises.length;
+ Promise.all(jobPromises).then(function () {
+ if (len === jobPromises.length) {
odoo.log();
} else {
- log_when_loaded();
+ logWhenLoaded();
}
});
- }, 6000);
- };
- $(log_when_loaded);
-
+ }, 9999);
+ });
})();