Commit 19028f05 by Tomáš Peterka Committed by Tomáš Peterka

[renderjs_ui] Add FormBox implementation

parent 6a893b8d
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, user-scalable=no" />
<title>ERP5 FormBox Field</title>
<!-- renderjs -->
<script src="rsvp.js" type="text/javascript"></script>
<script src="renderjs.js" type="text/javascript"></script>
<!-- custom script -->
<script src="gadget_erp5_field_formbox.js" type="text/javascript"></script>
</head>
<body>
</body>
</html>
\ No newline at end of file
/*global window, rJS, URI */
/*jslint nomen: true, indent: 2, maxerr: 3 */
/**
* FormBox displays embedded form in itself.
*
* There are two common CSS classes bound to the FormBox Field
* - "horizontal_align_form_box" renders fields without labels in horizontal manner (useful for phone etc.)
* - "invisible" despite its name should only hide label
*
*/
(function (window, rJS, URI) {
"use strict";
rJS(window)
.setState({
subgadget_template: undefined,
editable: undefined,
css_class: ''
})
.declareAcquiredMethod("jio_getAttachment", "jio_getAttachment")
.declareMethod('render', function (options) {
var element = this.element,
gadget = this,
field_json = options.field_json || {},
new_state = {
value: field_json.value || field_json.default || "",
text_content: field_json.value || field_json.default || "",
editable: field_json.editable,
required: field_json.required,
name: field_json.key,
title: field_json.title,
hidden: field_json.hidden,
view: field_json.view,
css_class: field_json.css_class,
// field_json._embedded is HATEOASed subobj specs included in FormBox
erp5_embedded_document: field_json._embedded
};
// prefer editability from the global context (form)
if (options.editable !== undefined) {
new_state.editable = options.editable;
}
if (gadget.state.subgadget_template === undefined || options.reset === true) {
// render subgadget only when there is none OR render is explicitely requested
return gadget.declareGadget('gadget_erp5_page_form.html', {scope: 'sub'})
.push(function (form_gadget) {
// Clear first to DOM, append after to reduce flickering/manip
while (element.firstChild) {
element.removeChild(element.firstChild);
}
element.appendChild(form_gadget.element);
// Add newly created subgadget to the state
new_state.subgadget_template = 'gadget_erp5_page_form.html';
return gadget.changeState(new_state);
});
}
return gadget.changeState(new_state);
})
.onStateChange(function (modification_dict) {
var gadget = this,
erp5_document_uri = new URI(gadget.state.erp5_embedded_document._view._links.traversed_document.href),
form_options = {
erp5_document: {
_embedded: gadget.state.erp5_embedded_document
},
key: gadget.state.name,
view: gadget.state.view,
jio_key: erp5_document_uri.segment(2),
editable: gadget.state.editable
};
// do not preserve objects in the state
delete gadget.state.erp5_embedded_document;
// pass CSS class to the DIV element
if (modification_dict.hasOwnProperty('css_class')) {
gadget.element.classList.add(modification_dict.css_class);
}
return gadget.getDeclaredGadget('sub')
.push(function (subgadget) {
subgadget.render(form_options);
});
})
.declareMethod('getContent', function () {
if (this.state.editable) {
return this.getDeclaredGadget('sub')
.push(function (gadget) {
return gadget.getContent();
});
}
return {};
})
.declareMethod('checkValidity', function () {
if (this.state.editable) {
return this.getDeclaredGadget('sub')
.push(function (gadget) {
return gadget.checkValidity();
});
}
return true;
});
}(window, rJS, URI));
\ No newline at end of file
......@@ -230,7 +230,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>955.49459.46929.49339</string> </value>
<value> <string>961.18982.46006.23483</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -248,7 +248,7 @@
</tuple>
<state>
<tuple>
<float>1480936090.54</float>
<float>1502268077.5</float>
<string>UTC</string>
</tuple>
</state>
......
/*global window, document, rJS, RSVP */
/*jslint indent: 2, maxerr: 3 */
/**
* Label gadget takes care of displaying validation errors and label.
*
* Every form field is wrapped in that widget which has some consequences:
*
* - CSS classes sharing: label copy CSS classes of embedded field for itself
* because CSS selectors are not good in selectin up the DOM tree
* - class "invisible" despite its name is supposed to hide only label
* - class "horizontal_align_form_box" will prevent any label to show as well
*
*/
(function (window, document, rJS, RSVP) {
"use strict";
......@@ -9,8 +20,10 @@
.setState({
label_text: '',
error_text: '',
label: true
label: true,
css_class: ''
})
.ready(function () {
return this.changeState({
label_element: this.element.querySelector('label'),
......@@ -28,7 +41,8 @@
error_text: options.field_json.error_text || '',
options: options,
scope: options.field_json.key,
hidden: options.field_json.hidden
hidden: options.field_json.hidden,
css_class: options.field_json.css_class
};
return this.changeState(state_dict);
})
......@@ -48,6 +62,10 @@
}
this.state.label_element.setAttribute('for', gadget.state.scope);
if (modification_dict.hasOwnProperty('css_class') && this.state.css_class) {
this.state.label_element.classList.add(this.state.css_class);
}
if (modification_dict.hasOwnProperty('error_text')) {
this.state.error_element.textContent = "";
if (this.state.error_text) {
......
......@@ -230,7 +230,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>956.17308.23196.16947</string> </value>
<value> <string>961.17833.11141.14523</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -248,7 +248,7 @@
</tuple>
<state>
<tuple>
<float>1482938684.96</float>
<float>1502198959.61</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -918,6 +918,9 @@ div[data-gadget-scope='header'] .ui-header ul {
.gadget-content {
padding: 24pt;
padding-top: 66pt;
/* Disclaimer: It is here only for backward-compatibility! Don't rely on
this class because it will be removed.
It was used to force horizontal rendering of fields inside FormBox. */
/*
@media @smartphone {
.ui-field-contain {
......@@ -974,6 +977,9 @@ div[data-gadget-scope='header'] .ui-header ul {
.gadget-content .ui-field-contain div {
width: 100%;
}
.gadget-content .horizontal_align_form_box .ui-field-contain > div {
display: block-inline;
}
.gadget-content .ui-content-header-plain {
font-size: 150%;
}
......@@ -1047,6 +1053,12 @@ div[data-gadget-scope='header'] .ui-header ul {
.gadget-content .ui-field-contain > label {
flex: 1;
color: #777777;
/* Disclaimer: It is here only for backward-compatibility! Don't rely on
this class because it will be removed.
It was used to hide the label of a FormBox. */
}
.gadget-content .ui-field-contain > label.invisible {
display: none;
}
.gadget-content .ui-field-contain > label + div {
flex: 3;
......
......@@ -242,7 +242,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>961.19028.47293.9045</string> </value>
<value> <string>961.19210.8471.60620</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -260,7 +260,7 @@
</tuple>
<state>
<tuple>
<float>1502271576.03</float>
<float>1502444264.19</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -85,7 +85,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>rjs_gadget_erp5_pt_formpage_html</string> </value>
<value> <string>rjs_gadget_erp5_page_form_html</string> </value>
</item>
<item>
<key> <string>language</string> </key>
......@@ -103,7 +103,7 @@
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Gadget ERP5 Doc</string> </value>
<value> <string>Gadget ERP5 Form Page</string> </value>
</item>
<item>
<key> <string>version</string> </key>
......@@ -234,7 +234,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>952.64761.25287.18397</string> </value>
<value> <string>960.5523.58984.43537</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -252,7 +252,7 @@
</tuple>
<state>
<tuple>
<float>1475148161.21</float>
<float>1501681620.31</float>
<string>UTC</string>
</tuple>
</state>
......
/*global window, rJS, URI */
/*global window, rJS, URI, RSVP */
/*jslint nomen: true, indent: 2, maxerr: 3 */
(function (window, rJS, URI) {
(function (window, rJS, URI, RSVP) {
"use strict";
function loadFormContent(gadget, result) {
......@@ -28,7 +28,29 @@
.declareAcquiredMethod("updatePanel", "updatePanel")
/////////////////////////////////////////////////////////////////
// declared methods
// Proxy methods to the child gadget
/////////////////////////////////////////////////////////////////
.declareMethod('triggerSubmit', function () {
return this.getDeclaredGadget('fg')
.push(function (g) {
return g.triggerSubmit();
});
})
.declareMethod('checkValidity', function () {
return this.getDeclaredGadget('fg')
.push(function (declared_gadget) {
return declared_gadget.checkValidity();
});
})
.declareMethod('getContent', function () {
return this.getDeclaredGadget('fg')
.push(function (declared_gadget) {
return declared_gadget.getContent();
});
})
/////////////////////////////////////////////////////////////////
// Own methods
/////////////////////////////////////////////////////////////////
.allowPublicAcquisition("jio_allDocs", function (param_list) {
var gadget = this;
......@@ -46,54 +68,79 @@
.allowPublicAcquisition('notifySubmit', function () {
return this.triggerSubmit();
})
.declareMethod('triggerSubmit', function () {
return this.getDeclaredGadget('fg')
.push(function (g) {
return g.triggerSubmit();
});
})
.declareMethod("render", function (options) {
var gadget = this;
return gadget.jio_getAttachment(options.jio_key, options.view)
.push(function (result) {
var uri;
if (!result._embedded) {
return gadget.jio_getAttachment(options.jio_key, "links")
.push(function (result2) {
return gadget.redirect({command: 'change', options: {
view: result2._links.view[0].href,
page: undefined
}});
});
var gadget = this,
promise_queue = new RSVP.Queue(),
new_state = {
options: options,
erp5_document: undefined,
erp5_form: undefined,
url: undefined
};
if (options.hasOwnProperty('erp5_document')) {
// if we get erp5 document during rendering then no need to fetch it
new_state.erp5_document = options.erp5_document;
// remove reference to erp5_document from options (and new_state.options)
delete options.erp5_document;
} else {
promise_queue
.push(function () {
return gadget.jio_getAttachment(options.jio_key, options.view);
})
.push(function (result) {
new_state.erp5_document = result;
if (!result._embedded) {
return gadget.jio_getAttachment(options.jio_key, "links")
.push(function (result2) {
return gadget.redirect({command: 'change', options: {
view: result2._links.view[0].href,
page: undefined
}});
});
}
});
}
// options.editable differs when it comes from the erp5_launcher of FormBox - try to unify it here
if (options.editable === "true" || options.editable === true || options.editable === "1" || options.editable === 1) {
options.editable = 1;
} else {
options.editable = 0;
}
return promise_queue
.push(function () {
var uri = new URI(new_state.erp5_document._embedded._view._links.form_definition.href);
return gadget.jio_getAttachment(uri.segment(2), "view");
})
.push(function (erp5_form) {
var url = "gadget_erp5_pt_" + erp5_form.pt;
// XXX Hardcoded specific behaviour for form_view
if ((options.editable === 1) && (erp5_form.pt === "form_view")) {
url += "_editable";
}
url += ".html";
uri = new URI(result._embedded._view._links.form_definition.href);
return gadget.jio_getAttachment(uri.segment(2), "view")
.push(function (erp5_form) {
var url = "gadget_erp5_pt_" + erp5_form.pt;
// XXX Hardcoded specific behaviour for form_view
if ((options.editable !== undefined) && (erp5_form.pt === "form_view")) {
url += "_editable";
}
url += ".html";
return gadget.changeState({
jio_key: options.jio_key,
options: options,
view: options.view,
url: url,
erp5_document: JSON.stringify(result),
erp5_form: JSON.stringify(erp5_form)
});
});
new_state.url = url;
new_state.erp5_form = JSON.stringify(erp5_form);
new_state.erp5_document = JSON.stringify(new_state.erp5_document);
return gadget.changeState(new_state);
});
})
.onStateChange(function (modification_dict) {
var queue,
gadget = this,
options = this.state.options,
page_template_gadget,
clean_dom = modification_dict.hasOwnProperty('url');
// clean_dom === false only in case of displaying errors
clean_dom = modification_dict.hasOwnProperty('url'),
erp5_document = JSON.parse(gadget.state.erp5_document),
erp5_form = JSON.parse(gadget.state.erp5_form);
if (clean_dom) {
queue = gadget.declareGadget(gadget.state.url, {scope: "fg"});
} else {
......@@ -103,9 +150,7 @@
.push(function (result) {
page_template_gadget = result;
var sub_options = options.fg || {},
erp5_document = JSON.parse(gadget.state.erp5_document),
erp5_form = JSON.parse(gadget.state.erp5_form);
var sub_options = options.fg || {};
loadFormContent(gadget, erp5_document._embedded._view);
......@@ -113,11 +158,10 @@
sub_options.form_definition = erp5_form;
sub_options.view = options.view;
sub_options.action_view = options.action_view;
sub_options.jio_key = options.jio_key;
sub_options.editable = options.editable;
sub_options.jio_key = options.jio_key; // jIO identifier of currently rendered ERP5 document
sub_options.editable = options.editable; // form decides on editability of its fields
return page_template_gadget.render(sub_options);
})
.push(function () {
if (clean_dom) {
......@@ -138,7 +182,7 @@
if (/^[^\/]+_module\/.+$/.test(jio_key)) {
/*jslint regexp: false*/
return gadget.updatePanel({
erp5_document: JSON.parse(gadget.state.erp5_document),
erp5_document: erp5_document,
editable: gadget.state.options.editable
});
}
......@@ -153,4 +197,4 @@
return this.changeState({erp5_document: JSON.stringify(erp5_document)});
});
}(window, rJS, URI));
\ No newline at end of file
}(window, rJS, URI, RSVP));
\ No newline at end of file
......@@ -81,7 +81,7 @@
</item>
<item>
<key> <string>id</string> </key>
<value> <string>rjs_gadget_erp5_pt_formpage_js</string> </value>
<value> <string>rjs_gadget_erp5_page_form_js</string> </value>
</item>
<item>
<key> <string>language</string> </key>
......@@ -230,7 +230,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>958.28347.56387.25600</string> </value>
<value> <string>961.16455.47614.10018</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -248,7 +248,7 @@
</tuple>
<state>
<tuple>
<float>1491391860.07</float>
<float>1502198575.84</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -78,7 +78,21 @@
.declareAcquiredMethod("displayFormulatorValidationError",
"displayFormulatorValidationError")
/////////////////////////////////////////////////////////////////
// Proxy methods to the child gadget
/////////////////////////////////////////////////////////////////
.declareMethod('checkValidity', function () {
return this.getDeclaredGadget("erp5_form")
.push(function (declared_gadget) {
return declared_gadget.checkValidity();
});
})
.declareMethod('getContent', function () {
return this.getDeclaredGadget("erp5_form")
.push(function (declared_gadget) {
return declared_gadget.getContent();
});
})
/////////////////////////////////////////////////////////////////
// declared methods
/////////////////////////////////////////////////////////////////
......@@ -88,7 +102,7 @@
.declareMethod('render', function (options) {
var state_dict = {
id: options.jio_key,
jio_key: options.jio_key,
view: options.view,
editable: options.editable,
erp5_document: options.erp5_document,
......@@ -158,6 +172,9 @@
form_options.erp5_document = form_gadget.state.erp5_document;
form_options.form_definition = form_gadget.state.form_definition;
form_options.view = form_gadget.state.view;
form_options.jio_key = form_gadget.state.jio_key;
form_options.editable = form_gadget.state.editable;
return erp5_form.render(form_options);
})
.push(function () {
......@@ -217,7 +234,7 @@
}
return form_gadget.jio_putAttachment(
form_gadget.state.id,
form_gadget.state.jio_key,
action.href,
data
);
......@@ -267,7 +284,7 @@
form_gadget.deferRevokeObjectUrlWithLink(object_url, a);
} else {
jio_key = new URI(location).segment(2);
if (form_gadget.state.id === jio_key) {
if (form_gadget.state.jio_key === jio_key) {
// Do not update navigation history if dialog redirect to the same document
list.push(form_gadget.redirect({command: 'change', options: {jio_key: jio_key, view: "view", page: undefined, editable: form_gadget.state.editable}}));
} else {
......
......@@ -230,7 +230,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>961.657.32982.5239</string> </value>
<value> <string>961.16461.13159.33399</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -248,7 +248,7 @@
</tuple>
<state>
<tuple>
<float>1501168424.28</float>
<float>1502116641.07</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -14,6 +14,21 @@
.declareAcquiredMethod("renderEditorPanel", "renderEditorPanel")
/////////////////////////////////////////////////////////////////
// Proxy methods to the child gadget
/////////////////////////////////////////////////////////////////
.declareMethod('checkValidity', function () {
return this.getDeclaredGadget("erp5_form")
.push(function (declared_gadget) {
return declared_gadget.checkValidity();
});
})
.declareMethod('getContent', function () {
return this.getDeclaredGadget("erp5_form")
.push(function (declared_gadget) {
return declared_gadget.getContent();
});
})
/////////////////////////////////////////////////////////////////
// declared methods
/////////////////////////////////////////////////////////////////
.declareMethod('render', function (options) {
......@@ -21,7 +36,7 @@
return gadget.getUrlParameter('extended_search')
.push(function (extended_search) {
var state_dict = {
id: options.jio_key,
jio_key: options.jio_key,
view: options.view,
editable: options.editable,
erp5_document: options.erp5_document,
......@@ -44,6 +59,8 @@
form_options.erp5_document = form_gadget.state.erp5_document;
form_options.form_definition = form_gadget.state.form_definition;
form_options.view = form_gadget.state.view;
form_options.jio_key = form_gadget.state.jio_key;
form_options.editable = form_gadget.state.editable;
// XXX Hardcoded for listbox's hide functionality
form_options.form_definition.hide_enabled = true;
......
......@@ -230,7 +230,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>954.34605.28227.45721</string> </value>
<value> <string>961.16133.2066.40123</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -248,7 +248,7 @@
</tuple>
<state>
<tuple>
<float>1476198578.1</float>
<float>1502114387.31</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -17,6 +17,21 @@
"displayFormulatorValidationError")
/////////////////////////////////////////////////////////////////
// Proxy methods to the child gadget
/////////////////////////////////////////////////////////////////
.declareMethod('checkValidity', function () {
return this.getDeclaredGadget("erp5_form")
.push(function (declared_gadget) {
return declared_gadget.checkValidity();
});
})
.declareMethod('getContent', function () {
return this.getDeclaredGadget("erp5_form")
.push(function (declared_gadget) {