Commit f7320a4b authored by Romain Courteaud's avatar Romain Courteaud

Implement interaction between the header and the page content.

Move loading notification in the header (drop JQM global notification).
Inform user when some changes were made in the form.
Add form submit notification.
Allow to trigger the form submit from the header button.
Do not submit any form if one gadget field validation constraint fails. Display the error next to the label.
parent 4fd705df
......@@ -102,7 +102,7 @@
<value> <string encoding="cdata"><![CDATA[
CACHE MANIFEST\n
# generated on Mon, 29 Sep 2014 14:23:16 +0000\n
# generated on Thu, 02 Oct 2014 12:06:38 +0000\n
# XXX + fonts\n
# images/ajax-loader.gif\n
CACHE:\n
......@@ -322,7 +322,7 @@ NETWORK:\n
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>937.58588.9950.37290</string> </value>
<value> <string>937.64214.42636.62720</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -340,7 +340,7 @@ NETWORK:\n
</tuple>
<state>
<tuple>
<float>1412000596.45</float>
<float>1412254043.5</float>
<string>GMT</string>
</tuple>
</state>
......
......@@ -120,6 +120,9 @@
);\n
input.setAttribute(\'name\', field_json.key);\n
input.setAttribute(\'title\', field_json.title);\n
if (field_json.required === 1) {\n
input.setAttribute(\'required\', \'required\');\n
}\n
if (field_json.editable !== 1) {\n
input.setAttribute(\'readonly\', \'readonly\');\n
input.setAttribute(\'data-wrapper-class\', \'ui-state-disabled ui-state-readonly\');\n
......@@ -256,7 +259,7 @@
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>sven</string> </value>
<value> <string>romain</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
......@@ -270,7 +273,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>937.48460.49347.43246</string> </value>
<value> <string>937.51520.38687.48861</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -288,7 +291,7 @@
</tuple>
<state>
<tuple>
<float>1411576355.41</float>
<float>1412082125.19</float>
<string>GMT</string>
</tuple>
</state>
......
......@@ -130,6 +130,27 @@
})\n
\n
.declareAcquiredMethod("aq_put", "jio_put")\n
\n
.allowPublicAcquisition("notifyInvalid", function (param_list, scope) {\n
return this.getDeclaredGadget(scope)\n
.push(function (gadget) {\n
return gadget.getElement();\n
})\n
.push(function (gadget_element) {\n
gadget_element.previousElementSibling.querySelector("span").textContent = " (" + param_list[0] + ")";\n
});\n
})\n
\n
.allowPublicAcquisition("notifyValid", function (param_list, scope) {\n
/*jslint unparam:true*/\n
return this.getDeclaredGadget(scope)\n
.push(function (gadget) {\n
return gadget.getElement();\n
})\n
.push(function (gadget_element) {\n
gadget_element.previousElementSibling.querySelector("span").textContent = "";\n
});\n
})\n
/////////////////////////////////////////////////////////////////\n
// declared methods\n
/////////////////////////////////////////////////////////////////\n
......@@ -172,9 +193,11 @@
var field_queue = new RSVP.Queue(),\n
sandbox = "public",\n
field_url = \'gadget_erp5_field_readonly.html\',\n
// Don\'t change the structure without changing notifyValid and notifyInvalid\n
field_element = document.createElement("div"),\n
gadget_element = document.createElement("div"),\n
label_element = document.createElement("label"),\n
error_element = document.createElement("span"),\n
renderered_field = rendered_form[field[0]];\n
\n
field_element.className = "ui-field-contain";\n
......@@ -185,6 +208,8 @@
// field_element.setAttribute(\'data-role\', \'fieldcontain\');\n
label_element.setAttribute(\'for\', renderered_field.key);\n
label_element.textContent = renderered_field.title;\n
// error_element.setAttribute(\'class\', \'ui-state-error ui-corner-all\');\n
label_element.appendChild(error_element);\n
if (group[0] !== "bottom") {\n
field_element.appendChild(label_element);\n
}\n
......@@ -196,6 +221,8 @@
field_url = \'gadget_erp5_field_list.html\';\n
} else if (renderered_field.type === \'StringField\') {\n
field_url = \'gadget_erp5_field_string.html\';\n
} else if (renderered_field.type === \'RelationStringField\') {\n
field_url = \'gadget_erp5_field_relation_string.html\';\n
} else if (renderered_field.type === \'TextAreaField\') {\n
field_url = \'gadget_erp5_field_textarea.html\';\n
} else if (renderered_field.type === \'FloatField\') {\n
......@@ -284,6 +311,34 @@
.push(function () {\n
return data;\n
});\n
})\n
\n
\n
.declareMethod("checkValidity", function () {\n
var form_gadget = this,\n
k,\n
field_gadget,\n
count = form_gadget.props.gadget_list.length,\n
result = true,\n
queue = new RSVP.Queue();\n
\n
function extendData(field_validity) {\n
result = result && field_validity;\n
}\n
\n
for (k = 0; k < count; k += 1) {\n
field_gadget = form_gadget.props.gadget_list[k];\n
// XXX Hack until better defined\n
if (field_gadget.checkValidity !== undefined) {\n
queue\n
.push(field_gadget.checkValidity.bind(field_gadget))\n
.push(extendData);\n
}\n
}\n
return queue\n
.push(function () {\n
return result;\n
});\n
\n
});\n
\n
......@@ -424,7 +479,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>937.58318.16438.37768</string> </value>
<value> <string>937.64263.33357.15633</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -442,7 +497,7 @@
</tuple>
<state>
<tuple>
<float>1411999896.88</float>
<float>1412254860.94</float>
<string>GMT</string>
</tuple>
</state>
......
......@@ -116,6 +116,7 @@
<script src="RSVP.js" type="text/javascript"></script>\n
<script src="renderjs.js" type="text/javascript"></script>\n
<script src="handlebars.js" type="text/javascript"></script>\n
<script src="gadget_global.js" type="text/javascript"></script>\n
\n
<!-- custom script -->\n
<script src="gadget_erp5_header.js" type="text/javascript"></script>\n
......@@ -126,6 +127,9 @@
<script id="header-link-template" type="text/x-handlebars-template">\n
<a role="button" href="{{url}}" class="responsive ui-btn ui-icon-{{icon}} ui-btn-icon-left ui-first-child ui-last-child {{class}}">{{title}}</a>\n
</script>\n
<script id="header-button-template" type="text/x-handlebars-template">\n
<form><button type=\'submit\' class=\'responsive ui-btn ui-icon-{{icon}} ui-btn-icon-left ui-first-child ui-last-child {{class}}\'>{{title}}</button></form>\n
</script>\n
\n
<script id="sub-header-template" type="text/x-handlebars-template">\n
{{#each sub_header_list}}\n
......@@ -306,7 +310,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>937.54262.65313.31897</string> </value>
<value> <string>937.58644.46215.53674</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -324,7 +328,7 @@
</tuple>
<state>
<tuple>
<float>1411993212.79</float>
<float>1412241646.76</float>
<string>GMT</string>
</tuple>
</state>
......
......@@ -102,8 +102,8 @@
<value> <string encoding="cdata"><![CDATA[
/*jslint nomen: true, indent: 2, maxerr: 3 */\n
/*global window, rJS, Handlebars, document */\n
(function (window, rJS, Handlebars, document) {\n
/*global window, rJS, Handlebars, document, loopEventListener */\n
(function (window, rJS, Handlebars, document, loopEventListener) {\n
"use strict";\n
\n
/////////////////////////////////////////////////////////////////\n
......@@ -121,12 +121,16 @@
.getElementById("header-title-link-template")\n
.innerHTML,\n
header_title_link_template = Handlebars.compile(header_title_link_source),\n
\n
\n
sub_header_source = gadget_klass.__template_element\n
.getElementById("sub-header-template")\n
.innerHTML,\n
sub_header_template = Handlebars.compile(sub_header_source),\n
\n
header_button_source = gadget_klass.__template_element\n
.getElementById("header-button-template")\n
.innerHTML,\n
header_button_template = Handlebars.compile(header_button_source),\n
header_link_source = gadget_klass.__template_element\n
.getElementById("header-link-template")\n
.innerHTML,\n
......@@ -139,6 +143,12 @@
// Init local properties\n
.ready(function (g) {\n
g.props = {};\n
g.stats = {\n
loaded: false,\n
modified: false,\n
submitted: true,\n
options: {}\n
};\n
})\n
\n
// Assign the element to a variable\n
......@@ -153,12 +163,49 @@
g.props.title_element = element.querySelector("h1");\n
});\n
})\n
\n
.ready(function (g) {\n
return g.render(g.stats.options);\n
})\n
\n
.declareAcquiredMethod("whoWantToDisplayThis", "whoWantToDisplayThis")\n
.declareAcquiredMethod("jio_get", "jio_get")\n
.declareAcquiredMethod("triggerSubmit", "triggerSubmit")\n
/////////////////////////////////////////////////////////////////\n
// declared methods\n
/////////////////////////////////////////////////////////////////\n
.declareMethod(\'notifyLoading\', function () {\n
if (this.stats.loaded) {\n
this.stats.loaded = false;\n
return this.render(this.stats.options);\n
}\n
})\n
.declareMethod(\'notifyLoaded\', function () {\n
if (!this.stats.loaded) {\n
this.stats.loaded = true;\n
return this.render(this.stats.options);\n
}\n
})\n
.declareMethod(\'notifyChange\', function () {\n
if (!this.stats.modified) {\n
this.stats.modified = true;\n
return this.render(this.stats.options);\n
}\n
})\n
.declareMethod(\'notifySubmitting\', function () {\n
if (this.stats.submitted) {\n
this.stats.submitted = false;\n
return this.render(this.stats.options);\n
}\n
})\n
.declareMethod(\'notifySubmitted\', function () {\n
if (!this.stats.submitted) {\n
this.stats.submitted = true;\n
// Change modify here, to allow user to redo some modification and being correctly notified\n
this.stats.modified = false;\n
return this.render(this.stats.options);\n
}\n
})\n
.declareMethod(\'render\', function (options) {\n
var gadget = this,\n
possible_left_link_list = [\n
......@@ -171,6 +218,10 @@
possible_right_link_list = [\n
[\'edit_url\', \'Edit\', \'edit\']\n
],\n
possible_right_button_list = [\n
[\'save_action\', \'Save\', \'check\'],\n
[\'submit_action\', \'Proceed\', \'share\']\n
],\n
possible_sub_header_list = [\n
[\'tab_url\', \'Tabs\', \'eye\'],\n
[\'jump_url\', \'Jump\', \'plane\'],\n
......@@ -187,10 +238,12 @@
count = 0,\n
left_link,\n
right_link,\n
right_button,\n
default_right_icon = "",\n
title_link = {title: "ERP5"},\n
sub_header_list = [],\n
alphabet = "abcdefghijklmnopqrstuvwxyz";\n
\n
gadget.stats.options = options;\n
// Handle main title\n
if (options.hasOwnProperty("page_title")) {\n
title_link.title = options.page_title;\n
......@@ -227,6 +280,20 @@
}\n
\n
// Handle right link\n
if (!gadget.stats.loaded) {\n
default_right_icon = "spinner";\n
// Show default loading information\n
right_link = {\n
title: "Loading",\n
icon: default_right_icon,\n
url: "",\n
class: "ui-disabled"\n
};\n
} else if (!gadget.stats.submitted) {\n
default_right_icon = "spinner";\n
} else if (gadget.stats.modified) {\n
default_right_icon = "warning";\n
}\n
for (i = 0; i < possible_right_link_list.length; i += 1) {\n
if (options.hasOwnProperty(possible_right_link_list[i][0])) {\n
klass = "";\n
......@@ -235,17 +302,27 @@
}\n
right_link = {\n
title: possible_right_link_list[i][1],\n
icon: possible_right_link_list[i][2],\n
icon: default_right_icon || possible_right_link_list[i][2],\n
url: options[possible_right_link_list[i][0]],\n
class: klass\n
};\n
count += 1;\n
}\n
}\n
if (right_link === undefined) {\n
gadget.props.right_link.innerHTML = "";\n
} else {\n
for (i = 0; i < possible_right_button_list.length; i += 1) {\n
if (options.hasOwnProperty(possible_right_button_list[i][0])) {\n
right_button = {\n
title: possible_right_button_list[i][1],\n
icon: default_right_icon || possible_right_button_list[i][2]\n
};\n
}\n
}\n
if (right_button !== undefined) {\n
gadget.props.right_link.innerHTML = header_button_template(right_button);\n
} else if (right_link !== undefined) {\n
gadget.props.right_link.innerHTML = header_link_template(right_link);\n
} else {\n
gadget.props.right_link.innerHTML = "";\n
}\n
\n
// Handle sub header\n
......@@ -274,10 +351,28 @@
gadget.props.sub_header_ul.innerHTML = sub_header_template({\n
sub_header_list: sub_header_list\n
});\n
})\n
\n
//////////////////////////////////////////////\n
// handle button click\n
//////////////////////////////////////////////\n
.declareService(function () {\n
var form_gadget = this;\n
\n
function formSubmit() {\n
return form_gadget.triggerSubmit();\n
}\n
\n
// Listen to form submit\n
return loopEventListener(\n
form_gadget.props.element,\n
\'submit\',\n
false,\n
formSubmit\n
);\n
});\n
\n
}(window, rJS, Handlebars, document));
}(window, rJS, Handlebars, document, loopEventListener));
]]></string> </value>
</item>
......@@ -414,7 +509,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>937.58590.25550.25514</string> </value>
<value> <string>937.64299.47970.64017</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -432,7 +527,7 @@
</tuple>
<state>
<tuple>
<float>1412000590.73</float>
<float>1412256721.33</float>
<string>GMT</string>
</tuple>
</state>
......
......@@ -283,7 +283,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>937.58476.63930.3566</string> </value>
<value> <string>937.59751.29793.48025</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -301,7 +301,7 @@
</tuple>
<state>
<tuple>
<float>1412000194.01</float>
<float>1412251612.53</float>
<string>GMT</string>
</tuple>
</state>
......
......@@ -282,6 +282,31 @@
return displayError(this, param_list[0]);\n
})\n
\n
.allowPublicAcquisition(\'notifySubmitting\', function () {\n
return this.getDeclaredGadget("header")\n
.push(function (header_gadget) {\n
return header_gadget.notifySubmitting();\n
});\n
})\n
\n
.allowPublicAcquisition(\'notifySubmitted\', function () {\n
return this.getDeclaredGadget("header")\n
.push(function (header_gadget) {\n
return header_gadget.notifySubmitted();\n
});\n
})\n
.allowPublicAcquisition(\'notifyChange\', function () {\n
return this.getDeclaredGadget("header")\n
.push(function (header_gadget) {\n
return header_gadget.notifyChange();\n
});\n
})\n
.allowPublicAcquisition(\'triggerSubmit\', function () {\n
return this.getDeclaredGadget("pg")\n
.push(function (page_gadget) {\n
return page_gadget.triggerSubmit();\n
});\n
})\n
/////////////////////////////////////////////////////////////////\n
// declared methods\n
/////////////////////////////////////////////////////////////////\n
......@@ -299,10 +324,14 @@
// Render the page\n
.declareMethod(\'render\', function (options) {\n
var gadget = this,\n
header_gadget,\n
main_gadget;\n
return new RSVP.Queue()\n
return gadget.getDeclaredGadget("header")\n
.push(function (declared_gadget) {\n
header_gadget = declared_gadget;\n
return header_gadget.notifyLoading();\n
})\n
.push(function () {\n
$.mobile.loading(\'show\');\n
\n
// By default, init the header options to be empty (ERP5 title by default + sidebar)\n
gadget.props.header_argument_list = [{}];\n
......@@ -327,9 +356,6 @@
\n
.push(function (result) {\n
main_gadget = result;\n
return gadget.getDeclaredGadget("header");\n
})\n
.push(function (header_gadget) {\n
return header_gadget.render.apply(header_gadget, gadget.props.header_argument_list);\n
})\n
.push(function () {\n
......@@ -344,14 +370,13 @@
element.removeChild(element.firstChild);\n
}\n
element.appendChild(fragment);\n
$.mobile.loading(\'hide\');\n
return $(element).trigger("create");\n
$(element).trigger("create");\n
return header_gadget.notifyLoaded();\n
});\n
}\n
})\n
\n
.push(undefined, function (error) {\n
$.mobile.loading(\'hide\');\n
return displayError(gadget, error);\n
});\n
});\n
......@@ -493,7 +518,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>937.59906.57015.45841</string> </value>
<value> <string>937.64000.63491.17493</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -511,7 +536,7 @@
</tuple>
<state>
<tuple>
<float>1412080420.18</float>
<float>1412256764.71</float>
<string>GMT</string>
</tuple>
</state>
......
......@@ -146,6 +146,9 @@
});\n
}\n
select.innerHTML += tmp;\n
if (field_json.required === 1) {\n
select.setAttribute(\'required\', \'required\');\n
}\n
if (field_json.editable !== 1) {\n
select.setAttribute(\'readonly\', \'readonly\');\n
select.setAttribute(\'data-wrapper-class\', \'ui-state-readonly\');\n
......@@ -284,7 +287,7 @@
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>sven</string> </value>
<value> <string>romain</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
......@@ -298,7 +301,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>937.51405.48576.10854</string> </value>
<value> <string>937.59950.62249.34679</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -316,7 +319,7 @@
</tuple>
<state>
<tuple>
<float>1411570828.74</float>
<float>1412243487.09</float>
<string>GMT</string>
</tuple>
</state>
......
......@@ -127,12 +127,12 @@
<h1></h1>\n
<!-- XXX action, method, fieldset -->\n
<form class="dialog_form">\n
<button type="submit" class="ui-btn ui-btn-b ui-btn-inline\n
ui-icon-action ui-btn-icon-right ui-screen-hidden">Submit</button>\n
<div data-gadget-url="gadget_erp5_form.html"\n
data-gadget-scope="erp5_form"\n
data-gadget-sandbox="public">\n
</div>\n
<!--button type="submit" class="ui-btn ui-btn-b ui-btn-inline\n
ui-icon-action ui-btn-icon-right"></button-->\n
</form>\n
</body>\n
</html>
......@@ -272,7 +272,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>937.54178.14656.62515</string> </value>
<value> <string>937.54178.32965.53043</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -290,7 +290,7 @@
</tuple>
<state>
<tuple>
<float>1411735830.16</float>
<float>1412240451.98</float>
<string>GMT</string>
</tuple>
</state>
......
......@@ -141,6 +141,9 @@
/////////////////////////////////////////////////////////////////\n
// declared methods\n
/////////////////////////////////////////////////////////////////\n
.declareMethod(\'triggerSubmit\', function () {\n
this.props.element.querySelector(\'button\').click();\n
})\n
.declareMethod(\'render\', function (options) {\n
var erp5_document = options.erp5_document,\n
form_options = options.erp5_form || {},\n
......@@ -180,7 +183,8 @@
return form_gadget.renderPageHeader({\n
cancel_url: all_result[1],\n
page_title: options.erp5_document.title,\n
breadcrumb_url: all_result[2]\n
breadcrumb_url: all_result[2],\n
submit_action: true\n
});\n
});\n
})\n
......@@ -377,7 +381,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>937.58416.46801.34491</string> </value>
<value> <string>937.64201.40983.3993</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -395,7 +399,7 @@
</tuple>
<state>
<tuple>
<float>1411990143.42</float>
<float>1412251248.98</float>
<string>GMT</string>
</tuple>
</state>
......
......@@ -131,10 +131,19 @@
\n
return new RSVP.Queue()\n
.push(function () {\n
var new_content_action = options.erp5_document._links.action_object_new_content_action;\n
if (new_content_action !== undefined) {\n
new_content_action = gadget.whoWantToDisplayThisPage({name: new_content_action.name});\n
} else {\n
new_content_action = "";\n
}\n
\n
return RSVP.all([\n
gadget.getDeclaredGadget("erp5_searchfield"),\n
gadget.getDeclaredGadget("erp5_form"),\n
gadget.whoWantToDisplayThisPage({name: options.view, page: "breadcrumb"})\n
gadget.whoWantToDisplayThisPage({name: options.view, page: "breadcrumb"}),\n
new_content_action,\n
gadget.whoWantToDisplayThisPage({page: "action", name: options.view})\n
]);\n
})\n
.push(function (all_gadget) {\n
......@@ -144,9 +153,9 @@
gadget.renderPageHeader({\n
jump_url: "",\n
cut_url: "",\n
actions_url: "",\n
actions_url: all_gadget[4],\n
export_url: "",\n
add_url: "",\n
add_url: all_gadget[3],\n
page_title: options.erp5_document.title,\n
breadcrumb_url: all_gadget[2]\n
})\n
......@@ -291,7 +300,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>937.54284.12675.3652</string> </value>
<value> <string>937.64086.34377.7560</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -309,7 +318,7 @@
</tuple>
<state>
<tuple>
<float>1411742227.2</float>
<float>1412257173.84</float>
<string>GMT</string>
</tuple>
</state>
......
......@@ -130,14 +130,9 @@
\n
<!-- XXX action, method, fieldset -->\n
<!-- XXX needs a theme -->\n
<form class="save_form ui-body-c">\n
<!--button type="submit" class="ui-btn ui-btn-b ui-btn-inline\n
ui-icon-edit ui-btn-icon-right">Save</button-->\n
\n
<!--div class="action_list ui-controlgroup ui-controlgroup-horizontal ui-corner-all">\n
<div class="ui-controlgroup-controls">\n
</div>\n
</div-->\n
<form class="save_form ui-body-c" novalidate>\n
<button type="submit" class="ui-btn ui-btn-b ui-btn-inline\n
ui-icon-edit ui-btn-icon-right ui-screen-hidden">Save</button>\n
\n
<div data-gadget-url="gadget_erp5_form.html"\n