Commit b54b55dd authored by Romain Courteaud's avatar Romain Courteaud

[erp5_xhtml_style] Do not crash the UI in case of gadget error

parent 48b1447b
/*global window, rJS, RSVP, document*/ /*global window, rJS, RSVP, document, console*/
/*jslint nomen: true, maxlen:80, indent:2*/ /*jslint nomen: true, maxlen:80, indent:2*/
(function (window, document, rJS, RSVP) { (function (window, rJS, RSVP, document, console) {
"use strict"; "use strict";
function promiseEventListener(target, type, useCapture) { function promiseEventListener(target, type, useCapture) {
////////////////////////// //////////////////////////
// Resolve the promise as soon as the event is triggered // Resolve the promise as soon as the event is triggered
...@@ -27,143 +28,167 @@ ...@@ -27,143 +28,167 @@
return new RSVP.Promise(resolver, canceller); return new RSVP.Promise(resolver, canceller);
} }
function displayFieldError(error) {
console.warn(error);
// Display the error message in the portal_status location
// As renderJS does not report which element is failing while loading
// a gadget
var error_element = document.getElementById('transition_message');
error_element.textContent = error + '. ' + error_element.textContent;
}
function getGadgetContent(gadget) {
return gadget.getContent()
.push(undefined, function (error) {
// Do not crash if gadget getContent is wrongly implemented,
// ie, UI should work even if one gadget does not
displayFieldError(error);
return {};
});
}
rJS(window) rJS(window)
///////////////////////////////////////////////////////////////// .setState({
// ready rejected_dict: {},
///////////////////////////////////////////////////////////////// field_list: [],
// Init local properties gadget_list: []
.ready(function (g) {
g.props = {};
}) })
// Assign the element to a variable .allowPublicAcquisition('reportGadgetDeclarationError',
.ready(function (g) { function (argument_list, scope) {
return g.getElement() // Do not crash the UI in case of wrongly configured gadget,
.push(function (element) { // bad network, loading bug.
g.props.element = element; this.state.rejected_dict[scope] = null;
}); return displayFieldError(argument_list[0]);
}) })
.allowPublicAcquisition('reportServiceError',
function (argument_list) {
// Do not crash the UI in case of gadget service error.
return displayFieldError(argument_list[0]);
})
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// declared methods // declared methods
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
.declareService(function () { .declareService(function () {
var g = this, // Call render on all gadget fields
i, var gadget = this,
list_gadget = document.querySelectorAll("[data-gadget-url]"), field_list = [],
all_gadget, i;
list = [],
gadget_attributes = [],
url,
form = g.props.element.querySelector("form"),
scope,
value,
key,
tmp;
for (i = 0; i < list_gadget.length; i += 1) {
url = list_gadget[i].getAttribute("data-gadget-url");
key = list_gadget[i].getAttribute("data-gadget-editable");
value = list_gadget[i].getAttribute("data-gadget-value");
//renderable
if (url !== undefined && url !== null) {
tmp = {};
scope = list_gadget[i].getAttribute("data-gadget-scope");
list.push(g.getDeclaredGadget(scope));
tmp.sandbox = list_gadget[i].getAttribute("data-gadget-sandbox");
tmp.editable = key;
tmp.key = key;
tmp.value = value;
gadget_attributes.push(tmp);
}
}
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function () {
return RSVP.all(list); var field_element_list =
}) gadget.element.querySelectorAll("[data-gadget-url]"),
.push(function (results) { field_element,
all_gadget = results; field_scope,
list = []; field_url,
for (i = 0; i < gadget_attributes.length; i += 1) { promise_list = [];
if (gadget_attributes[i].sandbox === "iframe") {
list.push(all_gadget[i].getElement()); for (i = 0; i < field_element_list.length; i += 1) {
field_element = field_element_list[i];
field_url = field_element.getAttribute("data-gadget-url");
field_scope = field_element.getAttribute("data-gadget-scope");
// Renderable
if ((field_url !== undefined) && (field_url !== null) &&
(field_scope !== null) &&
(!gadget.state.rejected_dict.hasOwnProperty(field_scope))) {
field_list.push({
sandbox: field_element.getAttribute("data-gadget-sandbox"),
editable: field_element.getAttribute("data-gadget-editable"),
key: field_element.getAttribute("data-gadget-editable"),
value: field_element.getAttribute("data-gadget-value")
});
promise_list.push(gadget.getDeclaredGadget(field_scope));
} }
} }
return RSVP.all(list); gadget.state.field_list = field_list;
return RSVP.all(promise_list);
}) })
.push(function (elements) { .push(function (result_list) {
gadget.state.gadget_list = result_list;
var iframe, var iframe,
j, sub_element,
sub_value, sub_value,
sub_key; sub_key,
list = []; promise_list = [];
for (i = 0, j = 0; i < gadget_attributes.length; i += 1) { for (i = 0; i < field_list.length; i += 1) {
if (all_gadget[i].render !== undefined) { if (result_list[i].render !== undefined) {
sub_value = gadget_attributes[i].value; sub_value = field_list[i].value;
sub_key = gadget_attributes[i].key; sub_key = field_list[i].key;
list.push( promise_list.push(
all_gadget[i].render( result_list[i].render({key: sub_key, value: sub_value})
{ .push(undefined, displayFieldError)
"key": sub_key, /* XXX Highlight the gadget element with a small colored
"value": sub_value * error message. Clicking on the element could unroll
} * more information like the traceback. */
).push(undefined, function (error) {
/* TODO: Highlight the gadget element with a small colored
* error message. Clicking on the element could unroll
* more information like the traceback. */
console.log(error);
})
); );
} }
if (gadget_attributes[i].sandbox === "iframe") { if (field_list[i].sandbox === "iframe") {
iframe = elements[j].querySelector('iframe'); sub_element = result_list[i].element;
iframe = sub_element.querySelector('iframe');
//xxx input field //xxx input field
elements[j].parentNode.style.width = "100%"; sub_element.parentNode.style.width = "100%";
elements[j].parentNode.style.height = "100%"; sub_element.parentNode.style.height = "100%";
//xxx section div //xxx section div
elements[j].style.width = "100%"; sub_element.style.width = "100%";
elements[j].style.height = "100%"; sub_element.style.height = "100%";
iframe.style.width = "100%"; iframe.style.width = "100%";
iframe.style.height = "100%"; iframe.style.height = "100%";
iframe.allowFullscreen = true; iframe.allowFullscreen = true;
j += 1;
} }
} }
return RSVP.all(list); return RSVP.all(promise_list);
}) });
})
.declareService(function () {
/*Do not use ajax call but submit an hidden form.
So in this way, we can use form submit mecanisme
provided by browser.
if use ajax, we should get the return page manually
which is difficult.
The new hidden fields have been added with the
gadget values and the submit button which
has been activated (relation field image, save button, etc).
This is done by listening the "click" event on the
submit/image button.
After all, submit the form manually again.
*/
var context = this,
form = this.element.querySelector("form");
return new RSVP.Queue()
.push(function () { .push(function () {
/*Do not use ajax call but submit an hidden form. var image_list = context.element
So in this way, we can use form submit mecanisme .querySelectorAll("input[type='image']"),
provided by browser. submit_list = context.element
if use ajax, we should get the return page manually .querySelectorAll("button[type='submit']"),
which is difficult. i,
The new hidden fields have been added with the promise_list = [];
gadget values and the submit button which
has been activated (relation field image, save button, etc). promise_list.push(promiseEventListener(context.element, "submit",
This is done by listening the "click" event on the false));
submit/image button. for (i = 0; i < image_list.length; i += 1) {
After all, submit the form manually again. promise_list.push(promiseEventListener(image_list[i], "click",
*/ false));
var input_images = }
g.props.element.querySelectorAll("input[type='image']"), for (i = 0; i < submit_list.length; i += 1) {
input_submits = promise_list.push(promiseEventListener(submit_list[i], "click",
g.props.element.querySelectorAll("button[type='submit']"); false));
list = [];
if (input_images.length || input_submits.length) {
list.push(promiseEventListener(g.props.element, "submit", false));
for (i = 0; i < input_images.length; i += 1) {
list.push(promiseEventListener(input_images[i], "click", false));
}
for (i = 0; i < input_submits.length; i += 1) {
list.push(promiseEventListener(input_submits[i], "click", false));
}
return RSVP.any(list);
} }
return promiseEventListener(g.props.element, "submit", false); return RSVP.any(promise_list);
}) })
.push(function (evt) { .push(function (evt) {
var input, var input,
hidden_button, hidden_button,
target; target,
list = []; i,
promise_list = [];
if (evt.type === "click") { if (evt.type === "click") {
input = document.createElement("input"); input = document.createElement("input");
input.setAttribute("type", "hidden"); input.setAttribute("type", "hidden");
...@@ -171,34 +196,34 @@ ...@@ -171,34 +196,34 @@
input.setAttribute("name", target.getAttribute("name")); input.setAttribute("name", target.getAttribute("name"));
form.appendChild(input); form.appendChild(input);
} else { } else {
hidden_button = g.props.element.querySelector(".hidden_button"); hidden_button = context.element.querySelector(".hidden_button");
hidden_button.setAttribute("type", "hidden"); hidden_button.setAttribute("type", "hidden");
} }
for (i = 0; i < all_gadget.length; i += 1) { for (i = 0; i < context.state.gadget_list.length; i += 1) {
if (all_gadget[i].getContent !== undefined && if (context.state.gadget_list[i].getContent !== undefined &&
gadget_attributes[i].editable !== null) { context.state.field_list[i].editable !== null) {
list.push(all_gadget[i].getContent()); promise_list.push(getGadgetContent(context.state.gadget_list[i]));
} }
} }
return RSVP.all(list); return RSVP.all(promise_list);
}) })
.push(function (all_content) { .push(function (content_list) {
var input, var input,
i,
name; name;
for (i = 0; i < all_content.length; i += 1) { for (i = 0; i < content_list.length; i += 1) {
for (name in all_content[i]) { for (name in content_list[i]) {
if (all_content[i].hasOwnProperty(name)) { if (content_list[i].hasOwnProperty(name)) {
input = document.createElement("input"); input = document.createElement("input");
input.setAttribute("type", "hidden"); input.setAttribute("type", "hidden");
input.setAttribute("name", name); input.setAttribute("name", name);
input.setAttribute("value", all_content[i][name]); input.setAttribute("value", content_list[i][name]);
form.appendChild(input); form.appendChild(input);
} }
} }
} }
}) return form.submit();
.push(function () {
form.submit();
}); });
}); });
}(window, document, rJS, RSVP));
\ No newline at end of file }(window, rJS, RSVP, document, console));
\ 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