Commit b9418fe5 authored by Romain Courteaud's avatar Romain Courteaud

[erp5_xhtml_style/erp5_web_renderjs_ui] Update renderJS 0.16.2

parent 15da544e
...@@ -670,21 +670,22 @@ if (typeof document.contains !== 'function') { ...@@ -670,21 +670,22 @@ if (typeof document.contains !== 'function') {
/* /*
* renderJs - Generic Gadget library renderer. * renderJs - Generic Gadget library renderer.
* http://www.renderjs.org/documentation * https://renderjs.nexedi.com/
*/ */
(function (document, window, RSVP, DOMParser, Channel, MutationObserver, (function wrapRenderJS(document, window, RSVP, DOMParser, Channel,
Node, FileReader, Blob, navigator, Event, URL) { MutationObserver, Node, FileReader, Blob, navigator,
Event, URL) {
"use strict"; "use strict";
function readBlobAsDataURL(blob) { function readBlobAsDataURL(blob) {
var fr = new FileReader(); var fr = new FileReader();
return new RSVP.Promise(function (resolve, reject) { return new RSVP.Promise(function waitFormDataURLRead(resolve, reject) {
fr.addEventListener("load", function (evt) { fr.addEventListener("load", function handleDataURLRead(evt) {
resolve(evt.target.result); resolve(evt.target.result);
}); });
fr.addEventListener("error", reject); fr.addEventListener("error", reject);
fr.readAsDataURL(blob); fr.readAsDataURL(blob);
}, function () { }, function cancelReadBlobAsDataURL() {
fr.abort(); fr.abort();
}); });
} }
...@@ -717,7 +718,7 @@ if (typeof document.contains !== 'function') { ...@@ -717,7 +718,7 @@ if (typeof document.contains !== 'function') {
} }
function itsANonResolvableTrap(resolve, reject) { function itsANonResolvableTrap(resolve, reject) {
var result; var result;
handle_event_callback = function (evt) { handle_event_callback = function handleEventCallback(evt) {
if (prevent_default) { if (prevent_default) {
evt.stopPropagation(); evt.stopPropagation();
evt.preventDefault(); evt.preventDefault();
...@@ -733,10 +734,10 @@ if (typeof document.contains !== 'function') { ...@@ -733,10 +734,10 @@ if (typeof document.contains !== 'function') {
callback_promise = result; callback_promise = result;
new RSVP.Queue() new RSVP.Queue()
.push(function () { .push(function waitForEventCallbackResult() {
return result; return result;
}) })
.push(undefined, function (error) { .push(undefined, function handleEventCallbackError(error) {
if (!(error instanceof RSVP.CancellationError)) { if (!(error instanceof RSVP.CancellationError)) {
canceller(); canceller();
reject(error); reject(error);
...@@ -814,11 +815,11 @@ if (typeof document.contains !== 'function') { ...@@ -814,11 +815,11 @@ if (typeof document.contains !== 'function') {
error_list = [], error_list = [],
all_dependency_loaded_deferred; all_dependency_loaded_deferred;
window.addEventListener('error', function (error) { window.addEventListener('error', function handleGlobalError(error) {
error_list.push(error); error_list.push(error);
}); });
window.addEventListener('beforeunload', function () { window.addEventListener('beforeunload', function handleBeforeUnload() {
// XXX If another listener cancel the page unload, // XXX If another listener cancel the page unload,
// it will not restore renderJS crash report // it will not restore renderJS crash report
is_page_unloaded = true; is_page_unloaded = true;
...@@ -961,12 +962,11 @@ if (typeof document.contains !== 'function') { ...@@ -961,12 +962,11 @@ if (typeof document.contains !== 'function') {
ResolvedMonitorError.prototype = new Error(); ResolvedMonitorError.prototype = new Error();
ResolvedMonitorError.prototype.constructor = ResolvedMonitorError; ResolvedMonitorError.prototype.constructor = ResolvedMonitorError;
Monitor = function () { Monitor = function createMonitor() {
var monitor = this, var monitor = this,
promise_list = [], promise_list = [],
promise, promise,
reject, reject,
notify,
resolved; resolved;
if (!(this instanceof Monitor)) { if (!(this instanceof Monitor)) {
...@@ -983,8 +983,8 @@ if (typeof document.contains !== 'function') { ...@@ -983,8 +983,8 @@ if (typeof document.contains !== 'function') {
promise_list = []; promise_list = [];
} }
promise = new RSVP.Promise(function (done, fail, progress) { promise = new RSVP.Promise(function promiseMonitor(done, fail) {
reject = function (rejectedReason) { reject = function rejectMonitor(rejectedReason) {
if (resolved) { if (resolved) {
return; return;
} }
...@@ -994,36 +994,31 @@ if (typeof document.contains !== 'function') { ...@@ -994,36 +994,31 @@ if (typeof document.contains !== 'function') {
canceller(); canceller();
return fail(rejectedReason); return fail(rejectedReason);
}; };
notify = progress;
}, canceller); }, canceller);
monitor.cancel = function () { monitor.cancel = function cancelMonitor() {
if (resolved) { if (resolved) {
return; return;
} }
resolved = true; resolved = true;
promise.cancel(); promise.cancel();
promise.fail(function (rejectedReason) { promise.fail(function rejectMonitorPromise(rejectedReason) {
monitor.isRejected = true; monitor.isRejected = true;
monitor.rejectedReason = rejectedReason; monitor.rejectedReason = rejectedReason;
}); });
}; };
monitor.then = function () { monitor.then = promise.then.bind(promise);
return promise.then.apply(promise, arguments); monitor.fail = promise.fail.bind(promise);
};
monitor.fail = function () {
return promise.fail.apply(promise, arguments);
};
monitor.monitor = function (promise_to_monitor) { monitor.monitor = function startMonitor(promise_to_monitor) {
if (resolved) { if (resolved) {
throw new ResolvedMonitorError(); throw new ResolvedMonitorError();
} }
var queue = new RSVP.Queue() var queue = new RSVP.Queue()
.push(function () { .push(function waitForPromiseToMonitor() {
return promise_to_monitor; return promise_to_monitor;
}) })
.push(function (fulfillmentValue) { .push(function handlePromiseToMonitorSuccess(fulfillmentValue) {
// Promise to monitor is fullfilled, remove it from the list // Promise to monitor is fullfilled, remove it from the list
var len = promise_list.length, var len = promise_list.length,
sub_promise_to_monitor, sub_promise_to_monitor,
...@@ -1037,7 +1032,7 @@ if (typeof document.contains !== 'function') { ...@@ -1037,7 +1032,7 @@ if (typeof document.contains !== 'function') {
} }
} }
promise_list = new_promise_list; promise_list = new_promise_list;
}, function (rejectedReason) { }, function handlePromiseToMonitorError(rejectedReason) {
if (rejectedReason instanceof RSVP.CancellationError) { if (rejectedReason instanceof RSVP.CancellationError) {
if (!(promise_to_monitor.isFulfilled && if (!(promise_to_monitor.isFulfilled &&
promise_to_monitor.isRejected)) { promise_to_monitor.isRejected)) {
...@@ -1047,9 +1042,6 @@ if (typeof document.contains !== 'function') { ...@@ -1047,9 +1042,6 @@ if (typeof document.contains !== 'function') {
} }
reject(rejectedReason); reject(rejectedReason);
throw rejectedReason; throw rejectedReason;
}, function (notificationValue) {
notify(notificationValue);
return notificationValue;
}); });
promise_list.push(queue); promise_list.push(queue);
...@@ -1076,7 +1068,7 @@ if (typeof document.contains !== 'function') { ...@@ -1076,7 +1068,7 @@ if (typeof document.contains !== 'function') {
RenderJSGadget.prototype.__required_css_list = []; RenderJSGadget.prototype.__required_css_list = [];
RenderJSGadget.prototype.__required_js_list = []; RenderJSGadget.prototype.__required_js_list = [];
function createMonitor(g) { function createGadgetMonitor(g) {
if (g.__monitor !== undefined) { if (g.__monitor !== undefined) {
g.__monitor.cancel(); g.__monitor.cancel();
} }
...@@ -1084,19 +1076,17 @@ if (typeof document.contains !== 'function') { ...@@ -1084,19 +1076,17 @@ if (typeof document.contains !== 'function') {
g.__job_dict = {}; g.__job_dict = {};
g.__job_list = []; g.__job_list = [];
g.__job_triggered = false; g.__job_triggered = false;
g.__monitor.fail(function (error) { g.__monitor.fail(function handleGadgetMonitorError(error) {
if (!(error instanceof RSVP.CancellationError)) { if (!(error instanceof RSVP.CancellationError)) {
return g.aq_reportServiceError(error); return g.aq_reportServiceError(error);
} }
}).fail(function (error) { // Crash the application if the acquisition generates an error.
// Crash the application if the acquisition generates an error. }).fail(letsCrash);
return letsCrash(error);
});
} }
function clearGadgetInternalParameters() { function clearGadgetInternalParameters() {
this.__sub_gadget_dict = {}; this.__sub_gadget_dict = {};
createMonitor(this); createGadgetMonitor(this);
} }
function loadSubGadgetDOMDeclaration() { function loadSubGadgetDOMDeclaration() {
...@@ -1110,7 +1100,7 @@ if (typeof document.contains !== 'function') { ...@@ -1110,7 +1100,7 @@ if (typeof document.contains !== 'function') {
context = this; context = this;
function prepareReportGadgetDeclarationError(scope) { function prepareReportGadgetDeclarationError(scope) {
return function (error) { return function reportGadgetDeclarationError(error) {
var aq_dict = context.__acquired_method_dict || {}, var aq_dict = context.__acquired_method_dict || {},
method_name = 'reportGadgetDeclarationError'; method_name = 'reportGadgetDeclarationError';
if (aq_dict.hasOwnProperty(method_name)) { if (aq_dict.hasOwnProperty(method_name)) {
...@@ -1143,58 +1133,56 @@ if (typeof document.contains !== 'function') { ...@@ -1143,58 +1133,56 @@ if (typeof document.contains !== 'function') {
RenderJSGadget.__ready_list = [clearGadgetInternalParameters, RenderJSGadget.__ready_list = [clearGadgetInternalParameters,
loadSubGadgetDOMDeclaration]; loadSubGadgetDOMDeclaration];
RenderJSGadget.ready = function (callback) { RenderJSGadget.ready = function ready(callback) {
this.__ready_list.push(callback); this.__ready_list.push(callback);
return this; return this;
}; };
RenderJSGadget.setState = function (state_dict) { RenderJSGadget.setState = function setState(state_dict) {
var json_state = JSON.stringify(state_dict); var json_state = JSON.stringify(state_dict);
this.__ready_list.unshift(function () { this.__ready_list.unshift(function setStateDefaultValue() {
this.state = JSON.parse(json_state); this.state = JSON.parse(json_state);
}); });
return this; return this;
}; };
RenderJSGadget.onStateChange = function (callback) { RenderJSGadget.onStateChange = function onStateChange(callback) {
this.prototype.__state_change_callback = callback; this.prototype.__state_change_callback = callback;
return this; return this;
}; };
RenderJSGadget.__service_list = []; RenderJSGadget.__service_list = [];
RenderJSGadget.declareService = function (callback) { RenderJSGadget.declareService = function declareService(callback) {
this.__service_list.push(callback); this.__service_list.push(callback);
return this; return this;
}; };
RenderJSGadget.onEvent = function (type, callback, use_capture, RenderJSGadget.onEvent = function onEvent(type, callback, use_capture,
prevent_default) { prevent_default) {
this.__service_list.push(function () { this.__service_list.push(function startLoopEventListenerService() {
return loopEventListener(this.element, type, use_capture, return loopEventListener(this.element, type, use_capture,
callback.bind(this), prevent_default); callback.bind(this), prevent_default);
}); });
return this; return this;
}; };
RenderJSGadget.onLoop = function (callback, delay) { RenderJSGadget.onLoop = function onLoop(callback, delay) {
if (delay === undefined) { if (delay === undefined) {
delay = 0; delay = 0;
} }
this.__service_list.push(function () { this.__service_list.push(function handleServiceCallback() {
var queue_loop = new RSVP.Queue(), var queue_loop = new RSVP.Queue(),
context = this, context = this,
wait = function () { wait = function waitForLoopIteration() {
queue_loop queue_loop
.push(function () { .push(function waitNextOnLoopDelay() {
return RSVP.delay(delay); return RSVP.delay(delay);
}) })
.push(function () { .push(function waitNextOnLoopAnimationFrame() {
// Only loop when the app has the focus // Only loop when the app has the focus
return promiseAnimationFrame(); return promiseAnimationFrame();
}) })
.push(function () { .push(function executeOnLoopCallback() {
return callback.apply(context, []); return callback.apply(context, []);
}) })
.push(function () { .push(wait);
wait();
});
}; };
wait(); wait();
return queue_loop; return queue_loop;
...@@ -1204,7 +1192,7 @@ if (typeof document.contains !== 'function') { ...@@ -1204,7 +1192,7 @@ if (typeof document.contains !== 'function') {
function runJob(gadget, name, callback, argument_list) { function runJob(gadget, name, callback, argument_list) {
var job_promise = new RSVP.Queue() var job_promise = new RSVP.Queue()
.push(function () { .push(function waitForJobCallback() {
return callback.apply(gadget, argument_list); return callback.apply(gadget, argument_list);
}); });
if (gadget.__job_dict.hasOwnProperty(name)) { if (gadget.__job_dict.hasOwnProperty(name)) {
...@@ -1212,10 +1200,10 @@ if (typeof document.contains !== 'function') { ...@@ -1212,10 +1200,10 @@ if (typeof document.contains !== 'function') {
} }
gadget.__job_dict[name] = job_promise; gadget.__job_dict[name] = job_promise;
gadget.__monitor.monitor(new RSVP.Queue() gadget.__monitor.monitor(new RSVP.Queue()
.push(function () { .push(function waitForJobPromise() {
return job_promise; return job_promise;
}) })
.push(undefined, function (error) { .push(undefined, function handleJobError(error) {
if (!(error instanceof RSVP.CancellationError)) { if (!(error instanceof RSVP.CancellationError)) {
throw error; throw error;
} }
...@@ -1224,7 +1212,7 @@ if (typeof document.contains !== 'function') { ...@@ -1224,7 +1212,7 @@ if (typeof document.contains !== 'function') {
function startService(gadget) { function startService(gadget) {
gadget.__monitor.monitor(new RSVP.Queue() gadget.__monitor.monitor(new RSVP.Queue()
.push(function () { .push(function monitorAllServiceList() {
var i, var i,
service_list = gadget.constructor.__service_list, service_list = gadget.constructor.__service_list,
job_list = gadget.__job_list; job_list = gadget.__job_list;
...@@ -1245,8 +1233,8 @@ if (typeof document.contains !== 'function') { ...@@ -1245,8 +1233,8 @@ if (typeof document.contains !== 'function') {
// gadget internal method, which trigger execution // gadget internal method, which trigger execution
// of a function inside a service // of a function inside a service
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
RenderJSGadget.declareJob = function (name, callback) { RenderJSGadget.declareJob = function declareJob(name, callback) {
this.prototype[name] = function () { this.prototype[name] = function triggerJob() {
var context = this, var context = this,
argument_list = arguments; argument_list = arguments;
...@@ -1263,13 +1251,13 @@ if (typeof document.contains !== 'function') { ...@@ -1263,13 +1251,13 @@ if (typeof document.contains !== 'function') {
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// RenderJSGadget.declareMethod // RenderJSGadget.declareMethod
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
RenderJSGadget.declareMethod = function (name, callback) { RenderJSGadget.declareMethod = function declareMethod(name, callback) {
this.prototype[name] = function () { this.prototype[name] = function triggerMethod() {
var context = this, var context = this,
argument_list = arguments; argument_list = arguments;
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function waitForMethodCallback() {
return callback.apply(context, argument_list); return callback.apply(context, argument_list);
}); });
}; };
...@@ -1278,27 +1266,27 @@ if (typeof document.contains !== 'function') { ...@@ -1278,27 +1266,27 @@ if (typeof document.contains !== 'function') {
}; };
RenderJSGadget RenderJSGadget
.declareMethod('getInterfaceList', function () { .declareMethod('getInterfaceList', function getInterfaceList() {
// Returns the list of gadget prototype // Returns the list of gadget prototype
return this.__interface_list; return this.__interface_list;
}) })
.declareMethod('getRequiredCSSList', function () { .declareMethod('getRequiredCSSList', function getRequiredCSSList() {
// Returns a list of CSS required by the gadget // Returns a list of CSS required by the gadget
return this.__required_css_list; return this.__required_css_list;
}) })
.declareMethod('getRequiredJSList', function () { .declareMethod('getRequiredJSList', function getRequiredJSList() {
// Returns a list of JS required by the gadget // Returns a list of JS required by the gadget
return this.__required_js_list; return this.__required_js_list;
}) })
.declareMethod('getPath', function () { .declareMethod('getPath', function getPath() {
// Returns the path of the code of a gadget // Returns the path of the code of a gadget
return this.__path; return this.__path;
}) })
.declareMethod('getTitle', function () { .declareMethod('getTitle', function getTitle() {
// Returns the title of a gadget // Returns the title of a gadget
return this.__title; return this.__title;
}) })
.declareMethod('getElement', function () { .declareMethod('getElement', function getElement() {
// Returns the DOM Element of a gadget // Returns the DOM Element of a gadget
// XXX Kept for compatibility. Use element property directly // XXX Kept for compatibility. Use element property directly
if (this.element === undefined) { if (this.element === undefined) {
...@@ -1306,24 +1294,24 @@ if (typeof document.contains !== 'function') { ...@@ -1306,24 +1294,24 @@ if (typeof document.contains !== 'function') {
} }
return this.element; return this.element;
}) })
.declareMethod('changeState', function (state_dict) { .declareMethod('changeState', function changeState(state_dict) {
var next_onStateChange = new RSVP.Queue(), var next_onStateChange = new RSVP.Queue(),
previous_onStateCHange, previous_onStateCHange,
context = this; context = this;
if (context.hasOwnProperty('__previous_onStateChange')) { if (context.hasOwnProperty('__previous_onStateChange')) {
previous_onStateCHange = context.__previous_onStateChange; previous_onStateCHange = context.__previous_onStateChange;
next_onStateChange next_onStateChange
.push(function () { .push(function waitForPreviousStateChange() {
return previous_onStateCHange; return previous_onStateCHange;
}) })
.push(undefined, function () { .push(undefined, function handlePreviousStateChangeError() {
// Run callback even if previous failed // Run callback even if previous failed
return; return;
}); });
} }
context.__previous_onStateChange = next_onStateChange; context.__previous_onStateChange = next_onStateChange;
return next_onStateChange return next_onStateChange
.push(function () { .push(function checkStateModification() {
var key, var key,
modified = false, modified = false,
previous_cancelled = context.hasOwnProperty('__modification_dict'), previous_cancelled = context.hasOwnProperty('__modification_dict'),
...@@ -1345,10 +1333,10 @@ if (typeof document.contains !== 'function') { ...@@ -1345,10 +1333,10 @@ if (typeof document.contains !== 'function') {
if (modified && context.__state_change_callback !== undefined) { if (modified && context.__state_change_callback !== undefined) {
context.__modification_dict = modification_dict; context.__modification_dict = modification_dict;
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function waitForStateChangeCallback() {
return context.__state_change_callback(modification_dict); return context.__state_change_callback(modification_dict);
}) })
.push(function (result) { .push(function handleStateChangeSuccess(result) {
delete context.__modification_dict; delete context.__modification_dict;
return result; return result;
}); });
...@@ -1372,7 +1360,7 @@ if (typeof document.contains !== 'function') { ...@@ -1372,7 +1360,7 @@ if (typeof document.contains !== 'function') {
} }
} }
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function waitForAcquireMethod() {
// Do not specify default __acquired_method_dict on prototype // Do not specify default __acquired_method_dict on prototype
// to prevent modifying this default value (with // to prevent modifying this default value (with
// allowPublicAcquiredMethod for example) // allowPublicAcquiredMethod for example)
...@@ -1383,7 +1371,7 @@ if (typeof document.contains !== 'function') { ...@@ -1383,7 +1371,7 @@ if (typeof document.contains !== 'function') {
} }
throw new renderJS.AcquisitionError("aq_dynamic is not defined"); throw new renderJS.AcquisitionError("aq_dynamic is not defined");
}) })
.push(undefined, function (error) { .push(undefined, function handleAcquireMethodError(error) {
if (error instanceof renderJS.AcquisitionError) { if (error instanceof renderJS.AcquisitionError) {
return gadget.__aq_parent(method_name, argument_list); return gadget.__aq_parent(method_name, argument_list);
} }
...@@ -1392,12 +1380,12 @@ if (typeof document.contains !== 'function') { ...@@ -1392,12 +1380,12 @@ if (typeof document.contains !== 'function') {
} }
RenderJSGadget.declareAcquiredMethod = RenderJSGadget.declareAcquiredMethod =
function (name, method_name_to_acquire) { function declareAcquiredMethod(name, method_name_to_acquire) {
this.prototype[name] = function () { this.prototype[name] = function acquireMethod() {
var argument_list = Array.prototype.slice.call(arguments, 0), var argument_list = Array.prototype.slice.call(arguments, 0),
gadget = this; gadget = this;
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function waitForAqParent() {
return gadget.__aq_parent(method_name_to_acquire, argument_list); return gadget.__aq_parent(method_name_to_acquire, argument_list);
}); });
}; };
...@@ -1414,7 +1402,7 @@ if (typeof document.contains !== 'function') { ...@@ -1414,7 +1402,7 @@ if (typeof document.contains !== 'function') {
// RenderJSGadget.allowPublicAcquisition // RenderJSGadget.allowPublicAcquisition
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
RenderJSGadget.allowPublicAcquisition = RenderJSGadget.allowPublicAcquisition =
function (method_name, callback) { function allowPublicAcquisition(method_name, callback) {
this.prototype.__acquired_method_dict[method_name] = callback; this.prototype.__acquired_method_dict[method_name] = callback;
// Allow chain // Allow chain
...@@ -1423,10 +1411,11 @@ if (typeof document.contains !== 'function') { ...@@ -1423,10 +1411,11 @@ if (typeof document.contains !== 'function') {
// Set aq_parent on gadget_instance which call acquire on parent_gadget // Set aq_parent on gadget_instance which call acquire on parent_gadget
function setAqParent(gadget_instance, parent_gadget) { function setAqParent(gadget_instance, parent_gadget) {
gadget_instance.__aq_parent = function (method_name, argument_list) { gadget_instance.__aq_parent =
return acquire.apply(parent_gadget, [gadget_instance, method_name, function __aq_parent(method_name, argument_list) {
argument_list]); return acquire.apply(parent_gadget, [gadget_instance, method_name,
}; argument_list]);
};
} }
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
...@@ -1462,19 +1451,13 @@ if (typeof document.contains !== 'function') { ...@@ -1462,19 +1451,13 @@ if (typeof document.contains !== 'function') {
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
function privateDeclarePublicGadget(url, options, parent_gadget) { function privateDeclarePublicGadget(url, options, parent_gadget) {
return new RSVP.Queue() return renderJS.declareGadgetKlass(url)
.push(function () { // gadget loading should not be interrupted
return renderJS.declareGadgetKlass(url) // if not, gadget's definition will not be complete
// gadget loading should not be interrupted //.then will return another promise
// if not, gadget's definition will not be complete //so loading_klass_promise can't be cancel
//.then will return another promise .then(function createPrivateInstanceFromKlass(Klass) {
//so loading_klass_promise can't be cancel // Get the gadget class and instanciate it
.then(function (result) {
return result;
});
})
// Get the gadget class and instanciate it
.push(function (Klass) {
if (options.element === undefined) { if (options.element === undefined) {
options.element = document.createElement("div"); options.element = document.createElement("div");
} }
...@@ -1543,12 +1526,12 @@ if (typeof document.contains !== 'function') { ...@@ -1543,12 +1526,12 @@ if (typeof document.contains !== 'function') {
gadget_instance = new RenderJSIframeGadget(); gadget_instance = new RenderJSIframeGadget();
setAqParent(gadget_instance, parent_gadget); setAqParent(gadget_instance, parent_gadget);
iframe = document.createElement("iframe"); iframe = document.createElement("iframe");
iframe.addEventListener('error', function (error) { iframe.addEventListener('error', function handleIframeError(error) {
iframe_loading_deferred.reject(error); iframe_loading_deferred.reject(error);
}); });
iframe.addEventListener('load', function () { iframe.addEventListener('load', function handleIframeLoad() {
return RSVP.timeout(5000) return RSVP.timeout(5000)
.fail(function () { .fail(function triggerIframeTimeout() {
iframe_loading_deferred.reject( iframe_loading_deferred.reject(
new Error('Timeout while loading: ' + url) new Error('Timeout while loading: ' + url)
); );
...@@ -1573,46 +1556,51 @@ if (typeof document.contains !== 'function') { ...@@ -1573,46 +1556,51 @@ if (typeof document.contains !== 'function') {
}); });
// Create new method from the declareMethod call inside the iframe // Create new method from the declareMethod call inside the iframe
gadget_instance.__chan.bind("declareMethod", gadget_instance.__chan.bind(
function (trans, method_name) { "declareMethod",
gadget_instance[method_name] = function () { function handleChannelDeclareMethod(trans, method_name) {
gadget_instance[method_name] = function triggerChannelDeclareMethod() {
var argument_list = arguments, var argument_list = arguments,
wait_promise = new RSVP.Promise(function (resolve, reject) { wait_promise = new RSVP.Promise(
gadget_instance.__chan.call({ function handleChannelCall(resolve, reject) {
method: "methodCall", gadget_instance.__chan.call({
params: [ method: "methodCall",
method_name, params: [
Array.prototype.slice.call(argument_list, 0)], method_name,
success: resolve, Array.prototype.slice.call(argument_list, 0)],
error: reject success: resolve,
}); error: reject
}); });
}
);
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function waitForChannelCall() {
return wait_promise; return wait_promise;
}); });
}; };
return "OK"; return "OK";
}); }
);
// Wait for the iframe to be loaded before continuing // Wait for the iframe to be loaded before continuing
gadget_instance.__chan.bind("ready", function (trans) { gadget_instance.__chan.bind("ready", function handleChannelReady(trans) {
iframe_loading_deferred.resolve(gadget_instance); iframe_loading_deferred.resolve(gadget_instance);
return "OK"; return "OK";
}); });
gadget_instance.__chan.bind("failed", function (trans, params) { gadget_instance.__chan.bind("failed",
iframe_loading_deferred.reject(params); function handleChannelFail(trans, params) {
return "OK"; iframe_loading_deferred.reject(params);
}); return "OK";
gadget_instance.__chan.bind("acquire", function (trans, params) { });
gadget_instance.__aq_parent.apply(gadget_instance, params) gadget_instance.__chan.bind("acquire",
.then(function (g) { function handleChannelAcquire(trans, params) {
trans.complete(g); gadget_instance.__aq_parent.apply(gadget_instance, params)
}).fail(function (e) { .then(trans.complete)
trans.error(e.toString()); .fail(function handleChannelAcquireError(e) {
}); trans.error(e.toString());
trans.delayReturn(true); });
}); trans.delayReturn(true);
});
return iframe_loading_deferred.promise; return iframe_loading_deferred.promise;
} }
...@@ -1623,10 +1611,10 @@ if (typeof document.contains !== 'function') { ...@@ -1623,10 +1611,10 @@ if (typeof document.contains !== 'function') {
function privateDeclareDataUrlGadget(url, options, parent_gadget) { function privateDeclareDataUrlGadget(url, options, parent_gadget) {
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function waitForDataUrlAjax() {
return ajax(url); return ajax(url);
}) })
.push(function (xhr) { .push(function handleDataURLAjaxResponse(xhr) {
// Insert a "base" element, in order to resolve all relative links // Insert a "base" element, in order to resolve all relative links
// which could get broken with a data url // which could get broken with a data url
var doc = (new DOMParser()).parseFromString(xhr.responseText, var doc = (new DOMParser()).parseFromString(xhr.responseText,
...@@ -1639,7 +1627,7 @@ if (typeof document.contains !== 'function') { ...@@ -1639,7 +1627,7 @@ if (typeof document.contains !== 'function') {
{type: "text/html;charset=UTF-8"}); {type: "text/html;charset=UTF-8"});
return readBlobAsDataURL(blob); return readBlobAsDataURL(blob);
}) })
.push(function (data_url) { .push(function handleDataURL(data_url) {
return privateDeclareIframeGadget(data_url, options, parent_gadget); return privateDeclareIframeGadget(data_url, options, parent_gadget);
}); });
} }
...@@ -1648,7 +1636,7 @@ if (typeof document.contains !== 'function') { ...@@ -1648,7 +1636,7 @@ if (typeof document.contains !== 'function') {
// RenderJSGadget.declareGadget // RenderJSGadget.declareGadget
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
RenderJSGadget RenderJSGadget
.declareMethod('declareGadget', function (url, options) { .declareMethod('declareGadget', function declareGadget(url, options) {
var parent_gadget = this; var parent_gadget = this;
if (options === undefined) { if (options === undefined) {
...@@ -1662,7 +1650,7 @@ if (typeof document.contains !== 'function') { ...@@ -1662,7 +1650,7 @@ if (typeof document.contains !== 'function') {
url = renderJS.getAbsoluteURL(url, this.__path); url = renderJS.getAbsoluteURL(url, this.__path);
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function waitForPrivateDeclareGadget() {
var method; var method;
if (options.sandbox === "public") { if (options.sandbox === "public") {
method = privateDeclarePublicGadget; method = privateDeclarePublicGadget;
...@@ -1677,7 +1665,7 @@ if (typeof document.contains !== 'function') { ...@@ -1677,7 +1665,7 @@ if (typeof document.contains !== 'function') {
return method(url, options, parent_gadget); return method(url, options, parent_gadget);
}) })
// Set the HTML context // Set the HTML context
.push(function (gadget_instance) { .push(function setGadgetInstanceHTMLContext(gadget_instance) {
var i, var i,
scope, scope,
queue = new RSVP.Queue(); queue = new RSVP.Queue();
...@@ -1686,7 +1674,7 @@ if (typeof document.contains !== 'function') { ...@@ -1686,7 +1674,7 @@ if (typeof document.contains !== 'function') {
return gadget_instance; return gadget_instance;
} }
function ready_executable_wrapper(fct) { function ready_executable_wrapper(fct) {
return function () { return function executeReadyWrapper() {
return fct.call(gadget_instance, gadget_instance); return fct.call(gadget_instance, gadget_instance);
}; };
} }
...@@ -1730,13 +1718,14 @@ if (typeof document.contains !== 'function') { ...@@ -1730,13 +1718,14 @@ if (typeof document.contains !== 'function') {
return queue; return queue;
}); });
}) })
.declareMethod('getDeclaredGadget', function (gadget_scope) { .declareMethod('getDeclaredGadget',
if (!this.__sub_gadget_dict.hasOwnProperty(gadget_scope)) { function getDeclaredGadget(gadget_scope) {
throw new Error("Gadget scope '" + gadget_scope + "' is not known."); if (!this.__sub_gadget_dict.hasOwnProperty(gadget_scope)) {
} throw new Error("Gadget scope '" + gadget_scope + "' is not known.");
return this.__sub_gadget_dict[gadget_scope]; }
}) return this.__sub_gadget_dict[gadget_scope];
.declareMethod('dropGadget', function (gadget_scope) { })
.declareMethod('dropGadget', function dropGadget(gadget_scope) {
if (!this.__sub_gadget_dict.hasOwnProperty(gadget_scope)) { if (!this.__sub_gadget_dict.hasOwnProperty(gadget_scope)) {
throw new Error("Gadget scope '" + gadget_scope + "' is not known."); throw new Error("Gadget scope '" + gadget_scope + "' is not known.");
} }
...@@ -1747,7 +1736,7 @@ if (typeof document.contains !== 'function') { ...@@ -1747,7 +1736,7 @@ if (typeof document.contains !== 'function') {
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// renderJS selector // renderJS selector
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
renderJS = function (selector) { renderJS = function getLoadingGadget(selector) {
var result; var result;
if (selector === window) { if (selector === window) {
// window is the 'this' value when loading a javascript file // window is the 'this' value when loading a javascript file
...@@ -1763,7 +1752,7 @@ if (typeof document.contains !== 'function') { ...@@ -1763,7 +1752,7 @@ if (typeof document.contains !== 'function') {
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// renderJS.AcquisitionError // renderJS.AcquisitionError
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
renderJS.AcquisitionError = function (message) { renderJS.AcquisitionError = function createAcquisitionError(message) {
this.name = "AcquisitionError"; this.name = "AcquisitionError";
if ((message !== undefined) && (typeof message !== "string")) { if ((message !== undefined) && (typeof message !== "string")) {
throw new TypeError('You must pass a string.'); throw new TypeError('You must pass a string.');
...@@ -1777,7 +1766,7 @@ if (typeof document.contains !== 'function') { ...@@ -1777,7 +1766,7 @@ if (typeof document.contains !== 'function') {
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// renderJS.getAbsoluteURL // renderJS.getAbsoluteURL
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
renderJS.getAbsoluteURL = function (url, base_url) { renderJS.getAbsoluteURL = function getAbsoluteURL(url, base_url) {
if (base_url && url) { if (base_url && url) {
return new URL(url, base_url).href; return new URL(url, base_url).href;
} }
...@@ -1787,7 +1776,7 @@ if (typeof document.contains !== 'function') { ...@@ -1787,7 +1776,7 @@ if (typeof document.contains !== 'function') {
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// renderJS.declareJS // renderJS.declareJS
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
renderJS.declareJS = function (url, container, pop) { renderJS.declareJS = function declareJS(url, container, pop) {
// https://www.html5rocks.com/en/tutorials/speed/script-loading/ // https://www.html5rocks.com/en/tutorials/speed/script-loading/
// Prevent infinite recursion if loading render.js // Prevent infinite recursion if loading render.js
// more than once // more than once
...@@ -1796,28 +1785,30 @@ if (typeof document.contains !== 'function') { ...@@ -1796,28 +1785,30 @@ if (typeof document.contains !== 'function') {
result = RSVP.resolve(); result = RSVP.resolve();
} else { } else {
javascript_registration_dict[url] = null; javascript_registration_dict[url] = null;
result = new RSVP.Promise(function (resolve, reject) { result = new RSVP.Promise(
var newScript; function waitForJSLoadEvent(resolve, reject) {
newScript = document.createElement('script'); var newScript;
newScript.async = false; newScript = document.createElement('script');
newScript.type = 'text/javascript'; newScript.async = false;
newScript.onload = function () { newScript.type = 'text/javascript';
if (pop === true) { newScript.onload = function triggerJSLoaded() {
// Drop the current loading klass info used by selector if (pop === true) {
gadget_loading_klass_list.shift(); // Drop the current loading klass info used by selector
} gadget_loading_klass_list.shift();
resolve(); }
}; resolve();
newScript.onerror = function (e) { };
if (pop === true) { newScript.onerror = function triggerJSNotLoaded(e) {
// Drop the current loading klass info used by selector if (pop === true) {
gadget_loading_klass_list.shift(); // Drop the current loading klass info used by selector
} gadget_loading_klass_list.shift();
reject(e); }
}; reject(e);
newScript.src = url; };
container.appendChild(newScript); newScript.src = url;
}); container.appendChild(newScript);
}
);
} }
return result; return result;
}; };
...@@ -1825,7 +1816,7 @@ if (typeof document.contains !== 'function') { ...@@ -1825,7 +1816,7 @@ if (typeof document.contains !== 'function') {
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// renderJS.declareCSS // renderJS.declareCSS
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
renderJS.declareCSS = function (url, container) { renderJS.declareCSS = function declareCSS(url, container) {
// https://github.com/furf/jquery-getCSS/blob/master/jquery.getCSS.js // https://github.com/furf/jquery-getCSS/blob/master/jquery.getCSS.js
// No way to cleanly check if a css has been loaded // No way to cleanly check if a css has been loaded
// So, always resolve the promise... // So, always resolve the promise...
...@@ -1834,19 +1825,17 @@ if (typeof document.contains !== 'function') { ...@@ -1834,19 +1825,17 @@ if (typeof document.contains !== 'function') {
if (stylesheet_registration_dict.hasOwnProperty(url)) { if (stylesheet_registration_dict.hasOwnProperty(url)) {
result = RSVP.resolve(); result = RSVP.resolve();
} else { } else {
result = new RSVP.Promise(function (resolve, reject) { result = new RSVP.Promise(function waitForCSSLoadEvent(resolve, reject) {
var link; var link;
link = document.createElement('link'); link = document.createElement('link');
link.rel = 'stylesheet'; link.rel = 'stylesheet';
link.type = 'text/css'; link.type = 'text/css';
link.href = url; link.href = url;
link.onload = function () { link.onload = function triggerCSSLoaded() {
stylesheet_registration_dict[url] = null; stylesheet_registration_dict[url] = null;
resolve(); resolve();
}; };
link.onerror = function (e) { link.onerror = reject;
reject(e);
};
container.appendChild(link); container.appendChild(link);
}); });
} }
...@@ -1862,7 +1851,7 @@ if (typeof document.contains !== 'function') { ...@@ -1862,7 +1851,7 @@ if (typeof document.contains !== 'function') {
key, key,
parsed_html; parsed_html;
// Class inheritance // Class inheritance
tmp_constructor = function () { tmp_constructor = function createSuperKlass() {
RenderJSGadget.call(this); RenderJSGadget.call(this);
}; };
tmp_constructor.__ready_list = RenderJSGadget.__ready_list.slice(); tmp_constructor.__ready_list = RenderJSGadget.__ready_list.slice();
...@@ -1908,23 +1897,35 @@ if (typeof document.contains !== 'function') { ...@@ -1908,23 +1897,35 @@ if (typeof document.contains !== 'function') {
return tmp_constructor; return tmp_constructor;
} }
renderJS.declareGadgetKlass = function (url) { renderJS.declareGadgetKlass = function declareGadgetKlass(url) {
var tmp_constructor,
defer;
if (gadget_model_defer_dict.hasOwnProperty(url)) { if (gadget_model_defer_dict.hasOwnProperty(url)) {
// Return klass object if it already exists // Return klass object if it already exists
return gadget_model_defer_dict[url].promise; if (gadget_model_defer_dict[url].hasOwnProperty('defer_list')) {
// Klass not yet loaded.
// Add a new defer
defer = RSVP.defer();
gadget_model_defer_dict[url].defer_list.push(defer);
return defer.promise;
}
if (gadget_model_defer_dict[url].is_resolved) {
return RSVP.resolve(gadget_model_defer_dict[url].result);
}
return RSVP.reject(gadget_model_defer_dict[url].result);
} }
var tmp_constructor, gadget_model_defer_dict[url] = {
defer = RSVP.defer(); defer_list: []
};
gadget_model_defer_dict[url] = defer;
// Fetch the HTML page and parse it // Fetch the HTML page and parse it
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function waitForGadgetKlassAjax() {
return ajax(url); return ajax(url);
}) })
.push(function (result) { .push(function handleGadgetKlassAjax(result) {
tmp_constructor = parse(result, url); tmp_constructor = parse(result, url);
var fragment = document.createDocumentFragment(), var fragment = document.createDocumentFragment(),
promise_list = [], promise_list = [],
...@@ -1946,14 +1947,28 @@ if (typeof document.contains !== 'function') { ...@@ -1946,14 +1947,28 @@ if (typeof document.contains !== 'function') {
document.head.appendChild(fragment); document.head.appendChild(fragment);
return RSVP.all(promise_list); return RSVP.all(promise_list);
}) })
.push(function () { .push(function handleGadgetKlassLoadingSuccess() {
defer.resolve(tmp_constructor); var i,
len = gadget_model_defer_dict[url].defer_list.length;
for (i = 0; i < len; i += 1) {
gadget_model_defer_dict[url].defer_list[i].resolve(tmp_constructor);
}
delete gadget_model_defer_dict[url].defer_list;
gadget_model_defer_dict[url].result = tmp_constructor;
gadget_model_defer_dict[url].is_resolved = true;
return tmp_constructor; return tmp_constructor;
}) })
.push(undefined, function (e) { .push(undefined, function handleGadgetKlassLoadingError(e) {
// Drop the current loading klass info used by selector // Drop the current loading klass info used by selector
// even in case of error // even in case of error
defer.reject(e); var i,
len = gadget_model_defer_dict[url].defer_list.length;
for (i = 0; i < len; i += 1) {
gadget_model_defer_dict[url].defer_list[i].reject(e);
}
delete gadget_model_defer_dict[url].defer_list;
gadget_model_defer_dict[url].result = e;
gadget_model_defer_dict[url].is_resolved = false;
throw e; throw e;
}); });
}; };
...@@ -1962,7 +1977,7 @@ if (typeof document.contains !== 'function') { ...@@ -1962,7 +1977,7 @@ if (typeof document.contains !== 'function') {
// renderJS.clearGadgetKlassList // renderJS.clearGadgetKlassList
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// For test purpose only // For test purpose only
renderJS.clearGadgetKlassList = function () { renderJS.clearGadgetKlassList = function clearGadgetKlassList() {
gadget_model_defer_dict = {}; gadget_model_defer_dict = {};
javascript_registration_dict = {}; javascript_registration_dict = {};
stylesheet_registration_dict = {}; stylesheet_registration_dict = {};
...@@ -1971,53 +1986,54 @@ if (typeof document.contains !== 'function') { ...@@ -1971,53 +1986,54 @@ if (typeof document.contains !== 'function') {
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// renderJS.parseGadgetHTMLDocument // renderJS.parseGadgetHTMLDocument
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
renderJS.parseGadgetHTMLDocument = function (document_element, url) { renderJS.parseGadgetHTMLDocument =
var settings = { function parseGadgetHTMLDocument(document_element, url) {
title: "", var settings = {
interface_list: [], title: "",
required_css_list: [], interface_list: [],
required_js_list: [] required_css_list: [],
}, required_js_list: []
i, },
element; i,
element;
if (!url || !isAbsoluteOrDataURL.test(url)) {
throw new Error("The url should be absolute: " + url); if (!url || !isAbsoluteOrDataURL.test(url)) {
} throw new Error("The url should be absolute: " + url);
}
if (document_element.nodeType === 9) { if (document_element.nodeType === 9) {
settings.title = document_element.title; settings.title = document_element.title;
if (document_element.head !== null) { if (document_element.head !== null) {
for (i = 0; i < document_element.head.children.length; i += 1) { for (i = 0; i < document_element.head.children.length; i += 1) {
element = document_element.head.children[i]; element = document_element.head.children[i];
if (element.href !== null) { if (element.href !== null) {
// XXX Manage relative URL during extraction of URLs // XXX Manage relative URL during extraction of URLs
// element.href returns absolute URL in firefox but "" in chrome; // element.href returns absolute URL in firefox but "" in chrome;
if (element.rel === "stylesheet") { if (element.rel === "stylesheet") {
settings.required_css_list.push( settings.required_css_list.push(
renderJS.getAbsoluteURL(element.getAttribute("href"), url) renderJS.getAbsoluteURL(element.getAttribute("href"), url)
); );
} else if (element.nodeName === "SCRIPT" && } else if (element.nodeName === "SCRIPT" &&
(element.type === "text/javascript" || (element.type === "text/javascript" ||
!element.type)) { !element.type)) {
settings.required_js_list.push( settings.required_js_list.push(
renderJS.getAbsoluteURL(element.getAttribute("src"), url) renderJS.getAbsoluteURL(element.getAttribute("src"), url)
); );
} else if (element.rel === } else if (element.rel ===
"http://www.renderjs.org/rel/interface") { "http://www.renderjs.org/rel/interface") {
settings.interface_list.push( settings.interface_list.push(
renderJS.getAbsoluteURL(element.getAttribute("href"), url) renderJS.getAbsoluteURL(element.getAttribute("href"), url)
); );
}
} }
} }
} }
} else {
throw new Error("The first parameter should be an HTMLDocument");
} }
} else { return settings;
throw new Error("The first parameter should be an HTMLDocument"); };
}
return settings;
};
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// global // global
...@@ -2037,7 +2053,7 @@ if (typeof document.contains !== 'function') { ...@@ -2037,7 +2053,7 @@ if (typeof document.contains !== 'function') {
// is triggered before everything was ready. // is triggered before everything was ready.
// (For instance, the HTML-tag for the self gadget gets inserted after // (For instance, the HTML-tag for the self gadget gets inserted after
// page load) // page load)
renderJS.manualBootstrap = function () { renderJS.manualBootstrap = function manualBootstrap() {
all_dependency_loaded_deferred.resolve(); all_dependency_loaded_deferred.resolve();
}; };
document.addEventListener('DOMContentLoaded', document.addEventListener('DOMContentLoaded',
...@@ -2065,7 +2081,7 @@ if (typeof document.contains !== 'function') { ...@@ -2065,7 +2081,7 @@ if (typeof document.contains !== 'function') {
TmpConstructor.__template_element.appendChild(fragment); TmpConstructor.__template_element.appendChild(fragment);
return RSVP.all([root_gadget.getRequiredJSList(), return RSVP.all([root_gadget.getRequiredJSList(),
root_gadget.getRequiredCSSList()]) root_gadget.getRequiredCSSList()])
.then(function (all_list) { .then(function handleRequireDependencyList(all_list) {
var i, var i,
js_list = all_list[0], js_list = all_list[0],
css_list = all_list[1]; css_list = all_list[1];
...@@ -2076,14 +2092,14 @@ if (typeof document.contains !== 'function') { ...@@ -2076,14 +2092,14 @@ if (typeof document.contains !== 'function') {
stylesheet_registration_dict[css_list[i]] = null; stylesheet_registration_dict[css_list[i]] = null;
} }
gadget_loading_klass_list.shift(); gadget_loading_klass_list.shift();
}).then(function () { }).then(function createMutationObserver() {
// select the target node // select the target node
var target = document.querySelector('body'), var target = document.querySelector('body'),
// create an observer instance // create an observer instance
observer = new MutationObserver(function (mutations) { observer = new MutationObserver(function observeMutatios(mutations) {
var i, k, len, len2, node, added_list; var i, k, len, len2, node, added_list;
mutations.forEach(function (mutation) { mutations.forEach(function observerMutation(mutation) {
if (mutation.type === 'childList') { if (mutation.type === 'childList') {
len = mutation.removedNodes.length; len = mutation.removedNodes.length;
...@@ -2092,7 +2108,7 @@ if (typeof document.contains !== 'function') { ...@@ -2092,7 +2108,7 @@ if (typeof document.contains !== 'function') {
if (node.nodeType === Node.ELEMENT_NODE) { if (node.nodeType === Node.ELEMENT_NODE) {
if (node.hasAttribute("data-gadget-url") && if (node.hasAttribute("data-gadget-url") &&
(node._gadget !== undefined)) { (node._gadget !== undefined)) {
createMonitor(node._gadget); createGadgetMonitor(node._gadget);
} }
added_list = added_list =
node.querySelectorAll("[data-gadget-url]"); node.querySelectorAll("[data-gadget-url]");
...@@ -2100,7 +2116,7 @@ if (typeof document.contains !== 'function') { ...@@ -2100,7 +2116,7 @@ if (typeof document.contains !== 'function') {
for (k = 0; k < len2; k += 1) { for (k = 0; k < len2; k += 1) {
node = added_list[k]; node = added_list[k];
if (node._gadget !== undefined) { if (node._gadget !== undefined) {
createMonitor(node._gadget); createGadgetMonitor(node._gadget);
} }
} }
} }
...@@ -2151,13 +2167,13 @@ if (typeof document.contains !== 'function') { ...@@ -2151,13 +2167,13 @@ if (typeof document.contains !== 'function') {
function createLastAcquisitionGadget() { function createLastAcquisitionGadget() {
var last_acquisition_gadget = new RenderJSGadget(); var last_acquisition_gadget = new RenderJSGadget();
last_acquisition_gadget.__acquired_method_dict = { last_acquisition_gadget.__acquired_method_dict = {
reportServiceError: function (param_list) { reportServiceError: function reportServiceError(param_list) {
letsCrash(param_list[0]); letsCrash(param_list[0]);
} }
}; };
// Stop acquisition on the last acquisition gadget // Stop acquisition on the last acquisition gadget
// Do not put this on the klass, as their could be multiple instances // Do not put this on the klass, as their could be multiple instances
last_acquisition_gadget.__aq_parent = function (method_name) { last_acquisition_gadget.__aq_parent = function __aq_parent(method_name) {
throw new renderJS.AcquisitionError( throw new renderJS.AcquisitionError(
"No gadget provides " + method_name "No gadget provides " + method_name
); );
...@@ -2205,7 +2221,7 @@ if (typeof document.contains !== 'function') { ...@@ -2205,7 +2221,7 @@ if (typeof document.contains !== 'function') {
]; ];
// Inform parent gadget about declareMethod calls here. // Inform parent gadget about declareMethod calls here.
notifyDeclareMethod = function (name) { notifyDeclareMethod = function notifyDeclareMethod(name) {
declare_method_list_waiting.push(name); declare_method_list_waiting.push(name);
}; };
...@@ -2218,13 +2234,13 @@ if (typeof document.contains !== 'function') { ...@@ -2218,13 +2234,13 @@ if (typeof document.contains !== 'function') {
loading_result = RSVP.any([ loading_result = RSVP.any([
channel_defer.promise, channel_defer.promise,
new RSVP.Queue() new RSVP.Queue()
.push(function () { .push(function waitForParentChannelCreation() {
// Expect the channel to parent to be usable after 1 second // Expect the channel to parent to be usable after 1 second
// If not, consider the gadget as the root // If not, consider the gadget as the root
// Drop all iframe channel communication // Drop all iframe channel communication
return RSVP.delay(1000); return RSVP.delay(1000);
}) })
.push(function () { .push(function handleParentChannelCreation() {
real_result_list[2] = undefined; real_result_list[2] = undefined;
return real_result_list; return real_result_list;
}) })
...@@ -2234,20 +2250,22 @@ if (typeof document.contains !== 'function') { ...@@ -2234,20 +2250,22 @@ if (typeof document.contains !== 'function') {
window: window.parent, window: window.parent,
origin: "*", origin: "*",
scope: "renderJS", scope: "renderJS",
onReady: function () { onReady: function onChannelReady() {
var k, var k,
len; len;
// Channel is ready, so now declare all methods // Channel is ready, so now declare all methods
notifyDeclareMethod = function (name) { notifyDeclareMethod = function notifyDeclareMethod(name) {
declare_method_list_waiting.push( declare_method_list_waiting.push(
new RSVP.Promise(function (resolve, reject) { new RSVP.Promise(
embedded_channel.call({ function promiseChannelDeclareMethodCall(resolve, reject) {
method: "declareMethod", embedded_channel.call({
params: name, method: "declareMethod",
success: resolve, params: name,
error: reject success: resolve,
}); error: reject
}) });
}
)
); );
}; };
...@@ -2263,7 +2281,7 @@ if (typeof document.contains !== 'function') { ...@@ -2263,7 +2281,7 @@ if (typeof document.contains !== 'function') {
} }
// Surcharge declareMethod to inform parent window // Surcharge declareMethod to inform parent window
TmpConstructor.declareMethod = function (name, callback) { TmpConstructor.declareMethod = function declareMethod(name, callback) {
var result = RenderJSGadget.declareMethod.apply( var result = RenderJSGadget.declareMethod.apply(
this, this,
[name, callback] [name, callback]
...@@ -2300,11 +2318,11 @@ if (typeof document.contains !== 'function') { ...@@ -2300,11 +2318,11 @@ if (typeof document.contains !== 'function') {
return root_gadget; return root_gadget;
} }
function ready_executable_wrapper(fct) { function ready_executable_wrapper(fct) {
return function (g) { return function wrapReadyFunction(g) {
return fct.call(g, g); return fct.call(g, g);
}; };
} }
TmpConstructor.ready(function () { TmpConstructor.ready(function startServiceInReady() {
return startService(this); return startService(this);
}); });
...@@ -2323,35 +2341,32 @@ if (typeof document.contains !== 'function') { ...@@ -2323,35 +2341,32 @@ if (typeof document.contains !== 'function') {
embedded_channel) { embedded_channel) {
// Define __aq_parent to inform parent window // Define __aq_parent to inform parent window
root_gadget.__aq_parent = root_gadget.__aq_parent =
TmpConstructor.prototype.__aq_parent = function (method_name, TmpConstructor.prototype.__aq_parent = function aq_parent(method_name,
argument_list, argument_list,
time_out) { time_out) {
return new RSVP.Promise(function (resolve, reject) { return new RSVP.Promise(
embedded_channel.call({ function waitForChannelAcquire(resolve, reject) {
method: "acquire", embedded_channel.call({
params: [ method: "acquire",
method_name, params: [
argument_list method_name,
], argument_list
success: function (s) { ],
resolve(s); success: resolve,
}, error: reject,
error: function (e) { timeout: time_out
reject(e); });
}, }
timeout: time_out );
});
});
}; };
// bind calls to renderJS method on the instance // bind calls to renderJS method on the instance
embedded_channel.bind("methodCall", function (trans, v) { embedded_channel.bind("methodCall", function methodCall(trans, v) {
root_gadget[v[0]].apply(root_gadget, v[1]) root_gadget[v[0]].apply(root_gadget, v[1])
.push(function (g) { .push(trans.complete,
trans.complete(g); function handleMethodCallError(e) {
}, function (e) { trans.error(e.toString());
trans.error(e.toString()); });
});
trans.delayReturn(true); trans.delayReturn(true);
}); });
} }
...@@ -2365,11 +2380,11 @@ if (typeof document.contains !== 'function') { ...@@ -2365,11 +2380,11 @@ if (typeof document.contains !== 'function') {
declare_method_list_waiting; declare_method_list_waiting;
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function waitForLoadingGadget() {
// Wait for the loading gadget to be created // Wait for the loading gadget to be created
return wait_for_gadget_loaded; return wait_for_gadget_loaded;
}) })
.push(function (result_list) { .push(function handleLoadingGadget(result_list) {
TmpConstructor = result_list[0]; TmpConstructor = result_list[0];
root_gadget = result_list[1]; root_gadget = result_list[1];
embedded_channel = result_list[2]; embedded_channel = result_list[2];
...@@ -2377,11 +2392,11 @@ if (typeof document.contains !== 'function') { ...@@ -2377,11 +2392,11 @@ if (typeof document.contains !== 'function') {
// Wait for all the gadget dependencies to be loaded // Wait for all the gadget dependencies to be loaded
return all_dependency_loaded_deferred.promise; return all_dependency_loaded_deferred.promise;
}) })
.push(function () { .push(function waitForDeclareMethodList() {
// Wait for all methods to be correctly declared // Wait for all methods to be correctly declared
return RSVP.all(declare_method_list_waiting); return RSVP.all(declare_method_list_waiting);
}) })
.push(function (result_list) { .push(function waitForMutationObserver(result_list) {
if (embedded_channel !== undefined) { if (embedded_channel !== undefined) {
finishAqParentConfiguration(TmpConstructor, root_gadget, finishAqParentConfiguration(TmpConstructor, root_gadget,
embedded_channel); embedded_channel);
...@@ -2389,16 +2404,16 @@ if (typeof document.contains !== 'function') { ...@@ -2389,16 +2404,16 @@ if (typeof document.contains !== 'function') {
// Check all DOM modifications to correctly start/stop services // Check all DOM modifications to correctly start/stop services
return configureMutationObserver(TmpConstructor, url, root_gadget); return configureMutationObserver(TmpConstructor, url, root_gadget);
}) })
.push(function () { .push(function waitForReadyList() {
// Trigger all ready functions // Trigger all ready functions
return triggerReadyList(TmpConstructor, root_gadget); return triggerReadyList(TmpConstructor, root_gadget);
}) })
.push(function () { .push(function notifyReady() {
if (embedded_channel !== undefined) { if (embedded_channel !== undefined) {
embedded_channel.notify({method: "ready"}); embedded_channel.notify({method: "ready"});
} }
}) })
.push(undefined, function (e) { .push(undefined, function handleBootstrapError(e) {
letsCrash(e); letsCrash(e);
if (embedded_channel !== undefined) { if (embedded_channel !== undefined) {
embedded_channel.notify({method: "failed", params: e.toString()}); embedded_channel.notify({method: "failed", params: e.toString()});
......
...@@ -230,7 +230,7 @@ ...@@ -230,7 +230,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>961.58176.38662.18807</string> </value> <value> <string>962.24020.58652.57139</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -248,7 +248,7 @@ ...@@ -248,7 +248,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1505981711.78</float> <float>1508945098.52</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -670,21 +670,22 @@ if (typeof document.contains !== 'function') { ...@@ -670,21 +670,22 @@ if (typeof document.contains !== 'function') {
/* /*
* renderJs - Generic Gadget library renderer. * renderJs - Generic Gadget library renderer.
* http://www.renderjs.org/documentation * https://renderjs.nexedi.com/
*/ */
(function (document, window, RSVP, DOMParser, Channel, MutationObserver, (function wrapRenderJS(document, window, RSVP, DOMParser, Channel,
Node, FileReader, Blob, navigator, Event, URL) { MutationObserver, Node, FileReader, Blob, navigator,
Event, URL) {
"use strict"; "use strict";
function readBlobAsDataURL(blob) { function readBlobAsDataURL(blob) {
var fr = new FileReader(); var fr = new FileReader();
return new RSVP.Promise(function (resolve, reject) { return new RSVP.Promise(function waitFormDataURLRead(resolve, reject) {
fr.addEventListener("load", function (evt) { fr.addEventListener("load", function handleDataURLRead(evt) {
resolve(evt.target.result); resolve(evt.target.result);
}); });
fr.addEventListener("error", reject); fr.addEventListener("error", reject);
fr.readAsDataURL(blob); fr.readAsDataURL(blob);
}, function () { }, function cancelReadBlobAsDataURL() {
fr.abort(); fr.abort();
}); });
} }
...@@ -717,7 +718,7 @@ if (typeof document.contains !== 'function') { ...@@ -717,7 +718,7 @@ if (typeof document.contains !== 'function') {
} }
function itsANonResolvableTrap(resolve, reject) { function itsANonResolvableTrap(resolve, reject) {
var result; var result;
handle_event_callback = function (evt) { handle_event_callback = function handleEventCallback(evt) {
if (prevent_default) { if (prevent_default) {
evt.stopPropagation(); evt.stopPropagation();
evt.preventDefault(); evt.preventDefault();
...@@ -733,10 +734,10 @@ if (typeof document.contains !== 'function') { ...@@ -733,10 +734,10 @@ if (typeof document.contains !== 'function') {
callback_promise = result; callback_promise = result;
new RSVP.Queue() new RSVP.Queue()
.push(function () { .push(function waitForEventCallbackResult() {
return result; return result;
}) })
.push(undefined, function (error) { .push(undefined, function handleEventCallbackError(error) {
if (!(error instanceof RSVP.CancellationError)) { if (!(error instanceof RSVP.CancellationError)) {
canceller(); canceller();
reject(error); reject(error);
...@@ -814,11 +815,11 @@ if (typeof document.contains !== 'function') { ...@@ -814,11 +815,11 @@ if (typeof document.contains !== 'function') {
error_list = [], error_list = [],
all_dependency_loaded_deferred; all_dependency_loaded_deferred;
window.addEventListener('error', function (error) { window.addEventListener('error', function handleGlobalError(error) {
error_list.push(error); error_list.push(error);
}); });
window.addEventListener('beforeunload', function () { window.addEventListener('beforeunload', function handleBeforeUnload() {
// XXX If another listener cancel the page unload, // XXX If another listener cancel the page unload,
// it will not restore renderJS crash report // it will not restore renderJS crash report
is_page_unloaded = true; is_page_unloaded = true;
...@@ -961,12 +962,11 @@ if (typeof document.contains !== 'function') { ...@@ -961,12 +962,11 @@ if (typeof document.contains !== 'function') {
ResolvedMonitorError.prototype = new Error(); ResolvedMonitorError.prototype = new Error();
ResolvedMonitorError.prototype.constructor = ResolvedMonitorError; ResolvedMonitorError.prototype.constructor = ResolvedMonitorError;
Monitor = function () { Monitor = function createMonitor() {
var monitor = this, var monitor = this,
promise_list = [], promise_list = [],
promise, promise,
reject, reject,
notify,
resolved; resolved;
if (!(this instanceof Monitor)) { if (!(this instanceof Monitor)) {
...@@ -983,8 +983,8 @@ if (typeof document.contains !== 'function') { ...@@ -983,8 +983,8 @@ if (typeof document.contains !== 'function') {
promise_list = []; promise_list = [];
} }
promise = new RSVP.Promise(function (done, fail, progress) { promise = new RSVP.Promise(function promiseMonitor(done, fail) {
reject = function (rejectedReason) { reject = function rejectMonitor(rejectedReason) {
if (resolved) { if (resolved) {
return; return;
} }
...@@ -994,36 +994,31 @@ if (typeof document.contains !== 'function') { ...@@ -994,36 +994,31 @@ if (typeof document.contains !== 'function') {
canceller(); canceller();
return fail(rejectedReason); return fail(rejectedReason);
}; };
notify = progress;
}, canceller); }, canceller);
monitor.cancel = function () { monitor.cancel = function cancelMonitor() {
if (resolved) { if (resolved) {
return; return;
} }
resolved = true; resolved = true;
promise.cancel(); promise.cancel();
promise.fail(function (rejectedReason) { promise.fail(function rejectMonitorPromise(rejectedReason) {
monitor.isRejected = true; monitor.isRejected = true;
monitor.rejectedReason = rejectedReason; monitor.rejectedReason = rejectedReason;
}); });
}; };
monitor.then = function () { monitor.then = promise.then.bind(promise);
return promise.then.apply(promise, arguments); monitor.fail = promise.fail.bind(promise);
};
monitor.fail = function () {
return promise.fail.apply(promise, arguments);
};
monitor.monitor = function (promise_to_monitor) { monitor.monitor = function startMonitor(promise_to_monitor) {
if (resolved) { if (resolved) {
throw new ResolvedMonitorError(); throw new ResolvedMonitorError();
} }
var queue = new RSVP.Queue() var queue = new RSVP.Queue()
.push(function () { .push(function waitForPromiseToMonitor() {
return promise_to_monitor; return promise_to_monitor;
}) })
.push(function (fulfillmentValue) { .push(function handlePromiseToMonitorSuccess(fulfillmentValue) {
// Promise to monitor is fullfilled, remove it from the list // Promise to monitor is fullfilled, remove it from the list
var len = promise_list.length, var len = promise_list.length,
sub_promise_to_monitor, sub_promise_to_monitor,
...@@ -1037,7 +1032,7 @@ if (typeof document.contains !== 'function') { ...@@ -1037,7 +1032,7 @@ if (typeof document.contains !== 'function') {
} }
} }
promise_list = new_promise_list; promise_list = new_promise_list;
}, function (rejectedReason) { }, function handlePromiseToMonitorError(rejectedReason) {
if (rejectedReason instanceof RSVP.CancellationError) { if (rejectedReason instanceof RSVP.CancellationError) {
if (!(promise_to_monitor.isFulfilled && if (!(promise_to_monitor.isFulfilled &&
promise_to_monitor.isRejected)) { promise_to_monitor.isRejected)) {
...@@ -1047,9 +1042,6 @@ if (typeof document.contains !== 'function') { ...@@ -1047,9 +1042,6 @@ if (typeof document.contains !== 'function') {
} }
reject(rejectedReason); reject(rejectedReason);
throw rejectedReason; throw rejectedReason;
}, function (notificationValue) {
notify(notificationValue);
return notificationValue;
}); });
promise_list.push(queue); promise_list.push(queue);
...@@ -1076,7 +1068,7 @@ if (typeof document.contains !== 'function') { ...@@ -1076,7 +1068,7 @@ if (typeof document.contains !== 'function') {
RenderJSGadget.prototype.__required_css_list = []; RenderJSGadget.prototype.__required_css_list = [];
RenderJSGadget.prototype.__required_js_list = []; RenderJSGadget.prototype.__required_js_list = [];
function createMonitor(g) { function createGadgetMonitor(g) {
if (g.__monitor !== undefined) { if (g.__monitor !== undefined) {
g.__monitor.cancel(); g.__monitor.cancel();
} }
...@@ -1084,19 +1076,17 @@ if (typeof document.contains !== 'function') { ...@@ -1084,19 +1076,17 @@ if (typeof document.contains !== 'function') {
g.__job_dict = {}; g.__job_dict = {};
g.__job_list = []; g.__job_list = [];
g.__job_triggered = false; g.__job_triggered = false;
g.__monitor.fail(function (error) { g.__monitor.fail(function handleGadgetMonitorError(error) {
if (!(error instanceof RSVP.CancellationError)) { if (!(error instanceof RSVP.CancellationError)) {
return g.aq_reportServiceError(error); return g.aq_reportServiceError(error);
} }
}).fail(function (error) { // Crash the application if the acquisition generates an error.
// Crash the application if the acquisition generates an error. }).fail(letsCrash);
return letsCrash(error);
});
} }
function clearGadgetInternalParameters() { function clearGadgetInternalParameters() {
this.__sub_gadget_dict = {}; this.__sub_gadget_dict = {};
createMonitor(this); createGadgetMonitor(this);
} }
function loadSubGadgetDOMDeclaration() { function loadSubGadgetDOMDeclaration() {
...@@ -1110,7 +1100,7 @@ if (typeof document.contains !== 'function') { ...@@ -1110,7 +1100,7 @@ if (typeof document.contains !== 'function') {
context = this; context = this;
function prepareReportGadgetDeclarationError(scope) { function prepareReportGadgetDeclarationError(scope) {
return function (error) { return function reportGadgetDeclarationError(error) {
var aq_dict = context.__acquired_method_dict || {}, var aq_dict = context.__acquired_method_dict || {},
method_name = 'reportGadgetDeclarationError'; method_name = 'reportGadgetDeclarationError';
if (aq_dict.hasOwnProperty(method_name)) { if (aq_dict.hasOwnProperty(method_name)) {
...@@ -1143,58 +1133,56 @@ if (typeof document.contains !== 'function') { ...@@ -1143,58 +1133,56 @@ if (typeof document.contains !== 'function') {
RenderJSGadget.__ready_list = [clearGadgetInternalParameters, RenderJSGadget.__ready_list = [clearGadgetInternalParameters,
loadSubGadgetDOMDeclaration]; loadSubGadgetDOMDeclaration];
RenderJSGadget.ready = function (callback) { RenderJSGadget.ready = function ready(callback) {
this.__ready_list.push(callback); this.__ready_list.push(callback);
return this; return this;
}; };
RenderJSGadget.setState = function (state_dict) { RenderJSGadget.setState = function setState(state_dict) {
var json_state = JSON.stringify(state_dict); var json_state = JSON.stringify(state_dict);
this.__ready_list.unshift(function () { this.__ready_list.unshift(function setStateDefaultValue() {
this.state = JSON.parse(json_state); this.state = JSON.parse(json_state);
}); });
return this; return this;
}; };
RenderJSGadget.onStateChange = function (callback) { RenderJSGadget.onStateChange = function onStateChange(callback) {
this.prototype.__state_change_callback = callback; this.prototype.__state_change_callback = callback;
return this; return this;
}; };
RenderJSGadget.__service_list = []; RenderJSGadget.__service_list = [];
RenderJSGadget.declareService = function (callback) { RenderJSGadget.declareService = function declareService(callback) {
this.__service_list.push(callback); this.__service_list.push(callback);
return this; return this;
}; };
RenderJSGadget.onEvent = function (type, callback, use_capture, RenderJSGadget.onEvent = function onEvent(type, callback, use_capture,
prevent_default) { prevent_default) {
this.__service_list.push(function () { this.__service_list.push(function startLoopEventListenerService() {
return loopEventListener(this.element, type, use_capture, return loopEventListener(this.element, type, use_capture,
callback.bind(this), prevent_default); callback.bind(this), prevent_default);
}); });
return this; return this;
}; };
RenderJSGadget.onLoop = function (callback, delay) { RenderJSGadget.onLoop = function onLoop(callback, delay) {
if (delay === undefined) { if (delay === undefined) {
delay = 0; delay = 0;
} }
this.__service_list.push(function () { this.__service_list.push(function handleServiceCallback() {
var queue_loop = new RSVP.Queue(), var queue_loop = new RSVP.Queue(),
context = this, context = this,
wait = function () { wait = function waitForLoopIteration() {
queue_loop queue_loop
.push(function () { .push(function waitNextOnLoopDelay() {
return RSVP.delay(delay); return RSVP.delay(delay);
}) })
.push(function () { .push(function waitNextOnLoopAnimationFrame() {
// Only loop when the app has the focus // Only loop when the app has the focus
return promiseAnimationFrame(); return promiseAnimationFrame();
}) })
.push(function () { .push(function executeOnLoopCallback() {
return callback.apply(context, []); return callback.apply(context, []);
}) })
.push(function () { .push(wait);
wait();
});
}; };
wait(); wait();
return queue_loop; return queue_loop;
...@@ -1204,7 +1192,7 @@ if (typeof document.contains !== 'function') { ...@@ -1204,7 +1192,7 @@ if (typeof document.contains !== 'function') {
function runJob(gadget, name, callback, argument_list) { function runJob(gadget, name, callback, argument_list) {
var job_promise = new RSVP.Queue() var job_promise = new RSVP.Queue()
.push(function () { .push(function waitForJobCallback() {
return callback.apply(gadget, argument_list); return callback.apply(gadget, argument_list);
}); });
if (gadget.__job_dict.hasOwnProperty(name)) { if (gadget.__job_dict.hasOwnProperty(name)) {
...@@ -1212,10 +1200,10 @@ if (typeof document.contains !== 'function') { ...@@ -1212,10 +1200,10 @@ if (typeof document.contains !== 'function') {
} }
gadget.__job_dict[name] = job_promise; gadget.__job_dict[name] = job_promise;
gadget.__monitor.monitor(new RSVP.Queue() gadget.__monitor.monitor(new RSVP.Queue()
.push(function () { .push(function waitForJobPromise() {
return job_promise; return job_promise;
}) })
.push(undefined, function (error) { .push(undefined, function handleJobError(error) {
if (!(error instanceof RSVP.CancellationError)) { if (!(error instanceof RSVP.CancellationError)) {
throw error; throw error;
} }
...@@ -1224,7 +1212,7 @@ if (typeof document.contains !== 'function') { ...@@ -1224,7 +1212,7 @@ if (typeof document.contains !== 'function') {
function startService(gadget) { function startService(gadget) {
gadget.__monitor.monitor(new RSVP.Queue() gadget.__monitor.monitor(new RSVP.Queue()
.push(function () { .push(function monitorAllServiceList() {
var i, var i,
service_list = gadget.constructor.__service_list, service_list = gadget.constructor.__service_list,
job_list = gadget.__job_list; job_list = gadget.__job_list;
...@@ -1245,8 +1233,8 @@ if (typeof document.contains !== 'function') { ...@@ -1245,8 +1233,8 @@ if (typeof document.contains !== 'function') {
// gadget internal method, which trigger execution // gadget internal method, which trigger execution
// of a function inside a service // of a function inside a service
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
RenderJSGadget.declareJob = function (name, callback) { RenderJSGadget.declareJob = function declareJob(name, callback) {
this.prototype[name] = function () { this.prototype[name] = function triggerJob() {
var context = this, var context = this,
argument_list = arguments; argument_list = arguments;
...@@ -1263,13 +1251,13 @@ if (typeof document.contains !== 'function') { ...@@ -1263,13 +1251,13 @@ if (typeof document.contains !== 'function') {
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// RenderJSGadget.declareMethod // RenderJSGadget.declareMethod
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
RenderJSGadget.declareMethod = function (name, callback) { RenderJSGadget.declareMethod = function declareMethod(name, callback) {
this.prototype[name] = function () { this.prototype[name] = function triggerMethod() {
var context = this, var context = this,
argument_list = arguments; argument_list = arguments;
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function waitForMethodCallback() {
return callback.apply(context, argument_list); return callback.apply(context, argument_list);
}); });
}; };
...@@ -1278,27 +1266,27 @@ if (typeof document.contains !== 'function') { ...@@ -1278,27 +1266,27 @@ if (typeof document.contains !== 'function') {
}; };
RenderJSGadget RenderJSGadget
.declareMethod('getInterfaceList', function () { .declareMethod('getInterfaceList', function getInterfaceList() {
// Returns the list of gadget prototype // Returns the list of gadget prototype
return this.__interface_list; return this.__interface_list;
}) })
.declareMethod('getRequiredCSSList', function () { .declareMethod('getRequiredCSSList', function getRequiredCSSList() {
// Returns a list of CSS required by the gadget // Returns a list of CSS required by the gadget
return this.__required_css_list; return this.__required_css_list;
}) })
.declareMethod('getRequiredJSList', function () { .declareMethod('getRequiredJSList', function getRequiredJSList() {
// Returns a list of JS required by the gadget // Returns a list of JS required by the gadget
return this.__required_js_list; return this.__required_js_list;
}) })
.declareMethod('getPath', function () { .declareMethod('getPath', function getPath() {
// Returns the path of the code of a gadget // Returns the path of the code of a gadget
return this.__path; return this.__path;
}) })
.declareMethod('getTitle', function () { .declareMethod('getTitle', function getTitle() {
// Returns the title of a gadget // Returns the title of a gadget
return this.__title; return this.__title;
}) })
.declareMethod('getElement', function () { .declareMethod('getElement', function getElement() {
// Returns the DOM Element of a gadget // Returns the DOM Element of a gadget
// XXX Kept for compatibility. Use element property directly // XXX Kept for compatibility. Use element property directly
if (this.element === undefined) { if (this.element === undefined) {
...@@ -1306,24 +1294,24 @@ if (typeof document.contains !== 'function') { ...@@ -1306,24 +1294,24 @@ if (typeof document.contains !== 'function') {
} }
return this.element; return this.element;
}) })
.declareMethod('changeState', function (state_dict) { .declareMethod('changeState', function changeState(state_dict) {
var next_onStateChange = new RSVP.Queue(), var next_onStateChange = new RSVP.Queue(),
previous_onStateCHange, previous_onStateCHange,
context = this; context = this;
if (context.hasOwnProperty('__previous_onStateChange')) { if (context.hasOwnProperty('__previous_onStateChange')) {
previous_onStateCHange = context.__previous_onStateChange; previous_onStateCHange = context.__previous_onStateChange;
next_onStateChange next_onStateChange
.push(function () { .push(function waitForPreviousStateChange() {
return previous_onStateCHange; return previous_onStateCHange;
}) })
.push(undefined, function () { .push(undefined, function handlePreviousStateChangeError() {
// Run callback even if previous failed // Run callback even if previous failed
return; return;
}); });
} }
context.__previous_onStateChange = next_onStateChange; context.__previous_onStateChange = next_onStateChange;
return next_onStateChange return next_onStateChange
.push(function () { .push(function checkStateModification() {
var key, var key,
modified = false, modified = false,
previous_cancelled = context.hasOwnProperty('__modification_dict'), previous_cancelled = context.hasOwnProperty('__modification_dict'),
...@@ -1345,10 +1333,10 @@ if (typeof document.contains !== 'function') { ...@@ -1345,10 +1333,10 @@ if (typeof document.contains !== 'function') {
if (modified && context.__state_change_callback !== undefined) { if (modified && context.__state_change_callback !== undefined) {
context.__modification_dict = modification_dict; context.__modification_dict = modification_dict;
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function waitForStateChangeCallback() {
return context.__state_change_callback(modification_dict); return context.__state_change_callback(modification_dict);
}) })
.push(function (result) { .push(function handleStateChangeSuccess(result) {
delete context.__modification_dict; delete context.__modification_dict;
return result; return result;
}); });
...@@ -1372,7 +1360,7 @@ if (typeof document.contains !== 'function') { ...@@ -1372,7 +1360,7 @@ if (typeof document.contains !== 'function') {
} }
} }
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function waitForAcquireMethod() {
// Do not specify default __acquired_method_dict on prototype // Do not specify default __acquired_method_dict on prototype
// to prevent modifying this default value (with // to prevent modifying this default value (with
// allowPublicAcquiredMethod for example) // allowPublicAcquiredMethod for example)
...@@ -1383,7 +1371,7 @@ if (typeof document.contains !== 'function') { ...@@ -1383,7 +1371,7 @@ if (typeof document.contains !== 'function') {
} }
throw new renderJS.AcquisitionError("aq_dynamic is not defined"); throw new renderJS.AcquisitionError("aq_dynamic is not defined");
}) })
.push(undefined, function (error) { .push(undefined, function handleAcquireMethodError(error) {
if (error instanceof renderJS.AcquisitionError) { if (error instanceof renderJS.AcquisitionError) {
return gadget.__aq_parent(method_name, argument_list); return gadget.__aq_parent(method_name, argument_list);
} }
...@@ -1392,12 +1380,12 @@ if (typeof document.contains !== 'function') { ...@@ -1392,12 +1380,12 @@ if (typeof document.contains !== 'function') {
} }
RenderJSGadget.declareAcquiredMethod = RenderJSGadget.declareAcquiredMethod =
function (name, method_name_to_acquire) { function declareAcquiredMethod(name, method_name_to_acquire) {
this.prototype[name] = function () { this.prototype[name] = function acquireMethod() {
var argument_list = Array.prototype.slice.call(arguments, 0), var argument_list = Array.prototype.slice.call(arguments, 0),
gadget = this; gadget = this;
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function waitForAqParent() {
return gadget.__aq_parent(method_name_to_acquire, argument_list); return gadget.__aq_parent(method_name_to_acquire, argument_list);
}); });
}; };
...@@ -1414,7 +1402,7 @@ if (typeof document.contains !== 'function') { ...@@ -1414,7 +1402,7 @@ if (typeof document.contains !== 'function') {
// RenderJSGadget.allowPublicAcquisition // RenderJSGadget.allowPublicAcquisition
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
RenderJSGadget.allowPublicAcquisition = RenderJSGadget.allowPublicAcquisition =
function (method_name, callback) { function allowPublicAcquisition(method_name, callback) {
this.prototype.__acquired_method_dict[method_name] = callback; this.prototype.__acquired_method_dict[method_name] = callback;
// Allow chain // Allow chain
...@@ -1423,10 +1411,11 @@ if (typeof document.contains !== 'function') { ...@@ -1423,10 +1411,11 @@ if (typeof document.contains !== 'function') {
// Set aq_parent on gadget_instance which call acquire on parent_gadget // Set aq_parent on gadget_instance which call acquire on parent_gadget
function setAqParent(gadget_instance, parent_gadget) { function setAqParent(gadget_instance, parent_gadget) {
gadget_instance.__aq_parent = function (method_name, argument_list) { gadget_instance.__aq_parent =
return acquire.apply(parent_gadget, [gadget_instance, method_name, function __aq_parent(method_name, argument_list) {
argument_list]); return acquire.apply(parent_gadget, [gadget_instance, method_name,
}; argument_list]);
};
} }
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
...@@ -1462,19 +1451,13 @@ if (typeof document.contains !== 'function') { ...@@ -1462,19 +1451,13 @@ if (typeof document.contains !== 'function') {
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
function privateDeclarePublicGadget(url, options, parent_gadget) { function privateDeclarePublicGadget(url, options, parent_gadget) {
return new RSVP.Queue() return renderJS.declareGadgetKlass(url)
.push(function () { // gadget loading should not be interrupted
return renderJS.declareGadgetKlass(url) // if not, gadget's definition will not be complete
// gadget loading should not be interrupted //.then will return another promise
// if not, gadget's definition will not be complete //so loading_klass_promise can't be cancel
//.then will return another promise .then(function createPrivateInstanceFromKlass(Klass) {
//so loading_klass_promise can't be cancel // Get the gadget class and instanciate it
.then(function (result) {
return result;
});
})
// Get the gadget class and instanciate it
.push(function (Klass) {
if (options.element === undefined) { if (options.element === undefined) {
options.element = document.createElement("div"); options.element = document.createElement("div");
} }
...@@ -1543,12 +1526,12 @@ if (typeof document.contains !== 'function') { ...@@ -1543,12 +1526,12 @@ if (typeof document.contains !== 'function') {
gadget_instance = new RenderJSIframeGadget(); gadget_instance = new RenderJSIframeGadget();
setAqParent(gadget_instance, parent_gadget); setAqParent(gadget_instance, parent_gadget);
iframe = document.createElement("iframe"); iframe = document.createElement("iframe");
iframe.addEventListener('error', function (error) { iframe.addEventListener('error', function handleIframeError(error) {
iframe_loading_deferred.reject(error); iframe_loading_deferred.reject(error);
}); });
iframe.addEventListener('load', function () { iframe.addEventListener('load', function handleIframeLoad() {
return RSVP.timeout(5000) return RSVP.timeout(5000)
.fail(function () { .fail(function triggerIframeTimeout() {
iframe_loading_deferred.reject( iframe_loading_deferred.reject(
new Error('Timeout while loading: ' + url) new Error('Timeout while loading: ' + url)
); );
...@@ -1573,46 +1556,51 @@ if (typeof document.contains !== 'function') { ...@@ -1573,46 +1556,51 @@ if (typeof document.contains !== 'function') {
}); });
// Create new method from the declareMethod call inside the iframe // Create new method from the declareMethod call inside the iframe
gadget_instance.__chan.bind("declareMethod", gadget_instance.__chan.bind(
function (trans, method_name) { "declareMethod",
gadget_instance[method_name] = function () { function handleChannelDeclareMethod(trans, method_name) {
gadget_instance[method_name] = function triggerChannelDeclareMethod() {
var argument_list = arguments, var argument_list = arguments,
wait_promise = new RSVP.Promise(function (resolve, reject) { wait_promise = new RSVP.Promise(
gadget_instance.__chan.call({ function handleChannelCall(resolve, reject) {
method: "methodCall", gadget_instance.__chan.call({
params: [ method: "methodCall",
method_name, params: [
Array.prototype.slice.call(argument_list, 0)], method_name,
success: resolve, Array.prototype.slice.call(argument_list, 0)],
error: reject success: resolve,
}); error: reject
}); });
}
);
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function waitForChannelCall() {
return wait_promise; return wait_promise;
}); });
}; };
return "OK"; return "OK";
}); }
);
// Wait for the iframe to be loaded before continuing // Wait for the iframe to be loaded before continuing
gadget_instance.__chan.bind("ready", function (trans) { gadget_instance.__chan.bind("ready", function handleChannelReady(trans) {
iframe_loading_deferred.resolve(gadget_instance); iframe_loading_deferred.resolve(gadget_instance);
return "OK"; return "OK";
}); });
gadget_instance.__chan.bind("failed", function (trans, params) { gadget_instance.__chan.bind("failed",
iframe_loading_deferred.reject(params); function handleChannelFail(trans, params) {
return "OK"; iframe_loading_deferred.reject(params);
}); return "OK";
gadget_instance.__chan.bind("acquire", function (trans, params) { });
gadget_instance.__aq_parent.apply(gadget_instance, params) gadget_instance.__chan.bind("acquire",
.then(function (g) { function handleChannelAcquire(trans, params) {
trans.complete(g); gadget_instance.__aq_parent.apply(gadget_instance, params)
}).fail(function (e) { .then(trans.complete)
trans.error(e.toString()); .fail(function handleChannelAcquireError(e) {
}); trans.error(e.toString());
trans.delayReturn(true); });
}); trans.delayReturn(true);
});
return iframe_loading_deferred.promise; return iframe_loading_deferred.promise;
} }
...@@ -1623,10 +1611,10 @@ if (typeof document.contains !== 'function') { ...@@ -1623,10 +1611,10 @@ if (typeof document.contains !== 'function') {
function privateDeclareDataUrlGadget(url, options, parent_gadget) { function privateDeclareDataUrlGadget(url, options, parent_gadget) {
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function waitForDataUrlAjax() {
return ajax(url); return ajax(url);
}) })
.push(function (xhr) { .push(function handleDataURLAjaxResponse(xhr) {
// Insert a "base" element, in order to resolve all relative links // Insert a "base" element, in order to resolve all relative links
// which could get broken with a data url // which could get broken with a data url
var doc = (new DOMParser()).parseFromString(xhr.responseText, var doc = (new DOMParser()).parseFromString(xhr.responseText,
...@@ -1639,7 +1627,7 @@ if (typeof document.contains !== 'function') { ...@@ -1639,7 +1627,7 @@ if (typeof document.contains !== 'function') {
{type: "text/html;charset=UTF-8"}); {type: "text/html;charset=UTF-8"});
return readBlobAsDataURL(blob); return readBlobAsDataURL(blob);
}) })
.push(function (data_url) { .push(function handleDataURL(data_url) {
return privateDeclareIframeGadget(data_url, options, parent_gadget); return privateDeclareIframeGadget(data_url, options, parent_gadget);
}); });
} }
...@@ -1648,7 +1636,7 @@ if (typeof document.contains !== 'function') { ...@@ -1648,7 +1636,7 @@ if (typeof document.contains !== 'function') {
// RenderJSGadget.declareGadget // RenderJSGadget.declareGadget
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
RenderJSGadget RenderJSGadget
.declareMethod('declareGadget', function (url, options) { .declareMethod('declareGadget', function declareGadget(url, options) {
var parent_gadget = this; var parent_gadget = this;
if (options === undefined) { if (options === undefined) {
...@@ -1662,7 +1650,7 @@ if (typeof document.contains !== 'function') { ...@@ -1662,7 +1650,7 @@ if (typeof document.contains !== 'function') {
url = renderJS.getAbsoluteURL(url, this.__path); url = renderJS.getAbsoluteURL(url, this.__path);
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function waitForPrivateDeclareGadget() {
var method; var method;
if (options.sandbox === "public") { if (options.sandbox === "public") {
method = privateDeclarePublicGadget; method = privateDeclarePublicGadget;
...@@ -1677,7 +1665,7 @@ if (typeof document.contains !== 'function') { ...@@ -1677,7 +1665,7 @@ if (typeof document.contains !== 'function') {
return method(url, options, parent_gadget); return method(url, options, parent_gadget);
}) })
// Set the HTML context // Set the HTML context
.push(function (gadget_instance) { .push(function setGadgetInstanceHTMLContext(gadget_instance) {
var i, var i,
scope, scope,
queue = new RSVP.Queue(); queue = new RSVP.Queue();
...@@ -1686,7 +1674,7 @@ if (typeof document.contains !== 'function') { ...@@ -1686,7 +1674,7 @@ if (typeof document.contains !== 'function') {
return gadget_instance; return gadget_instance;
} }
function ready_executable_wrapper(fct) { function ready_executable_wrapper(fct) {
return function () { return function executeReadyWrapper() {
return fct.call(gadget_instance, gadget_instance); return fct.call(gadget_instance, gadget_instance);
}; };
} }
...@@ -1730,13 +1718,14 @@ if (typeof document.contains !== 'function') { ...@@ -1730,13 +1718,14 @@ if (typeof document.contains !== 'function') {
return queue; return queue;
}); });
}) })
.declareMethod('getDeclaredGadget', function (gadget_scope) { .declareMethod('getDeclaredGadget',
if (!this.__sub_gadget_dict.hasOwnProperty(gadget_scope)) { function getDeclaredGadget(gadget_scope) {
throw new Error("Gadget scope '" + gadget_scope + "' is not known."); if (!this.__sub_gadget_dict.hasOwnProperty(gadget_scope)) {
} throw new Error("Gadget scope '" + gadget_scope + "' is not known.");
return this.__sub_gadget_dict[gadget_scope]; }
}) return this.__sub_gadget_dict[gadget_scope];
.declareMethod('dropGadget', function (gadget_scope) { })
.declareMethod('dropGadget', function dropGadget(gadget_scope) {
if (!this.__sub_gadget_dict.hasOwnProperty(gadget_scope)) { if (!this.__sub_gadget_dict.hasOwnProperty(gadget_scope)) {
throw new Error("Gadget scope '" + gadget_scope + "' is not known."); throw new Error("Gadget scope '" + gadget_scope + "' is not known.");
} }
...@@ -1747,7 +1736,7 @@ if (typeof document.contains !== 'function') { ...@@ -1747,7 +1736,7 @@ if (typeof document.contains !== 'function') {
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// renderJS selector // renderJS selector
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
renderJS = function (selector) { renderJS = function getLoadingGadget(selector) {
var result; var result;
if (selector === window) { if (selector === window) {
// window is the 'this' value when loading a javascript file // window is the 'this' value when loading a javascript file
...@@ -1763,7 +1752,7 @@ if (typeof document.contains !== 'function') { ...@@ -1763,7 +1752,7 @@ if (typeof document.contains !== 'function') {
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// renderJS.AcquisitionError // renderJS.AcquisitionError
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
renderJS.AcquisitionError = function (message) { renderJS.AcquisitionError = function createAcquisitionError(message) {
this.name = "AcquisitionError"; this.name = "AcquisitionError";
if ((message !== undefined) && (typeof message !== "string")) { if ((message !== undefined) && (typeof message !== "string")) {
throw new TypeError('You must pass a string.'); throw new TypeError('You must pass a string.');
...@@ -1777,7 +1766,7 @@ if (typeof document.contains !== 'function') { ...@@ -1777,7 +1766,7 @@ if (typeof document.contains !== 'function') {
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// renderJS.getAbsoluteURL // renderJS.getAbsoluteURL
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
renderJS.getAbsoluteURL = function (url, base_url) { renderJS.getAbsoluteURL = function getAbsoluteURL(url, base_url) {
if (base_url && url) { if (base_url && url) {
return new URL(url, base_url).href; return new URL(url, base_url).href;
} }
...@@ -1787,7 +1776,7 @@ if (typeof document.contains !== 'function') { ...@@ -1787,7 +1776,7 @@ if (typeof document.contains !== 'function') {
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// renderJS.declareJS // renderJS.declareJS
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
renderJS.declareJS = function (url, container, pop) { renderJS.declareJS = function declareJS(url, container, pop) {
// https://www.html5rocks.com/en/tutorials/speed/script-loading/ // https://www.html5rocks.com/en/tutorials/speed/script-loading/
// Prevent infinite recursion if loading render.js // Prevent infinite recursion if loading render.js
// more than once // more than once
...@@ -1796,28 +1785,30 @@ if (typeof document.contains !== 'function') { ...@@ -1796,28 +1785,30 @@ if (typeof document.contains !== 'function') {
result = RSVP.resolve(); result = RSVP.resolve();
} else { } else {
javascript_registration_dict[url] = null; javascript_registration_dict[url] = null;
result = new RSVP.Promise(function (resolve, reject) { result = new RSVP.Promise(
var newScript; function waitForJSLoadEvent(resolve, reject) {
newScript = document.createElement('script'); var newScript;
newScript.async = false; newScript = document.createElement('script');
newScript.type = 'text/javascript'; newScript.async = false;
newScript.onload = function () { newScript.type = 'text/javascript';
if (pop === true) { newScript.onload = function triggerJSLoaded() {
// Drop the current loading klass info used by selector if (pop === true) {
gadget_loading_klass_list.shift(); // Drop the current loading klass info used by selector
} gadget_loading_klass_list.shift();
resolve(); }
}; resolve();
newScript.onerror = function (e) { };
if (pop === true) { newScript.onerror = function triggerJSNotLoaded(e) {
// Drop the current loading klass info used by selector if (pop === true) {
gadget_loading_klass_list.shift(); // Drop the current loading klass info used by selector
} gadget_loading_klass_list.shift();
reject(e); }
}; reject(e);
newScript.src = url; };
container.appendChild(newScript); newScript.src = url;
}); container.appendChild(newScript);
}
);
} }
return result; return result;
}; };
...@@ -1825,7 +1816,7 @@ if (typeof document.contains !== 'function') { ...@@ -1825,7 +1816,7 @@ if (typeof document.contains !== 'function') {
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// renderJS.declareCSS // renderJS.declareCSS
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
renderJS.declareCSS = function (url, container) { renderJS.declareCSS = function declareCSS(url, container) {
// https://github.com/furf/jquery-getCSS/blob/master/jquery.getCSS.js // https://github.com/furf/jquery-getCSS/blob/master/jquery.getCSS.js
// No way to cleanly check if a css has been loaded // No way to cleanly check if a css has been loaded
// So, always resolve the promise... // So, always resolve the promise...
...@@ -1834,19 +1825,17 @@ if (typeof document.contains !== 'function') { ...@@ -1834,19 +1825,17 @@ if (typeof document.contains !== 'function') {
if (stylesheet_registration_dict.hasOwnProperty(url)) { if (stylesheet_registration_dict.hasOwnProperty(url)) {
result = RSVP.resolve(); result = RSVP.resolve();
} else { } else {
result = new RSVP.Promise(function (resolve, reject) { result = new RSVP.Promise(function waitForCSSLoadEvent(resolve, reject) {
var link; var link;
link = document.createElement('link'); link = document.createElement('link');
link.rel = 'stylesheet'; link.rel = 'stylesheet';
link.type = 'text/css'; link.type = 'text/css';
link.href = url; link.href = url;
link.onload = function () { link.onload = function triggerCSSLoaded() {
stylesheet_registration_dict[url] = null; stylesheet_registration_dict[url] = null;
resolve(); resolve();
}; };
link.onerror = function (e) { link.onerror = reject;
reject(e);
};
container.appendChild(link); container.appendChild(link);
}); });
} }
...@@ -1862,7 +1851,7 @@ if (typeof document.contains !== 'function') { ...@@ -1862,7 +1851,7 @@ if (typeof document.contains !== 'function') {
key, key,
parsed_html; parsed_html;
// Class inheritance // Class inheritance
tmp_constructor = function () { tmp_constructor = function createSuperKlass() {
RenderJSGadget.call(this); RenderJSGadget.call(this);
}; };
tmp_constructor.__ready_list = RenderJSGadget.__ready_list.slice(); tmp_constructor.__ready_list = RenderJSGadget.__ready_list.slice();
...@@ -1908,23 +1897,35 @@ if (typeof document.contains !== 'function') { ...@@ -1908,23 +1897,35 @@ if (typeof document.contains !== 'function') {
return tmp_constructor; return tmp_constructor;
} }
renderJS.declareGadgetKlass = function (url) { renderJS.declareGadgetKlass = function declareGadgetKlass(url) {
var tmp_constructor,
defer;
if (gadget_model_defer_dict.hasOwnProperty(url)) { if (gadget_model_defer_dict.hasOwnProperty(url)) {
// Return klass object if it already exists // Return klass object if it already exists
return gadget_model_defer_dict[url].promise; if (gadget_model_defer_dict[url].hasOwnProperty('defer_list')) {
// Klass not yet loaded.
// Add a new defer
defer = RSVP.defer();
gadget_model_defer_dict[url].defer_list.push(defer);
return defer.promise;
}
if (gadget_model_defer_dict[url].is_resolved) {
return RSVP.resolve(gadget_model_defer_dict[url].result);
}
return RSVP.reject(gadget_model_defer_dict[url].result);
} }
var tmp_constructor, gadget_model_defer_dict[url] = {
defer = RSVP.defer(); defer_list: []
};
gadget_model_defer_dict[url] = defer;
// Fetch the HTML page and parse it // Fetch the HTML page and parse it
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function waitForGadgetKlassAjax() {
return ajax(url); return ajax(url);
}) })
.push(function (result) { .push(function handleGadgetKlassAjax(result) {
tmp_constructor = parse(result, url); tmp_constructor = parse(result, url);
var fragment = document.createDocumentFragment(), var fragment = document.createDocumentFragment(),
promise_list = [], promise_list = [],
...@@ -1946,14 +1947,28 @@ if (typeof document.contains !== 'function') { ...@@ -1946,14 +1947,28 @@ if (typeof document.contains !== 'function') {
document.head.appendChild(fragment); document.head.appendChild(fragment);
return RSVP.all(promise_list); return RSVP.all(promise_list);
}) })
.push(function () { .push(function handleGadgetKlassLoadingSuccess() {
defer.resolve(tmp_constructor); var i,
len = gadget_model_defer_dict[url].defer_list.length;
for (i = 0; i < len; i += 1) {
gadget_model_defer_dict[url].defer_list[i].resolve(tmp_constructor);
}
delete gadget_model_defer_dict[url].defer_list;
gadget_model_defer_dict[url].result = tmp_constructor;
gadget_model_defer_dict[url].is_resolved = true;
return tmp_constructor; return tmp_constructor;
}) })
.push(undefined, function (e) { .push(undefined, function handleGadgetKlassLoadingError(e) {
// Drop the current loading klass info used by selector // Drop the current loading klass info used by selector
// even in case of error // even in case of error
defer.reject(e); var i,
len = gadget_model_defer_dict[url].defer_list.length;
for (i = 0; i < len; i += 1) {
gadget_model_defer_dict[url].defer_list[i].reject(e);
}
delete gadget_model_defer_dict[url].defer_list;
gadget_model_defer_dict[url].result = e;
gadget_model_defer_dict[url].is_resolved = false;
throw e; throw e;
}); });
}; };
...@@ -1962,7 +1977,7 @@ if (typeof document.contains !== 'function') { ...@@ -1962,7 +1977,7 @@ if (typeof document.contains !== 'function') {
// renderJS.clearGadgetKlassList // renderJS.clearGadgetKlassList
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// For test purpose only // For test purpose only
renderJS.clearGadgetKlassList = function () { renderJS.clearGadgetKlassList = function clearGadgetKlassList() {
gadget_model_defer_dict = {}; gadget_model_defer_dict = {};
javascript_registration_dict = {}; javascript_registration_dict = {};
stylesheet_registration_dict = {}; stylesheet_registration_dict = {};
...@@ -1971,53 +1986,54 @@ if (typeof document.contains !== 'function') { ...@@ -1971,53 +1986,54 @@ if (typeof document.contains !== 'function') {
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// renderJS.parseGadgetHTMLDocument // renderJS.parseGadgetHTMLDocument
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
renderJS.parseGadgetHTMLDocument = function (document_element, url) { renderJS.parseGadgetHTMLDocument =
var settings = { function parseGadgetHTMLDocument(document_element, url) {
title: "", var settings = {
interface_list: [], title: "",
required_css_list: [], interface_list: [],
required_js_list: [] required_css_list: [],
}, required_js_list: []
i, },
element; i,
element;
if (!url || !isAbsoluteOrDataURL.test(url)) {
throw new Error("The url should be absolute: " + url); if (!url || !isAbsoluteOrDataURL.test(url)) {
} throw new Error("The url should be absolute: " + url);
}
if (document_element.nodeType === 9) { if (document_element.nodeType === 9) {
settings.title = document_element.title; settings.title = document_element.title;
if (document_element.head !== null) { if (document_element.head !== null) {
for (i = 0; i < document_element.head.children.length; i += 1) { for (i = 0; i < document_element.head.children.length; i += 1) {
element = document_element.head.children[i]; element = document_element.head.children[i];
if (element.href !== null) { if (element.href !== null) {
// XXX Manage relative URL during extraction of URLs // XXX Manage relative URL during extraction of URLs
// element.href returns absolute URL in firefox but "" in chrome; // element.href returns absolute URL in firefox but "" in chrome;
if (element.rel === "stylesheet") { if (element.rel === "stylesheet") {
settings.required_css_list.push( settings.required_css_list.push(
renderJS.getAbsoluteURL(element.getAttribute("href"), url) renderJS.getAbsoluteURL(element.getAttribute("href"), url)
); );
} else if (element.nodeName === "SCRIPT" && } else if (element.nodeName === "SCRIPT" &&
(element.type === "text/javascript" || (element.type === "text/javascript" ||
!element.type)) { !element.type)) {
settings.required_js_list.push( settings.required_js_list.push(
renderJS.getAbsoluteURL(element.getAttribute("src"), url) renderJS.getAbsoluteURL(element.getAttribute("src"), url)
); );
} else if (element.rel === } else if (element.rel ===
"http://www.renderjs.org/rel/interface") { "http://www.renderjs.org/rel/interface") {
settings.interface_list.push( settings.interface_list.push(
renderJS.getAbsoluteURL(element.getAttribute("href"), url) renderJS.getAbsoluteURL(element.getAttribute("href"), url)
); );
}
} }
} }
} }
} else {
throw new Error("The first parameter should be an HTMLDocument");
} }
} else { return settings;
throw new Error("The first parameter should be an HTMLDocument"); };
}
return settings;
};
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// global // global
...@@ -2037,7 +2053,7 @@ if (typeof document.contains !== 'function') { ...@@ -2037,7 +2053,7 @@ if (typeof document.contains !== 'function') {
// is triggered before everything was ready. // is triggered before everything was ready.
// (For instance, the HTML-tag for the self gadget gets inserted after // (For instance, the HTML-tag for the self gadget gets inserted after
// page load) // page load)
renderJS.manualBootstrap = function () { renderJS.manualBootstrap = function manualBootstrap() {
all_dependency_loaded_deferred.resolve(); all_dependency_loaded_deferred.resolve();
}; };
document.addEventListener('DOMContentLoaded', document.addEventListener('DOMContentLoaded',
...@@ -2065,7 +2081,7 @@ if (typeof document.contains !== 'function') { ...@@ -2065,7 +2081,7 @@ if (typeof document.contains !== 'function') {
TmpConstructor.__template_element.appendChild(fragment); TmpConstructor.__template_element.appendChild(fragment);
return RSVP.all([root_gadget.getRequiredJSList(), return RSVP.all([root_gadget.getRequiredJSList(),
root_gadget.getRequiredCSSList()]) root_gadget.getRequiredCSSList()])
.then(function (all_list) { .then(function handleRequireDependencyList(all_list) {
var i, var i,
js_list = all_list[0], js_list = all_list[0],
css_list = all_list[1]; css_list = all_list[1];
...@@ -2076,14 +2092,14 @@ if (typeof document.contains !== 'function') { ...@@ -2076,14 +2092,14 @@ if (typeof document.contains !== 'function') {
stylesheet_registration_dict[css_list[i]] = null; stylesheet_registration_dict[css_list[i]] = null;
} }
gadget_loading_klass_list.shift(); gadget_loading_klass_list.shift();
}).then(function () { }).then(function createMutationObserver() {
// select the target node // select the target node
var target = document.querySelector('body'), var target = document.querySelector('body'),
// create an observer instance // create an observer instance
observer = new MutationObserver(function (mutations) { observer = new MutationObserver(function observeMutatios(mutations) {
var i, k, len, len2, node, added_list; var i, k, len, len2, node, added_list;
mutations.forEach(function (mutation) { mutations.forEach(function observerMutation(mutation) {
if (mutation.type === 'childList') { if (mutation.type === 'childList') {
len = mutation.removedNodes.length; len = mutation.removedNodes.length;
...@@ -2092,7 +2108,7 @@ if (typeof document.contains !== 'function') { ...@@ -2092,7 +2108,7 @@ if (typeof document.contains !== 'function') {
if (node.nodeType === Node.ELEMENT_NODE) { if (node.nodeType === Node.ELEMENT_NODE) {
if (node.hasAttribute("data-gadget-url") && if (node.hasAttribute("data-gadget-url") &&
(node._gadget !== undefined)) { (node._gadget !== undefined)) {
createMonitor(node._gadget); createGadgetMonitor(node._gadget);
} }
added_list = added_list =
node.querySelectorAll("[data-gadget-url]"); node.querySelectorAll("[data-gadget-url]");
...@@ -2100,7 +2116,7 @@ if (typeof document.contains !== 'function') { ...@@ -2100,7 +2116,7 @@ if (typeof document.contains !== 'function') {
for (k = 0; k < len2; k += 1) { for (k = 0; k < len2; k += 1) {
node = added_list[k]; node = added_list[k];
if (node._gadget !== undefined) { if (node._gadget !== undefined) {
createMonitor(node._gadget); createGadgetMonitor(node._gadget);
} }
} }
} }
...@@ -2151,13 +2167,13 @@ if (typeof document.contains !== 'function') { ...@@ -2151,13 +2167,13 @@ if (typeof document.contains !== 'function') {
function createLastAcquisitionGadget() { function createLastAcquisitionGadget() {
var last_acquisition_gadget = new RenderJSGadget(); var last_acquisition_gadget = new RenderJSGadget();
last_acquisition_gadget.__acquired_method_dict = { last_acquisition_gadget.__acquired_method_dict = {
reportServiceError: function (param_list) { reportServiceError: function reportServiceError(param_list) {
letsCrash(param_list[0]); letsCrash(param_list[0]);
} }
}; };
// Stop acquisition on the last acquisition gadget // Stop acquisition on the last acquisition gadget
// Do not put this on the klass, as their could be multiple instances // Do not put this on the klass, as their could be multiple instances
last_acquisition_gadget.__aq_parent = function (method_name) { last_acquisition_gadget.__aq_parent = function __aq_parent(method_name) {
throw new renderJS.AcquisitionError( throw new renderJS.AcquisitionError(
"No gadget provides " + method_name "No gadget provides " + method_name
); );
...@@ -2205,7 +2221,7 @@ if (typeof document.contains !== 'function') { ...@@ -2205,7 +2221,7 @@ if (typeof document.contains !== 'function') {
]; ];
// Inform parent gadget about declareMethod calls here. // Inform parent gadget about declareMethod calls here.
notifyDeclareMethod = function (name) { notifyDeclareMethod = function notifyDeclareMethod(name) {
declare_method_list_waiting.push(name); declare_method_list_waiting.push(name);
}; };
...@@ -2218,13 +2234,13 @@ if (typeof document.contains !== 'function') { ...@@ -2218,13 +2234,13 @@ if (typeof document.contains !== 'function') {
loading_result = RSVP.any([ loading_result = RSVP.any([
channel_defer.promise, channel_defer.promise,
new RSVP.Queue() new RSVP.Queue()
.push(function () { .push(function waitForParentChannelCreation() {
// Expect the channel to parent to be usable after 1 second // Expect the channel to parent to be usable after 1 second
// If not, consider the gadget as the root // If not, consider the gadget as the root
// Drop all iframe channel communication // Drop all iframe channel communication
return RSVP.delay(1000); return RSVP.delay(1000);
}) })
.push(function () { .push(function handleParentChannelCreation() {
real_result_list[2] = undefined; real_result_list[2] = undefined;
return real_result_list; return real_result_list;
}) })
...@@ -2234,20 +2250,22 @@ if (typeof document.contains !== 'function') { ...@@ -2234,20 +2250,22 @@ if (typeof document.contains !== 'function') {
window: window.parent, window: window.parent,
origin: "*", origin: "*",
scope: "renderJS", scope: "renderJS",
onReady: function () { onReady: function onChannelReady() {
var k, var k,
len; len;
// Channel is ready, so now declare all methods // Channel is ready, so now declare all methods
notifyDeclareMethod = function (name) { notifyDeclareMethod = function notifyDeclareMethod(name) {
declare_method_list_waiting.push( declare_method_list_waiting.push(
new RSVP.Promise(function (resolve, reject) { new RSVP.Promise(
embedded_channel.call({ function promiseChannelDeclareMethodCall(resolve, reject) {
method: "declareMethod", embedded_channel.call({
params: name, method: "declareMethod",
success: resolve, params: name,
error: reject success: resolve,
}); error: reject
}) });
}
)
); );
}; };
...@@ -2263,7 +2281,7 @@ if (typeof document.contains !== 'function') { ...@@ -2263,7 +2281,7 @@ if (typeof document.contains !== 'function') {
} }
// Surcharge declareMethod to inform parent window // Surcharge declareMethod to inform parent window
TmpConstructor.declareMethod = function (name, callback) { TmpConstructor.declareMethod = function declareMethod(name, callback) {
var result = RenderJSGadget.declareMethod.apply( var result = RenderJSGadget.declareMethod.apply(
this, this,
[name, callback] [name, callback]
...@@ -2300,11 +2318,11 @@ if (typeof document.contains !== 'function') { ...@@ -2300,11 +2318,11 @@ if (typeof document.contains !== 'function') {
return root_gadget; return root_gadget;
} }
function ready_executable_wrapper(fct) { function ready_executable_wrapper(fct) {
return function (g) { return function wrapReadyFunction(g) {
return fct.call(g, g); return fct.call(g, g);
}; };
} }
TmpConstructor.ready(function () { TmpConstructor.ready(function startServiceInReady() {
return startService(this); return startService(this);
}); });
...@@ -2323,35 +2341,32 @@ if (typeof document.contains !== 'function') { ...@@ -2323,35 +2341,32 @@ if (typeof document.contains !== 'function') {
embedded_channel) { embedded_channel) {
// Define __aq_parent to inform parent window // Define __aq_parent to inform parent window
root_gadget.__aq_parent = root_gadget.__aq_parent =
TmpConstructor.prototype.__aq_parent = function (method_name, TmpConstructor.prototype.__aq_parent = function aq_parent(method_name,
argument_list, argument_list,
time_out) { time_out) {
return new RSVP.Promise(function (resolve, reject) { return new RSVP.Promise(
embedded_channel.call({ function waitForChannelAcquire(resolve, reject) {
method: "acquire", embedded_channel.call({
params: [ method: "acquire",
method_name, params: [
argument_list method_name,
], argument_list
success: function (s) { ],
resolve(s); success: resolve,
}, error: reject,
error: function (e) { timeout: time_out
reject(e); });
}, }
timeout: time_out );
});
});
}; };
// bind calls to renderJS method on the instance // bind calls to renderJS method on the instance
embedded_channel.bind("methodCall", function (trans, v) { embedded_channel.bind("methodCall", function methodCall(trans, v) {
root_gadget[v[0]].apply(root_gadget, v[1]) root_gadget[v[0]].apply(root_gadget, v[1])
.push(function (g) { .push(trans.complete,
trans.complete(g); function handleMethodCallError(e) {
}, function (e) { trans.error(e.toString());
trans.error(e.toString()); });
});
trans.delayReturn(true); trans.delayReturn(true);
}); });
} }
...@@ -2365,11 +2380,11 @@ if (typeof document.contains !== 'function') { ...@@ -2365,11 +2380,11 @@ if (typeof document.contains !== 'function') {
declare_method_list_waiting; declare_method_list_waiting;
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function waitForLoadingGadget() {
// Wait for the loading gadget to be created // Wait for the loading gadget to be created
return wait_for_gadget_loaded; return wait_for_gadget_loaded;
}) })
.push(function (result_list) { .push(function handleLoadingGadget(result_list) {
TmpConstructor = result_list[0]; TmpConstructor = result_list[0];
root_gadget = result_list[1]; root_gadget = result_list[1];
embedded_channel = result_list[2]; embedded_channel = result_list[2];
...@@ -2377,11 +2392,11 @@ if (typeof document.contains !== 'function') { ...@@ -2377,11 +2392,11 @@ if (typeof document.contains !== 'function') {
// Wait for all the gadget dependencies to be loaded // Wait for all the gadget dependencies to be loaded
return all_dependency_loaded_deferred.promise; return all_dependency_loaded_deferred.promise;
}) })
.push(function () { .push(function waitForDeclareMethodList() {
// Wait for all methods to be correctly declared // Wait for all methods to be correctly declared
return RSVP.all(declare_method_list_waiting); return RSVP.all(declare_method_list_waiting);
}) })
.push(function (result_list) { .push(function waitForMutationObserver(result_list) {
if (embedded_channel !== undefined) { if (embedded_channel !== undefined) {
finishAqParentConfiguration(TmpConstructor, root_gadget, finishAqParentConfiguration(TmpConstructor, root_gadget,
embedded_channel); embedded_channel);
...@@ -2389,16 +2404,16 @@ if (typeof document.contains !== 'function') { ...@@ -2389,16 +2404,16 @@ if (typeof document.contains !== 'function') {
// Check all DOM modifications to correctly start/stop services // Check all DOM modifications to correctly start/stop services
return configureMutationObserver(TmpConstructor, url, root_gadget); return configureMutationObserver(TmpConstructor, url, root_gadget);
}) })
.push(function () { .push(function waitForReadyList() {
// Trigger all ready functions // Trigger all ready functions
return triggerReadyList(TmpConstructor, root_gadget); return triggerReadyList(TmpConstructor, root_gadget);
}) })
.push(function () { .push(function notifyReady() {
if (embedded_channel !== undefined) { if (embedded_channel !== undefined) {
embedded_channel.notify({method: "ready"}); embedded_channel.notify({method: "ready"});
} }
}) })
.push(undefined, function (e) { .push(undefined, function handleBootstrapError(e) {
letsCrash(e); letsCrash(e);
if (embedded_channel !== undefined) { if (embedded_channel !== undefined) {
embedded_channel.notify({method: "failed", params: e.toString()}); embedded_channel.notify({method: "failed", params: e.toString()});
...@@ -2412,4 +2427,4 @@ if (typeof document.contains !== 'function') { ...@@ -2412,4 +2427,4 @@ if (typeof document.contains !== 'function') {
); );
}(document, window, RSVP, DOMParser, Channel, MutationObserver, Node, }(document, window, RSVP, DOMParser, Channel, MutationObserver, Node,
FileReader, Blob, navigator, Event, URL)); FileReader, Blob, navigator, Event, URL));
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment