292 lines
11 KiB
JavaScript
292 lines
11 KiB
JavaScript
/*---------------------------------------------------------
|
|
* OpenERP Web Boostrap Code
|
|
*---------------------------------------------------------*/
|
|
|
|
/**
|
|
* @name openerp
|
|
* @namespace openerp
|
|
*
|
|
* Each module can return a deferred. In that case, the module is marked as loaded
|
|
* only when the deferred is resolved, and its value is equal to the resolved value.
|
|
* The module can be rejected (unloaded). This will be logged in the console as info.
|
|
*
|
|
* logs:
|
|
* Missing dependencies:
|
|
* These modules do not appear in the page. It is possible that the
|
|
* JavaScript file is not in the page or that the module name is wrong
|
|
* Failed modules:
|
|
* A javascript error is detected
|
|
* Rejected modules:
|
|
* The module returns a rejected deferred. It (and its dependent modules)
|
|
* is not loaded.
|
|
* Rejected linked modules:
|
|
* Modules who depend on a rejected module
|
|
* Non loaded modules:
|
|
* Modules who depend on a missing or a failed module
|
|
* Debug:
|
|
* Non loaded or failed module informations for debugging
|
|
*/
|
|
|
|
(function() {
|
|
"use strict";
|
|
|
|
var jobs = [];
|
|
var factories = Object.create(null);
|
|
var job_names = [];
|
|
var job_deps = [];
|
|
var job_deferred = [];
|
|
|
|
|
|
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 flectra = window.flectra = window.flectra || {};
|
|
_.extend(flectra, {
|
|
testing: typeof QUnit === "object",
|
|
debug: debug,
|
|
remaining_jobs: jobs,
|
|
|
|
__DEBUG__: {
|
|
didLogInfo: $.Deferred(),
|
|
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);
|
|
}
|
|
});
|
|
});
|
|
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,
|
|
},
|
|
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);
|
|
});
|
|
}
|
|
|
|
if (flectra.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;
|
|
|
|
jobs.push({
|
|
name: name,
|
|
factory: factory,
|
|
deps: deps,
|
|
});
|
|
|
|
job_names.push(name);
|
|
_.each(deps, function (dep) {
|
|
job_deps.push({from:dep, to:name});
|
|
});
|
|
|
|
this.process_jobs(jobs, services);
|
|
},
|
|
log: function () {
|
|
var missing = [];
|
|
var failed = [];
|
|
|
|
if (jobs.length) {
|
|
var debug_jobs = {};
|
|
var rejected = [];
|
|
var rejected_linked = [];
|
|
var job;
|
|
var jobdep;
|
|
|
|
for (var k=0; k<jobs.length; k++) {
|
|
debug_jobs[jobs[k].name] = job = {
|
|
dependencies: jobs[k].deps,
|
|
dependents: flectra.__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 = flectra.__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 = [];
|
|
}
|
|
job.missing.push(deps[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
missing = flectra.__DEBUG__.get_missing_jobs();
|
|
failed = flectra.__DEBUG__.get_failed_jobs();
|
|
var unloaded = _.filter(debug_jobs, function (job) { return job.missing; });
|
|
|
|
if (flectra.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 (flectra.debug && !_.isEmpty(debug_jobs)) {
|
|
log('Debug: ', debug_jobs);
|
|
}
|
|
}
|
|
}
|
|
flectra.__DEBUG__.js_modules = {
|
|
missing: missing,
|
|
failed: _.pluck(failed, 'name'),
|
|
};
|
|
flectra.__DEBUG__.didLogInfo.resolve();
|
|
},
|
|
process_jobs: function (jobs, services) {
|
|
var job;
|
|
|
|
function process_job (job) {
|
|
var require = make_require(job);
|
|
|
|
var job_exec;
|
|
var def = $.Deferred();
|
|
try {
|
|
job_exec = job.factory.call(null, require);
|
|
jobs.splice(jobs.indexOf(job), 1);
|
|
job_deferred.push(def);
|
|
} catch (e) {
|
|
job.error = e;
|
|
}
|
|
if (!job.error) {
|
|
$.when(job_exec).then(
|
|
function (data) {
|
|
services[job.name] = data;
|
|
def.resolve();
|
|
flectra.process_jobs(jobs, services);
|
|
}, function (e) {
|
|
job.rejected = e || true;
|
|
jobs.push(job);
|
|
def.resolve();
|
|
}
|
|
);
|
|
}
|
|
}
|
|
|
|
function is_ready (job) {
|
|
return !job.error && !job.rejected && _.every(job.factory.deps, function (name) { return name in services; });
|
|
}
|
|
|
|
function make_require (job) {
|
|
var deps = _.pick(services, job.deps);
|
|
|
|
function require (name) {
|
|
if (!(name in deps)) {
|
|
console.error('Undefined dependency: ', name);
|
|
} else {
|
|
require.__require_calls++;
|
|
}
|
|
return deps[name];
|
|
}
|
|
|
|
require.__require_calls = 0;
|
|
return require;
|
|
}
|
|
|
|
while (jobs.length && (job = _.find(jobs, is_ready))) {
|
|
process_job(job);
|
|
}
|
|
|
|
return services;
|
|
}
|
|
});
|
|
|
|
|
|
// automatically log errors detected when loading modules
|
|
var log_when_loaded = function () {
|
|
_.delay(function () {
|
|
var len = job_deferred.length;
|
|
$.when.apply($, job_deferred).then(function () {
|
|
if (len === job_deferred.length) {
|
|
flectra.log();
|
|
} else {
|
|
log_when_loaded();
|
|
}
|
|
});
|
|
}, 4000);
|
|
};
|
|
$(log_when_loaded);
|
|
|
|
})();
|