Commit e16082c8 authored by Sebastian's avatar Sebastian

Update to reflect new approach. Removed many unneeded UI gadgets

parent d220c102
/*globals window, document, RSVP, rJS,
URI, location, XMLHttpRequest, console, navigator*/
/*jslint indent: 2, maxlen: 80*/
(function (window, document, RSVP, rJS,
XMLHttpRequest, location, console, navigator) {
"use strict";
var MAIN_SCOPE = "m";
function renderMainGadget(gadget, url, options) {
return gadget.declareGadget(url, {
scope: MAIN_SCOPE
})
.push(function (page_gadget) {
gadget.props.m_options_string = JSON.stringify(options);
if (page_gadget.render === undefined) {
return [page_gadget];
}
return RSVP.all([
page_gadget,
page_gadget.render(options)
]);
})
.push(function (all_result) {
return all_result[0];
});
}
function initHeaderOptions(gadget) {
gadget.props.header_argument_list = {
panel_action: true,
title: gadget.props.application_title || "OfficeJS"
};
}
function initPanelOptions(gadget) {
gadget.props.panel_argument_list = {};
}
function route(my_root_gadget, my_scope, my_method, argument_list) {
return RSVP.Queue()
.push(function () {
return my_root_gadget.getDeclaredGadget(my_scope);
})
.push(function (my_gadget) {
if (argument_list) {
return my_gadget[my_method].apply(my_gadget, argument_list);
}
return my_gadget[my_method]();
});
}
function updateHeader(gadget) {
var header_gadget;
return gadget.getDeclaredGadget("header")
.push(function (result) {
header_gadget = result;
return header_gadget.notifySubmitted();
})
.push(function () {
return header_gadget.render(gadget.props.header_argument_list);
});
}
function updatePanel(gadget) {
return gadget.getDeclaredGadget("panel")
.push(function (panel_gadget) {
return panel_gadget.render(gadget.props.panel_argument_list);
});
}
function increaseLoadingCounter(gadget) {
return new RSVP.Queue()
.push(function () {
gadget.props.loading_counter += 1;
if (gadget.props.loading_counter === 1) {
return gadget.getDeclaredGadget("header")
.push(function (header_gadget) {
return header_gadget.notifyLoading();
});
}
});
}
function decreaseLoadingCounter(gadget) {
return new RSVP.Queue()
.push(function () {
gadget.props.loading_counter -= 1;
if (gadget.props.loading_counter < 0) {
gadget.props.loading_counter = 0;
// throw new Error("Unexpected negative loading counter");
}
if (gadget.props.loading_counter === 0) {
return gadget.getDeclaredGadget("header")
.push(function (header_gadget) {
return header_gadget.notifyLoaded();
});
}
});
}
function callJioGadget(gadget, method, param_list) {
var called = false;
return new RSVP.Queue()
.push(function () {
called = true;
return increaseLoadingCounter(gadget);
})
.push(function () {
return gadget.getDeclaredGadget("jio_gadget");
})
.push(function (jio_gadget) {
return jio_gadget[method].apply(jio_gadget, param_list);
})
.push(function (result) {
return decreaseLoadingCounter(gadget)
.push(function () {
return result;
});
}, function (error) {
if (called) {
return decreaseLoadingCounter(gadget)
.push(function () {
throw error;
});
}
throw error;
});
}
function displayErrorContent(gadget, error) {
// Do not break the application in case of errors.
// Display it to the user for now,
// and allow user to go back to the frontpage
var error_text = "";
if (error.target instanceof XMLHttpRequest) {
error_text = error.target.toString() + " " +
error.target.status + " " +
error.target.statusText + "\n" +
error.target.responseURL + "\n\n" +
error.target.getAllResponseHeaders();
} else if (error instanceof Error) {
error_text = error.toString();
} else {
error_text = JSON.stringify(error);
}
console.error(error);
if (error instanceof Error) {
console.error(error.stack);
}
if (gadget.props === undefined) {
// Gadget has not yet been correctly initialized
throw error;
}
return gadget.changeState({
error_text: error_text,
url: undefined
});
}
function displayError(gadget, error) {
if (error instanceof RSVP.CancellationError) {
return;
}
return displayErrorContent(gadget, error);
}
//////////////////////////////////////////
// Page rendering
//////////////////////////////////////////
rJS(window)
.ready(function () {
var gadget = this,
setting_gadget,
setting;
this.props = {
loading_counter: 0,
content_element: this.element.querySelector('.gadget-content')
};
// Configure setting storage
return gadget.getDeclaredGadget("setting_gadget")
.push(function (result) {
setting_gadget = result;
return setting_gadget.createJio({
type: "indexeddb",
database: "setting"
});
})
.push(function () {
return setting_gadget.get("setting")
.push(undefined, function (error) {
if (error.status_code === 404) {
return {};
}
throw error;
});
})
.push(function (result) {
setting = result;
// Extract configuration parameters stored in HTML
// XXX Will work only if top gadget...
var element_list =
document.head
.querySelectorAll("script[data-renderjs-configuration]"),
len = element_list.length,
key,
value,
i;
for (i = 0; i < len; i += 1) {
key = element_list[i].getAttribute('data-renderjs-configuration');
value = element_list[i].textContent;
gadget.props[key] = value;
setting[key] = value;
}
// Calculate erp5 hateoas url
setting.hateoas_url = (new URI(gadget.props.hateoas_url))
.absoluteTo(location.href)
.toString();
if (setting.hasOwnProperty('service_worker_url') &&
(setting.service_worker_url !== '')) {
if (navigator.serviceWorker !== undefined) {
// Check if a ServiceWorker already controls the site on load
if (!navigator.serviceWorker.controller) {
// Register the ServiceWorker
navigator.serviceWorker.register(setting.service_worker_url);
}
}
}
return setting_gadget.put("setting", setting);
})
.push(function () {
// Configure jIO storage
return gadget.getDeclaredGadget("jio_gadget");
})
.push(function (jio_gadget) {
return jio_gadget.createJio(setting.jio_storage_description);
})
//--.push(function () {
//-- return gadget.getDeclaredGadget('panel');
//--})
//--.push(function (panel_gadget) {
//-- return panel_gadget.render({});
//--})
.push(function () {
return gadget.getDeclaredGadget('router');
})
.push(function (router_gadget) {
return router_gadget.start();
});
})
//////////////////////////////////////////
// Allow Acquisition
//////////////////////////////////////////
.allowPublicAcquisition("getSetting", function (argument_list) {
var gadget = this,
key = argument_list[0],
default_value = argument_list[1];
return gadget.getDeclaredGadget("setting_gadget")
.push(function (jio_gadget) {
return jio_gadget.get("setting");
})
.push(function (doc) {
return doc[key] || default_value;
}, function (error) {
if (error.status_code === 404) {
return default_value;
}
throw error;
});
})
.allowPublicAcquisition("setSetting", function (argument_list) {
var jio_gadget,
gadget = this,
key = argument_list[0],
value = argument_list[1];
return gadget.getDeclaredGadget("setting_gadget")
.push(function (result) {
jio_gadget = result;
return jio_gadget.get("setting");
})
.push(undefined, function (error) {
if (error.status_code === 404) {
return {};
}
throw error;
})
.push(function (doc) {
doc[key] = value;
return jio_gadget.put('setting', doc);
});
})
.allowPublicAcquisition("translateHtml", function (argument_list) {
return this.getDeclaredGadget("translation_gadget")
.push(function (translation_gadget) {
return translation_gadget.translateHtml(argument_list[0]);
});
})
// XXX Those methods may be directly integrated into the header,
// as it handles the submit triggering
.allowPublicAcquisition('notifySubmitting', function (argument_list) {
return RSVP.all([
route(this, "header", 'notifySubmitting'),
route(this, "notification", 'notify', argument_list)
]);
})
.allowPublicAcquisition('notifySubmitted', function (argument_list) {
return RSVP.all([
route(this, "header", 'notifySubmitted'),
route(this, "notification", 'notify', argument_list)
]);
})
.allowPublicAcquisition('notifyChange', function (argument_list) {
return RSVP.all([
route(this, "header", 'notifyChange'),
route(this, "notification", 'notify', argument_list)
]);
})
.allowPublicAcquisition('refresh', function () {
var gadget = this;
return gadget.getDeclaredGadget(MAIN_SCOPE)
.push(function (main) {
if (main.render !== undefined) {
return main.render(JSON.parse(gadget.props.m_options_string));
}
}, function () {
return;
});
})
.allowPublicAcquisition("translate", function (argument_list) {
return this.getDeclaredGadget("translation_gadget")
.push(function (translation_gadget) {
return translation_gadget.translate(argument_list[0]);
});
})
.allowPublicAcquisition("redirect", function (param_list) {
return this.getDeclaredGadget('router')
.push(function (router_gadget) {
return router_gadget.redirect.apply(router_gadget, param_list);
});
})
.allowPublicAcquisition('reload', function () {
return location.reload();
})
.allowPublicAcquisition("getUrlParameter", function (param_list) {
return this.getDeclaredGadget('router')
.push(function (router_gadget) {
return router_gadget.getUrlParameter.apply(router_gadget, param_list);
});
})
.allowPublicAcquisition("getUrlFor", function (param_list) {
return this.getDeclaredGadget('router')
.push(function (router_gadget) {
return router_gadget.getCommandUrlFor.apply(router_gadget,
param_list);
});
})
.allowPublicAcquisition("updateHeader", function (param_list) {
var gadget = this;
initHeaderOptions(gadget);
return this.getDeclaredGadget("translation_gadget")
.push(function (translation_gadget) {
var promise_list = [],
key;
for (key in param_list[0]) {
if (param_list[0].hasOwnProperty(key)) {
gadget.props.header_argument_list[key] = param_list[0][key];
}
}
promise_list.push(translation_gadget.translate(
gadget.props.header_argument_list.title
));
if (gadget.props.header_argument_list.hasOwnProperty('right_title')) {
promise_list.push(translation_gadget.translate(
gadget.props.header_argument_list.right_title
));
}
return RSVP.all(promise_list);
})
.push(function (result_list) {
gadget.props.header_argument_list.title = result_list[0];
if (result_list.length === 2) {
gadget.props.header_argument_list.right_title = result_list[1];
}
// XXX Sven hack: number of _url determine padding for
// subheader on ui-content
var key,
count = 0;
for (key in gadget.props.header_argument_list) {
if (gadget.props.header_argument_list.hasOwnProperty(key)) {
if (key.indexOf('_url') > -1) {
count += 1;
}
}
}
if (count > 2) {
gadget.props.sub_header_class = "ui-has-subheader";
}
});
})
.allowPublicAcquisition("updatePanel", function (param_list) {
var gadget = this;
initPanelOptions(gadget);
gadget.props.panel_argument_list = param_list[0];
})
.allowPublicAcquisition('triggerPanel', function () {
return route(this, "panel", "toggle");
})
.allowPublicAcquisition('renderEditorPanel', function (param_list) {
return route(this, "editor_panel", 'render', param_list);
})
.allowPublicAcquisition("jio_allDocs", function (param_list) {
return callJioGadget(this, "allDocs", param_list);
})
.allowPublicAcquisition("jio_remove", function (param_list) {
return callJioGadget(this, "remove", param_list);
})
.allowPublicAcquisition("jio_post", function (param_list) {
return callJioGadget(this, "post", param_list);
})
.allowPublicAcquisition("jio_put", function (param_list) {
return callJioGadget(this, "put", param_list);
})
.allowPublicAcquisition("jio_get", function (param_list) {
return callJioGadget(this, "get", param_list);
})
.allowPublicAcquisition("jio_allAttachments", function (param_list) {
return callJioGadget(this, "allAttachments", param_list);
})
.allowPublicAcquisition("jio_getAttachment", function (param_list) {
return callJioGadget(this, "getAttachment", param_list);
})
.allowPublicAcquisition("jio_putAttachment", function (param_list) {
return callJioGadget(this, "putAttachment", param_list);
})
.allowPublicAcquisition("jio_removeAttachment", function (param_list) {
return callJioGadget(this, "removeAttachment", param_list);
})
.allowPublicAcquisition("jio_repair", function (param_list) {
return callJioGadget(this, "repair", param_list);
})
.allowPublicAcquisition("triggerSubmit", function (param_list) {
return this.getDeclaredGadget(MAIN_SCOPE)
.push(function (main_gadget) {
return main_gadget.triggerSubmit(param_list);
});
})
/////////////////////////////////////////////////////////////////
// declared methods
/////////////////////////////////////////////////////////////////
.allowPublicAcquisition("renderApplication", function (param_list) {
return this.render.apply(this, param_list);
})
.onStateChange(function (modification_dict) {
var gadget = this,
route_result = gadget.state;
if (modification_dict.hasOwnProperty('error_text')) {
return gadget.dropGadget(MAIN_SCOPE)
.push(undefined, function () {
// Do not crash the app if the pg gadget in not defined
// ie, keep the original error on screen
return;
})
.push(function () {
// XXX Improve error rendering
gadget.props.content_element.innerHTML = "<br/><br/><br/><pre></pre>";
gadget.props.content_element.querySelector('pre').textContent =
"Error: " + gadget.state.error_text;
// XXX Notify error
});
}
if (modification_dict.hasOwnProperty('url')) {
return new RSVP.Queue()
.push(function () {
return renderMainGadget(
gadget,
route_result.url,
route_result.options
);
})
.push(function (main_gadget) {
// Append loaded gadget in the page
if (main_gadget !== undefined) {
var element = gadget.props.content_element,
content_container = document.createDocumentFragment();
// content_container.className = "ui-content " +
// (gadget.props.sub_header_class || "");
// reset subheader indicator
delete gadget.props.sub_header_class;
// go to the top of the page
window.scrollTo(0, 0);
// Clear first to DOM, append after to reduce flickering/manip
while (element.firstChild) {
element.removeChild(element.firstChild);
}
content_container.appendChild(main_gadget.element);
element.appendChild(content_container);
return RSVP.all([
updateHeader(gadget),
updatePanel(gadget)
]);
// XXX Drop notification
// return header_gadget.notifyLoaded();
}
});
}
// Same subgadget
return gadget.getDeclaredGadget(MAIN_SCOPE)
.push(function (page_gadget) {
return page_gadget.render(gadget.state.options);
})
.push(function () {
return RSVP.all([
updateHeader(gadget),
updatePanel(gadget)
]);
});
})
// Render the page
.declareMethod('render', function (route_result, keep_message) {
/*--
var gadget = this;
// Reinitialize the loading counter
gadget.props.loading_counter = 0;
// By default, init the header options to be empty
// (ERP5 title by default + sidebar)
initHeaderOptions(gadget);
initPanelOptions(gadget);
return new RSVP.Queue()
.push(function () {
return increaseLoadingCounter(gadget);
})
.push(function () {
var promise_list = [
route(gadget, 'panel', 'close'),
route(gadget, 'editor_panel', 'close')
];
if (keep_message !== true) {
promise_list.push(route(gadget, 'notification', 'close'));
}
return RSVP.all(promise_list);
})
.push(function () {
return gadget.changeState({url: route_result.url,
options: route_result.options});
})
.push(function () {
return decreaseLoadingCounter(gadget);
}, function (error) {
return decreaseLoadingCounter(gadget)
.push(function () {
throw error;
});
});
--*/
})
/////////////////////////////////
// Handle sub gadgets services
/////////////////////////////////
.allowPublicAcquisition('reportServiceError', function (param_list,
gadget_scope) {
if (gadget_scope === undefined) {
// don't fail in case of dropped subgadget (like previous page)
return;
}
return displayError(this, param_list[0]);
})
.onEvent('submit', function () {
return displayError(this, new Error("Unexpected form submit"));
});
}(window, document, RSVP, rJS,
XMLHttpRequest, location, console, navigator));
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, user-scalable=no" />
<title>ERP5 Editor Panel</title>
<!-- renderjs -->
<script src="rsvp.js"></script>
<script src="renderjs.js"></script>
<!-- custom script -->
<script src="gadget_erp5_editor_panel.js"></script>
</head>
<body>
</body>
</html>
\ No newline at end of file
/*jslint indent: 2, maxerr: 3, nomen: true, maxlen: 80 */
/*global window, rJS, RSVP */
(function (window, rJS, RSVP) {
"use strict";
rJS(window)
//////////////////////////////////////////////
// acquired method
//////////////////////////////////////////////
.allowPublicAcquisition('trigger', function () {
return this.toggle();
})
.declareMethod('toggle', function () {
if (this.state.visible) {
return this.close();
}
return this.changeState({
visible: !this.state.visible
});
})
.declareMethod('close', function () {
return this.changeState({
visible: false,
url: undefined,
options: undefined
});
})
.declareMethod('render', function (url, options) {
// XXX Hack to close the panel if the sort/filter button
// is clicked twice
if (url === this.state.url) {
return this.changeState({
visible: false,
url: undefined,
options: undefined
});
}
return this.changeState({
visible: true,
url: url,
options: options
});
})
.onStateChange(function (modification_dict) {
var queue,
gadget = this;
if (this.state.visible) {
if (!this.element.classList.contains('visible')) {
this.element.classList.toggle('visible');
}
} else {
if (this.element.classList.contains('visible')) {
this.element.classList.remove('visible');
}
}
if (modification_dict.hasOwnProperty('url')) {
if (this.state.url === undefined) {
while (this.element.firstChild) {
this.element.removeChild(this.element.firstChild);
}
} else {
queue = this.declareGadget(this.state.url,
{scope: "declared_gadget"});
}
} else {
if (this.state.url !== undefined) {
queue = this.getDeclaredGadget("declared_gadget");
}
}
if (queue !== undefined) {
return queue
.push(function (declared_gadget) {
return RSVP.all([
declared_gadget,
declared_gadget.render(gadget.state.options)
]);
})
.push(function (result_list) {
if (modification_dict.hasOwnProperty('url')) {
while (gadget.element.firstChild) {
gadget.element.removeChild(gadget.element.firstChild);
}
gadget.element.appendChild(result_list[0].element);
}
});
}
});
}(window, rJS, RSVP));
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<title>Dummy Panel</title>
</head>
<body>
</body>
</html>
<!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 Notification</title>
<!-- renderjs -->
<script src="rsvp.js" type="text/javascript"></script>
<script src="renderjs.js" type="text/javascript"></script>
<script src="handlebars.js" type="text/javascript"></script>
<script id="success-button-template" type="text/x-handlebars-template">
<button type="submit" class='success'>{{message}}</button>
</script>
<script id="error-button-template" type="text/x-handlebars-template">
<button type="submit" class='error'>{{message}}</button>
</script>
<!-- custom script -->
<script src="gadget_erp5_notification.js" type="text/javascript"></script>
</head>
<body>
</body>
</html>
\ No newline at end of file
/*jslint nomen: true, indent: 2, maxerr: 3 */
/*global window, Node, rJS, Handlebars */
(function (window, Node, rJS, Handlebars) {
"use strict";
var gadget_klass = rJS(window),
success_button_source = gadget_klass.__template_element
.getElementById("success-button-template")
.innerHTML,
success_button_template = Handlebars.compile(success_button_source),
error_button_source = gadget_klass.__template_element
.getElementById("error-button-template")
.innerHTML,
error_button_template = Handlebars.compile(error_button_source);
/////////////////////////////////////////////////////////////////
// declared methods
/////////////////////////////////////////////////////////////////
gadget_klass
.declareMethod('notify', function (options) {
if (options) {
return this.changeState({
visible: true,
message: options.message,
status: options.status
});
}
return this.changeState({
visible: false
});
})
.declareMethod('close', function () {
return this.changeState({
visible: false
});
})
.onStateChange(function (modification_dict) {
if (modification_dict.hasOwnProperty('visible')) {
if (this.state.visible) {
if (!this.element.classList.contains('visible')) {
this.element.classList.toggle('visible');
}
} else {
if (this.element.classList.contains('visible')) {
this.element.classList.remove('visible');
}
}
}
if (modification_dict.hasOwnProperty('message')) {
if (this.state.status === 'success') {
this.element.innerHTML = success_button_template({
message: this.state.message
});
} else {
this.element.innerHTML = error_button_template({
message: this.state.message
});
}
}
})
.onEvent('click', function (evt) {
if ((evt.target.nodeType === Node.ELEMENT_NODE) &&
(evt.target.tagName === 'BUTTON')) {
return this.close();
}
}, false, false);
}(window, Node, rJS, Handlebars));
\ No newline at end of file
<!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 Panel</title>
<!--
data-i18n=Editable
-->
<!-- renderjs -->
<script src="rsvp.js" type="text/javascript"></script>
<script src="renderjs.js" type="text/javascript"></script>
<script src="handlebars.js" type="text/javascript"></script>
<script src="gadget_global.js" type="text/javascript"></script>
<script id="panel-template-header" type="text/x-handlebars-template">
<div data-role="header" class="ui-bar-inherit">
<div class="ui-controlgroup ui-controlgroup-horizontal ui-btn-left">
<div class="ui-controlgroup-controls">
<button data-i18n="Close" class="ui-btn ui-btn-icon-notext ui-icon-delete">Close</button>
</div>
</div>
<div class="panel_img">
<img class="ui-title" alt="ERP5" src="gadget_erp5_panel.png?format=png"/>
</div>
</div>
</script>
<script id="panel-template-body" type="text/x-handlebars-template">
<div class="ui-content">
<form class="dialog_form">
<button type="submit" class="ui-btn ui-btn-b ui-btn-inline
ui-icon-action ui-btn-icon-right ui-screen-hidden">Submit</button>
<div data-gadget-url="gadget_erp5_searchfield.html"
data-gadget-scope="erp5_searchfield"
data-gadget-sandbox="public"></div>
</form>
<ul data-role="listview" class="ui-listview" data-enhanced="true"></ul>
<div data-gadget-url="gadget_erp5_field_checkbox.html"
data-gadget-scope="erp5_checkbox"
data-gadget-sandbox="public"></div>
<dl></dl>
</div>
</script>
<script id="panel-template-body-list" type="text/x-handlebars-template">
<li class="ui-first-child"><a href="{{front_href}}" class="ui-btn ui-btn-icon-left ui-icon-home" data-i18n="Home">Home</a></li>
<li><a href="{{module_href}}" class="ui-btn ui-btn-icon-left ui-icon-puzzle-piece" data-i18n="Modules" accesskey="m">Modules</a></li>
<li><a href="{{worklist_href}}" class="ui-btn ui-btn-icon-left ui-icon-clipboard" data-i18n="Worklists" accesskey="w">Worklists</a></li>
<li><a href="{{history_href}}" class="ui-btn ui-btn-icon-left ui-icon-history" data-i18n="History" accesskey="h">History</a></li>
<li><a href="{{search_href}}" class="ui-btn ui-btn-icon-left ui-icon-search" data-i18n="Search" accesskey="s">Search</a></li>
<li><a href="{{preference_href}}" class="ui-btn ui-btn-icon-left ui-icon-gear" data-i18n="Preferences">Preferences</a></li>
<li class="ui-last-child"><a href="{{logout_href}}" class="ui-btn ui-btn-icon-left ui-icon-power-off" data-i18n="Logout" accesskey="o">Logout</a></li>
</script>
<script id="panel-template-body-desktop" type="text/x-handlebars-template">
<dt class="ui-content-title ui-body-c ui-btn ui-btn-icon-left ui-icon-eye" data-i18n="Views">Views</dt>
{{#each view_list}}
<dd data-role="listview" data-theme="c" data-inset="true" class="document-listview">
<a data-i18n="{{title}}" class="ui-body-inherit" href="{{href}}">{{title}}</a>
</dd>
{{/each}}
<dt class="ui-content-title ui-body-c ui-btn ui-btn-icon-left ui-icon-cogs" data-i18n="Decisions">Decisions</dt>
{{#each workflow_list}}
<dd data-role="listview" data-theme="c" data-inset="true" class="document-listview">
<a data-i18n="{{title}}" class="ui-body-inherit" href="{{href}}">{{title}}</a>
</dd>
{{/each}}
</script>
<!-- custom script -->
<script src="gadget_erp5_panel.js" type="text/javascript"></script>
</head>
<body>
<div class="jqm-navmenu-panel"></div>
</body>
</html>
\ No newline at end of file
/*jslint nomen: true, indent: 2, maxerr: 3, unparam: true */
/*global window, document, rJS, Handlebars, RSVP, Node, loopEventListener */
(function (window, document, rJS, Handlebars, RSVP, Node, loopEventListener) {
"use strict";
/////////////////////////////////////////////////////////////////
// temlates
/////////////////////////////////////////////////////////////////
// Precompile templates while loading the first gadget instance
var gadget_klass = rJS(window),
template_element = gadget_klass.__template_element,
panel_template_header = Handlebars.compile(template_element
.getElementById("panel-template-header")
.innerHTML),
panel_template_body = Handlebars.compile(template_element
.getElementById("panel-template-body")
.innerHTML),
panel_template_body_list = Handlebars.compile(template_element
.getElementById("panel-template-body-list")
.innerHTML),
panel_template_body_desktop = Handlebars.compile(template_element
.getElementById("panel-template-body-desktop")
.innerHTML);
gadget_klass
.setState({
visible: false,
desktop: false
})
//////////////////////////////////////////////
// acquired method
//////////////////////////////////////////////
.declareAcquiredMethod("getUrlFor", "getUrlFor")
.declareAcquiredMethod("translateHtml", "translateHtml")
.declareAcquiredMethod("translate", "translate")
.declareAcquiredMethod("redirect", "redirect")
.declareAcquiredMethod("getUrlParameter", "getUrlParameter")
/////////////////////////////////////////////////////////////////
// declared methods
/////////////////////////////////////////////////////////////////
.declareMethod('toggle', function () {
return this.changeState({
visible: !this.state.visible
});
})
.declareMethod('close', function () {
return this.changeState({
visible: false
});
})
.declareMethod('render', function (options) {
var erp5_document = options.erp5_document,
workflow_list,
view_list,
context = this;
if (erp5_document !== undefined) {
workflow_list = erp5_document._links.action_workflow || [];
view_list = erp5_document._links.action_object_view || [];
if (workflow_list.constructor !== Array) {
workflow_list = [workflow_list];
}
if (view_list.constructor !== Array) {
view_list = [view_list];
}
// Prevent has much as possible to modify the DOM panel
// stateChange prefer to compare strings
workflow_list = JSON.stringify(workflow_list);
view_list = JSON.stringify(view_list);
}
return context.getUrlParameter('editable')
.push(function (editable) {
return context.changeState({
workflow_list: workflow_list,
view_list: view_list,
global: true,
editable: options.editable || editable || false
});
});
})
.onStateChange(function (modification_dict) {
var context = this,
gadget = this,
queue = new RSVP.Queue(),
tmp_element;
if (modification_dict.hasOwnProperty("visible")) {
if (this.state.visible) {
if (!this.element.classList.contains('visible')) {
this.element.classList.toggle('visible');
}
} else {
if (this.element.classList.contains('visible')) {
this.element.classList.remove('visible');
}
}
}
if (modification_dict.hasOwnProperty("global")) {
queue
.push(function () {
// XXX: Customize panel header!
return context.translateHtml(
panel_template_header() +
panel_template_body()
);
})
.push(function (my_translated_or_plain_html) {
tmp_element = document.createElement('div');
tmp_element.innerHTML = my_translated_or_plain_html;
return context.declareGadget('gadget_erp5_searchfield.html', {
scope: "erp5_searchfield",
element: tmp_element.querySelector('[data-gadget-scope="erp5_searchfield"]')
});
})
.push(function (search_gadget) {
return search_gadget.render({
focus: false,
extended_search: ''
});
})
.push(function () {
return context.declareGadget('gadget_erp5_field_multicheckbox.html', {
scope: "erp5_checkbox",
element: tmp_element.querySelector('[data-gadget-scope="erp5_checkbox"]')
});
})
.push(function () {
context.element.querySelector("div").appendChild(tmp_element);
return context.listenResize();
});
}
if (modification_dict.hasOwnProperty("editable")) {
queue
// Update the global links
.push(function () {
return RSVP.all([
context.getUrlFor({command: 'display', options: {page: "front"}}),
context.getUrlFor({command: 'display', options: {page: "history"}}),
context.getUrlFor({command: 'display', options: {page: "preference"}}),
context.getUrlFor({command: 'display', options: {page: "logout"}}),
context.getUrlFor({command: 'display', options: {page: "search"}}),
context.getUrlFor({command: 'display', options: {page: "worklist"}}),
context.getUrlFor({command: 'display'})
]);
})
.push(function (result_list) {
return context.translateHtml(
panel_template_body_list({
"module_href": result_list[0],
"history_href": result_list[1],
"preference_href": result_list[2],
"logout_href": result_list[3],
"search_href": result_list[4],
"worklist_href": result_list[5],
"front_href": result_list[6]
})
);
})
.push(function (result) {
context.element.querySelector("ul").innerHTML = result;
// Update the checkbox field value
return RSVP.all([
context.getDeclaredGadget("erp5_checkbox"),
context.translate("Editable")
]);
})
.push(function (result_list) {
var value = [],
search_gadget = result_list[0],
title = result_list[1];
if (context.state.editable) {
value = ['editable'];
}
return search_gadget.render({field_json: {
editable: true,
name: 'editable',
key: 'editable',
hidden: false,
items: [[title, 'editable']],
default: value
}});
});
}
if ((this.state.global === true) &&
(modification_dict.hasOwnProperty("desktop") ||
modification_dict.hasOwnProperty("editable") ||
modification_dict.hasOwnProperty("workflow_list") ||
modification_dict.hasOwnProperty("view_list"))) {
if (!(this.state.desktop && (this.state.view_list !== undefined))) {
queue
.push(function () {
gadget.element.querySelector("dl").textContent = '';
});
} else {
queue
.push(function () {
var i = 0,
promise_list = [],
workflow_list = JSON.parse(gadget.state.workflow_list),
view_list = JSON.parse(gadget.state.view_list);
for (i = 0; i < workflow_list.length; i += 1) {
promise_list.push(
gadget.getUrlFor({
command: 'change',
options: {
view: workflow_list[i].href,
page: undefined
}
})
);
}
for (i = 0; i < view_list.length; i += 1) {
promise_list.push(
gadget.getUrlFor({
command: 'change',
options: {
view: view_list[i].href,
page: undefined
}
})
);
}
return RSVP.all(promise_list);
})
.push(function (result_list) {
var i,
result_workflow_list = [],
result_view_list = [],
workflow_list = JSON.parse(gadget.state.workflow_list),
view_list = JSON.parse(gadget.state.view_list);
for (i = 0; i < workflow_list.length; i += 1) {
result_workflow_list.push({
title: workflow_list[i].title,
href: result_list[i]
});
}
for (i = 0; i < view_list.length; i += 1) {
result_view_list.push({
title: view_list[i].title,
href: result_list[i + workflow_list.length]
});
}
gadget.element.querySelector("dl").innerHTML = panel_template_body_desktop({
workflow_list: result_workflow_list,
view_list: result_view_list
});
});
}
}
return queue;
})
/////////////////////////////////////////////////////////////////
// declared services
/////////////////////////////////////////////////////////////////
.onEvent('click', function (evt) {
if ((evt.target.nodeType === Node.ELEMENT_NODE) &&
(evt.target.tagName === 'BUTTON')) {
return this.toggle();
}
}, false, false)
.declareJob('listenResize', function () {
// resize should be only trigger after the render method
// as displaying the panel rely on external gadget (for translation for example)
var result,
event,
context = this;
function extractSizeAndDispatch() {
if (window.matchMedia("(min-width: 85em)").matches) {
return context.changeState({
desktop: true
});
}
return context.changeState({
desktop: false
});
}
result = loopEventListener(window, 'resize', false,
extractSizeAndDispatch);
event = document.createEvent("Event");
event.initEvent('resize', true, true);
window.dispatchEvent(event);
return result;
})
.allowPublicAcquisition('notifyChange', function (argument_list, scope) {
if (scope === 'erp5_checkbox') {
var context = this;
return context.getDeclaredGadget('erp5_checkbox')
.push(function (gadget) {
return gadget.getContent();
})
.push(function (result) {
var options = {editable: undefined};
if (result.editable.length === 1) {
options.editable = true;
}
return context.redirect({command: 'change', options: options});
});
}
// Typing a search query should not modify the header status
return;
})
.allowPublicAcquisition('notifyValid', function () {
// Typing a search query should not modify the header status
return;
})
.onEvent('submit', function (event) {
var gadget = this,
search_gadget,
redirect_options = {
page: "search"
};
return gadget
.getDeclaredGadget("erp5_searchfield")
.push(function (declared_gadget) {
search_gadget = declared_gadget;
return search_gadget.getContent();
})
.push(function (data) {
if (data.search) {
redirect_options.extended_search = data.search;
}
// let the search gadget know its current state (value and focus)
// in order to be able to zero it out in the next Promise
// input gadget's state does not reflect immediate reality
// so we need to manage its state from the parent
return search_gadget.render({
extended_search: data.search,
focus: true
});
})
.push(function () {
// we want the search field in side panel to be empty and blured
return search_gadget.render({
extended_search: '',
focus: false // we don't want focus on the empty field for sure
});
})
.push(function () {
return gadget.redirect({command: 'display', options: redirect_options});
});
}, /*useCapture=*/false, /*preventDefault=*/true);
}(window, document, rJS, Handlebars, RSVP, Node, loopEventListener));
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, user-scalable=no" />
<title>Router Gadget</title>
<!-- renderjs -->
<script src="rsvp.js" type="text/javascript"></script>
<script src="renderjs.js" type="text/javascript"></script>
<script src="jiodev.js" type="text/javascript"></script>
<script src="gadget_global.js" type="text/javascript"></script>
<script src="gadget_erp5_router.js" type="text/javascript"></script>
</head>
<body>
<div data-gadget-url="gadget_jio.html" data-gadget-scope="jio_form_content"></div>
<div data-gadget-url="gadget_jio.html" data-gadget-scope="jio_selection"></div>
<div data-gadget-url="gadget_jio.html" data-gadget-scope="jio_navigation_history"></div>
<div data-gadget-url="gadget_jio.html" data-gadget-scope="jio_document_state"></div>
</body>
</html>
/*global window, rJS, RSVP, loopEventListener, document, jIO, URI, URL, Blob */
/*jslint nomen: true, indent: 2 */
(function (window, rJS, RSVP, loopEventListener, document, jIO, URI, URL, Blob) {
"use strict";
// Keep reference of the latest allDocs params which reach to this view
// var SELECTION_KEY = "s",
// Keep reference in the global navigation pattern
// HISTORY KEY = "h"
// Current display parameter
// DISPLAY KEY = "d"
var PREVIOUS_KEY = "p",
NEXT_KEY = "n",
DROP_KEY = "u",
PREFIX_DISPLAY = "/",
PREFIX_COMMAND = "!",
// PREFIX_ERROR = "?",
// Display a jio document with only the passed parameters
COMMAND_DISPLAY_STATE = "display",
// Store the jio key for the person document of the user
COMMAND_LOGIN = "login",
// Display a raw string URL
COMMAND_RAW = "raw",
// Redisplay the page with the same parameters
COMMAND_RELOAD = "reload",
// Display the latest state stored for a jio document
COMMAND_DISPLAY_STORED_STATE = "display_stored_state",
// Display the current jio document, but change some URL parameters
COMMAND_CHANGE_STATE = "change",
// Like change, but also store the current jio document display state
COMMAND_STORE_AND_CHANGE_STATE = "store_and_change",
// Display one entry index from a selection
COMMAND_INDEX_STATE = "index",
// Display previous entry index from a selection
COMMAND_SELECTION_PREVIOUS = "selection_previous",
// Display next entry index from a selection
COMMAND_SELECTION_NEXT = "selection_next",
// Display previously accessed document
COMMAND_HISTORY_PREVIOUS = "history_previous",
// Store the current document in history and display the next one
COMMAND_PUSH_HISTORY = "push_history",
// Change UI language
COMMAND_CHANGE_LANGUAGE = "change_language",
VALID_URL_COMMAND_DICT = {},
STICKY_PARAMETER_LIST = ['editable'];
VALID_URL_COMMAND_DICT[COMMAND_DISPLAY_STATE] = null;
VALID_URL_COMMAND_DICT[COMMAND_DISPLAY_STORED_STATE] = null;
VALID_URL_COMMAND_DICT[COMMAND_CHANGE_STATE] = null;
VALID_URL_COMMAND_DICT[COMMAND_STORE_AND_CHANGE_STATE] = null;
VALID_URL_COMMAND_DICT[COMMAND_INDEX_STATE] = null;
VALID_URL_COMMAND_DICT[COMMAND_SELECTION_PREVIOUS] = null;
VALID_URL_COMMAND_DICT[COMMAND_SELECTION_NEXT] = null;
VALID_URL_COMMAND_DICT[COMMAND_HISTORY_PREVIOUS] = null;
VALID_URL_COMMAND_DICT[COMMAND_PUSH_HISTORY] = null;
VALID_URL_COMMAND_DICT[COMMAND_LOGIN] = null;
VALID_URL_COMMAND_DICT[COMMAND_RAW] = null;
VALID_URL_COMMAND_DICT[COMMAND_RELOAD] = null;
VALID_URL_COMMAND_DICT[COMMAND_CHANGE_LANGUAGE] = null;
function dropStickyParameterEntry(options) {
// Drop sticky parameters from an options dict
// Do not modify the options parameters, to prevent any unexpected side effect
var i,
result = JSON.parse(JSON.stringify(options));
for (i = 0; i < STICKY_PARAMETER_LIST.length; i += 1) {
delete result[STICKY_PARAMETER_LIST[i]];
}
return result;
}
function copyStickyParameterDict(previous_options, next_options, drop_options) {
var i,
key;
// Keep sticky parameters if they are currently defined in URL
if (drop_options === undefined) {
drop_options = {};
}
for (i = 0; i < STICKY_PARAMETER_LIST.length; i += 1) {
key = STICKY_PARAMETER_LIST[i];
// Check that sticky parameter previously exist and that it was not modified
if (previous_options.hasOwnProperty(key) && (!(next_options.hasOwnProperty(key) || drop_options.hasOwnProperty(key)))) {
next_options[key] = previous_options[key];
}
}
}
function endsWith(str, suffix) {
return str.indexOf(suffix, str.length - suffix.length) !== -1;
}
//////////////////////////////////////////////////////////////////
// Change URL functions
//////////////////////////////////////////////////////////////////
function synchronousChangeState(hash) {
window.location.replace(hash);
// Prevent execution of all next asynchronous code
throw new RSVP.CancellationError('Redirecting to ' + hash);
}
//////////////////////////////////////////////////////////////////
// Selection functions
//////////////////////////////////////////////////////////////////
function getSelection(gadget, selection_id) {
return gadget.props.jio_gadget.get(selection_id)
.push(function (result) {
return result.data;
});
}
function addHistory(gadget, options, previous_selection_id) {
// Drop sticky parameters
options = dropStickyParameterEntry(options);
var options_blob = {
type: "options",
data: options
},
blob_id;
return gadget.props.jio_gadget.post(options_blob)
.push(function (result) {
blob_id = result;
return gadget.props.jio_gadget.get(previous_selection_id)
.push(undefined, function () {
previous_selection_id = undefined;
});
})
.push(function () {
var data_history = {
type: "history",
options_id: blob_id,
previous_history_id: previous_selection_id
};
return gadget.props.jio_gadget.post(data_history);
})
.push(function (id) {
return id;
});
}
function addSelection(gadget, options) {
var data_blob = {
type: "selection",
data: options
};
return gadget.props.jio_gadget.post(data_blob);
}
//////////////////////////////////////////////////////////////////
// Build URL functions
//////////////////////////////////////////////////////////////////
function getCommandUrlFor(gadget, command, options) {
if (command === COMMAND_RAW) {
return options.url;
}
if (command === COMMAND_CHANGE_LANGUAGE) {
return new RSVP.Queue()
.push(function () {
return gadget.getSetting("website_url_set");
})
.push(function (result) {
var param_list = window.location.hash.split('#')[1],
new_url = JSON.parse(result)[options.language];
if (param_list) {
new_url += '#' + param_list;
}
return new_url;
});
}
var result = "#" + PREFIX_COMMAND + (command || ""),
prefix = "?",
key,
tmp,
tmp_dict;
tmp_dict = gadget.props.options;
for (key in tmp_dict) {
if (tmp_dict.hasOwnProperty(key) && (tmp_dict[key] !== undefined)) {
tmp = tmp_dict[key];
if (endsWith(key, ":json")) {
tmp = JSON.stringify(tmp);
}
result += prefix + PREVIOUS_KEY + "." + encodeURIComponent(key) + "=" + encodeURIComponent(tmp);
prefix = "&";
}
}
for (key in options) {
if (options.hasOwnProperty(key)) {
tmp = options[key];
if (tmp === undefined) {
// Key should be dropped from the URL
result += prefix + DROP_KEY + "." + encodeURIComponent(key) + "=";
} else {
if (endsWith(key, ":json")) {
tmp = JSON.stringify(tmp);
}
result += prefix + NEXT_KEY + "." + encodeURIComponent(key) + "=" + encodeURIComponent(tmp);
}
prefix = "&";
}
}
if (command === COMMAND_LOGIN) {
// Build URL template to allow getting user information
result += '{' + prefix + 'n.me}';
}
return result;
}
function getDisplayUrlFor(jio_key, options) {
var prefix = '?',
result,
tmp,
key;
result = "#" + PREFIX_DISPLAY + (jio_key || "");
for (key in options) {
if (options.hasOwnProperty(key) && options[key] !== undefined) {
// Don't keep empty values
tmp = options[key];
if (endsWith(key, ":json")) {
tmp = JSON.stringify(tmp);
}
result += prefix + encodeURIComponent(key) + "=" + encodeURIComponent(tmp);
prefix = '&';
}
}
return result;
}
//////////////////////////////////////////////////////////////////
// navigation history functions
//////////////////////////////////////////////////////////////////
function addNavigationHistoryAndDisplay(gadget, jio_key, options) {
var hash = getDisplayUrlFor(jio_key, options),
queue;
/*jslint regexp: true*/
if (jio_key && /^[^\/]+_module\/[^\/]+$/.test(jio_key)) {
/*jslint regexp: false*/
// This only work for remote access to ERP5...
queue = gadget.props.jio_navigation_gadget.put(jio_key, {
access_time: (new Date().getTime()),
hash: hash
})
.push(function () {
return gadget.props.jio_navigation_gadget.allDocs({
sort_on: [['access_time', 'descending']],
// Max number of history entry
limit: [30, 9999]
});
})
.push(function (result_list) {
// Remove old accessed documents
var i,
promise_list = [];
for (i = 0; i < result_list.data.rows.length; i += 1) {
promise_list.push(
gadget.props.jio_navigation_gadget.remove(result_list.data.rows[i].id)
);
}
return RSVP.all(promise_list);
});
} else {
queue = new RSVP.Queue();
}
return queue
.push(function () {
return synchronousChangeState(hash);
});
}
//////////////////////////////////////////////////////////////////
// exec command functions
//////////////////////////////////////////////////////////////////
function execDisplayCommand(gadget, next_options) {
// console.warn(command_options);
var jio_key = next_options.jio_key;
delete next_options.jio_key;
return addNavigationHistoryAndDisplay(gadget, jio_key, next_options);
}
function execDisplayStoredStateCommand(gadget, next_options) {
// console.warn(command_options);
var jio_key = next_options.jio_key,
queue;
delete next_options.jio_key;
if (jio_key) {
queue = gadget.props.jio_state_gadget.get(jio_key)
.push(function (options) {
// Keep the sticky parameters
copyStickyParameterDict(next_options, options);
next_options = options;
}, function (error) {
if ((error instanceof jIO.util.jIOError) &&
(error.status_code === 404)) {
return;
}
throw error;
});
} else {
queue = new RSVP.Queue();
}
return queue
.push(function () {
return addNavigationHistoryAndDisplay(gadget, jio_key, next_options);
});
}
function calculateChangeOptions(previous_options, next_options, drop_options) {
var key;
for (key in previous_options) {
if (previous_options.hasOwnProperty(key)) {
if (!next_options.hasOwnProperty(key)) {
next_options[key] = previous_options[key];
}
}
}
for (key in drop_options) {
if (drop_options.hasOwnProperty(key)) {
delete next_options[key];
}
}
return next_options;
}
function execChangeCommand(previous_options, next_options, drop_options) {
var options,
jio_key;
options = calculateChangeOptions(previous_options, next_options, drop_options);
jio_key = options.jio_key;
delete options.jio_key;
return synchronousChangeState(
getDisplayUrlFor(jio_key, options)
);
}
function execStoreAndChangeCommand(gadget, previous_options, next_options, drop_options) {
var options,
jio_key,
queue,
display_url;
options = calculateChangeOptions(previous_options, next_options, drop_options);
jio_key = options.jio_key;
delete options.jio_key;
display_url = getDisplayUrlFor(jio_key, options);
if (jio_key) {
queue = gadget.props.jio_state_gadget.put(jio_key, dropStickyParameterEntry(options));
} else {
queue = new RSVP.Queue();
}
return queue
.push(function () {
return synchronousChangeState(display_url);
});
}
function execIndexCommand(gadget, previous_options, next_options) {
var jio_key = next_options.jio_key,
selection_options = {};
delete next_options.jio_key;
// selection_options.index = next_options.index;
selection_options.query = next_options.query;
selection_options.list_method_template = next_options.list_method_template;
selection_options["sort_list:json"] = next_options["sort_list:json"] || [];
// Store selection in local DB
return addSelection(gadget, selection_options)
.push(function (id) {
next_options.selection = id;
// XXX Implement history management
return addHistory(gadget, previous_options);
})
.push(function (id) {
var tmp;
next_options.history = id;
if (gadget.props.form_content) {
tmp = gadget.props.form_content;
delete gadget.props.form_content;
return gadget.props.jio_form_content.putAttachment('/', id, new Blob([JSON.stringify(tmp)], {type: "application/json"}));
}
})
.push(function () {
return addNavigationHistoryAndDisplay(gadget, jio_key, next_options);
});
}
function execPushHistoryCommand(gadget, previous_options, next_options) {
var jio_key = next_options.jio_key;
delete next_options.jio_key;
// XXX Hack to support create dialog
delete previous_options.view;
delete previous_options.page;
return addHistory(gadget, previous_options)
.push(function (id) {
next_options.history = id;
return addNavigationHistoryAndDisplay(gadget, jio_key, next_options);
});
}
function execSelectionNextCommand(gadget, previous_options) {
if (previous_options.selection === undefined) {
return synchronousChangeState(
getCommandUrlFor(gadget, COMMAND_HISTORY_PREVIOUS, previous_options)
);
}
// Get the selection parameters
// Query all docs with those parameters + expected index
// Redirect to the result document
return getSelection(gadget, previous_options.selection)
.push(function (selection) {
return gadget.jio_allDocs({
"query": selection.query,
"list_method_template": selection.list_method_template,
"limit": [parseInt(previous_options.selection_index || '0', 10) + 1, 1],
"sort_on": selection["sort_list:json"]
})
.push(function (result) {
if (result.data.rows.length === 0) {
return synchronousChangeState(
getCommandUrlFor(gadget, COMMAND_HISTORY_PREVIOUS, previous_options)
);
}
var options = {
selection: previous_options.selection,
selection_index: parseInt(previous_options.selection_index || '0', 10) + 1,
history: previous_options.history
};
copyStickyParameterDict(previous_options, options);
return addNavigationHistoryAndDisplay(
gadget,
result.data.rows[0].id,
options
);
});
}, function (error) {
if ((error instanceof jIO.util.jIOError) &&
(error.status_code === 404)) {
return synchronousChangeState(
getCommandUrlFor(gadget, COMMAND_HISTORY_PREVIOUS, previous_options)
);
}
throw error;
});
}
function execSelectionPreviousCommand(gadget, previous_options) {
if (previous_options.selection === undefined) {
return synchronousChangeState(
getCommandUrlFor(gadget, COMMAND_HISTORY_PREVIOUS, previous_options)
);
}
// Get the selection parameters
// Query all docs with those parameters + expected index
// Redirect to the result document
return getSelection(gadget, previous_options.selection)
.push(function (selection) {
if (parseInt(previous_options.selection_index || '0', 10) < 1) {
return synchronousChangeState(
getCommandUrlFor(gadget, COMMAND_HISTORY_PREVIOUS, previous_options)
);
}
return gadget.jio_allDocs({
"query": selection.query,
"list_method_template": selection.list_method_template,
"limit": [parseInt(previous_options.selection_index, 10) - 1, 1],
"sort_on": selection["sort_list:json"]
})
.push(function (result) {
if (result.data.rows.length === 0) {
return synchronousChangeState(
getCommandUrlFor(gadget, COMMAND_HISTORY_PREVIOUS, previous_options)
);
}
var options = {
selection: previous_options.selection,
selection_index: parseInt(previous_options.selection_index, 10) - 1,
history: previous_options.history
};
copyStickyParameterDict(previous_options, options);
return addNavigationHistoryAndDisplay(
gadget,
result.data.rows[0].id,
options
);
});
}, function (error) {
if ((error instanceof jIO.util.jIOError) &&
(error.status_code === 404)) {
return synchronousChangeState(
getCommandUrlFor(gadget, COMMAND_HISTORY_PREVIOUS, previous_options)
);
}
throw error;
});
}
function redirectToParent(gadget, jio_key, previous_options) {
return gadget.jio_getAttachment(jio_key, "links")
.push(function (erp5_document) {
var parent_link = erp5_document._links.parent,
uri,
options = {};
if (parent_link !== undefined) {
uri = new URI(parent_link.href);
copyStickyParameterDict(previous_options, options);
return addNavigationHistoryAndDisplay(gadget, uri.segment(2), options);
}
});
}
function execHistoryPreviousCommand(gadget, previous_options, load_options) {
var history = previous_options.history,
jio_key = previous_options.jio_key,
relation_index = previous_options.relation_index,
field = previous_options.back_field,
queue = new RSVP.Queue(),
previous_id;
if (history === undefined) {
if (jio_key !== undefined) {
return redirectToParent(gadget, jio_key, previous_options);
}
}
if (previous_options.back_field) {
queue
.push(function () {
return gadget.props.jio_form_content.getAttachment('/', history);
})
.push(function (results) {
return jIO.util.readBlobAsText(results);
}, function (error) {
if ((error instanceof jIO.util.jIOError) &&
(error.status_code === 404)) {
return;
}
throw error;
})
.push(function (results) {
if (results) {
results = JSON.parse(results.target.result);
if (load_options.uid) {
results[field].value_text_list[relation_index] = "";
results[field].value_relative_url_list[relation_index] = load_options.jio_key;
results[field].value_uid_list[relation_index] = load_options.uid;
}
gadget.props.form_content = results;
}
});
}
queue
.push(function () {
return gadget.props.jio_gadget.get(history);
})
.push(function (history) {
previous_id = history.previous_history_id;
return gadget.props.jio_gadget.get(history.options_id);
})
.push(function (result) {
var result_list = [result, previous_id],
options = result_list[0].data,
next_jio_key = options.jio_key;
delete options.jio_key;
copyStickyParameterDict(previous_options, options);
return addNavigationHistoryAndDisplay(gadget, next_jio_key, options);
}, function (error) {
// XXX Check if 404
if ((error instanceof jIO.util.jIOError) &&
(error.status_code === 404)) {
return redirectToParent(gadget, jio_key, previous_options);
// return [{data: {}}, undefined];
}
throw error;
});
return queue;
}
function execLoginCommand(gadget, previous_options, next_options) {
var me = next_options.me;
return gadget.setSetting('me', me)
.push(function () {
return execDisplayCommand(gadget, previous_options);
});
}
function execReloadCommand(previous_options) {
var jio_key = previous_options.jio_key;
delete previous_options.jio_key;
return synchronousChangeState(
getDisplayUrlFor(jio_key, previous_options)
);
}
//////////////////////////////////////////////////////////////////
// Command URL functions
//////////////////////////////////////////////////////////////////
function routeMethodLess(gadget, previous_options) {
// Nothing. Go to front page
// If no frontpage is configured, his may comes from missing configuration on website
// or default HTML gadget modification date more recent than the website modification date
return gadget.getSetting("frontpage_gadget")
.push(function (result) {
var options = {page: result};
if (previous_options === undefined) {
previous_options = {};
}
copyStickyParameterDict(previous_options, options);
return synchronousChangeState(
getDisplayUrlFor(undefined, options)
);
});
}
function routeDisplay(gadget, command_options) {
if (command_options.path) {
if (command_options.args.page === undefined) {
return gadget.getSetting("jio_document_page_gadget", "form")
.push(function (jio_document_page_gadget) {
return synchronousChangeState(
getDisplayUrlFor(command_options.path, {
page: jio_document_page_gadget,
editable: command_options.args.editable,
view: command_options.args.view || 'view',
selection: command_options.args.selection,
selection_index: command_options.args.selection_index,
history: command_options.args.history
})
);
});
}
}
if (command_options.args.page === undefined) {
return routeMethodLess(gadget, command_options.args);
}
command_options.args.jio_key = command_options.path || undefined;
// Store current options to handle navigation
gadget.props.options = JSON.parse(JSON.stringify(command_options.args));
if (command_options.args.page === 'history') {
// This is an adhoc route to handle local navigation history
return gadget.props.jio_navigation_gadget.allDocs({
sort_on: [['access_time', 'descending']]
})
.push(function (result) {
var result_list = result.data.rows,
id_list = [],
i;
for (i = 0; i < result_list.length; i += 1) {
id_list.push(result_list[i].id);
}
return {
url: "gadget_erp5_page_" + command_options.args.page + ".html",
// XXX Drop this options thing.
// Implement a "getSelection" method
options: {
id_list: id_list
}
};
});
}
if (gadget.props.form_content) {
command_options.args.form_content = gadget.props.form_content;
delete gadget.props.form_content;
}
return {
url: "gadget_erp5_page_" + command_options.args.page + ".html",
// XXX Drop this options thing.
// Implement a "getSelection" method
options: command_options.args
// options: {}
};
}
function routeCommand(gadget, command_options) {
var args = command_options.args,
key,
split_list,
previous_options = {},
next_options = {},
drop_options = {},
valid = true;
// Rebuild the previous and next parameter dict
for (key in args) {
if (args.hasOwnProperty(key)) {
split_list = key.split('.', 2);
if (split_list.length !== 2) {
valid = false;
break;
}
if (split_list[0] === PREVIOUS_KEY) {
previous_options[split_list[1]] = args[key];
} else if (split_list[0] === NEXT_KEY) {
next_options[split_list[1]] = args[key];
} else if (split_list[0] === DROP_KEY) {
drop_options[split_list[1]] = args[key];
} else {
valid = false;
break;
}
}
}
if (!valid) {
throw new Error('Unsupported parameters: ' + key);
}
// Do not calculate this while generating the URL string to not do this too much time
copyStickyParameterDict(previous_options, next_options, drop_options);
if (command_options.path === COMMAND_DISPLAY_STATE) {
return execDisplayCommand(gadget, next_options);
}
if (command_options.path === COMMAND_DISPLAY_STORED_STATE) {
return execDisplayStoredStateCommand(gadget, next_options);
}
if (command_options.path === COMMAND_INDEX_STATE) {
return execIndexCommand(gadget, previous_options, next_options);
}
if (command_options.path === COMMAND_CHANGE_STATE) {
return execChangeCommand(previous_options, next_options, drop_options);
}
if (command_options.path === COMMAND_STORE_AND_CHANGE_STATE) {
return execStoreAndChangeCommand(gadget, previous_options, next_options, drop_options);
}
if (command_options.path === COMMAND_SELECTION_NEXT) {
return execSelectionNextCommand(gadget, previous_options);
}
if (command_options.path === COMMAND_SELECTION_PREVIOUS) {
return execSelectionPreviousCommand(gadget, previous_options);
}
if (command_options.path === COMMAND_HISTORY_PREVIOUS) {
return execHistoryPreviousCommand(gadget, previous_options, next_options);
}
if (command_options.path === COMMAND_PUSH_HISTORY) {
return execPushHistoryCommand(gadget, previous_options, next_options);
}
if (command_options.path === COMMAND_LOGIN) {
return execLoginCommand(gadget, previous_options, next_options);
}
if (command_options.path === COMMAND_RELOAD) {
return execReloadCommand(previous_options);
}
throw new Error('Unsupported command ' + command_options.path);
}
function listenHashChange(gadget) {
// Handle hash in this format: #$path1/path2?a=b&c=d
function extractHashAndDispatch(evt) {
var hash = (evt.newURL || window.location.toString()).split('#')[1],
split,
command = "",
query = "",
subhashes,
subhash,
keyvalue,
index,
key,
tmp,
args = {};
if (hash !== undefined) {
split = hash.split('?');
command = split[0] || "";
query = split[1] || "";
}
subhashes = query.split('&');
for (index in subhashes) {
if (subhashes.hasOwnProperty(index)) {
subhash = subhashes[index];
if (subhash !== '') {
keyvalue = subhash.split('=');
if (keyvalue.length === 2) {
key = decodeURIComponent(keyvalue[0].replace(/\+/gm, "%20"));
tmp = decodeURIComponent(keyvalue[1].replace(/\+/gm, "%20"));
if (tmp && (endsWith(key, ":json"))) {
tmp = JSON.parse(tmp);
}
args[key] = tmp;
}
}
}
}
return gadget.route({
method: command[0],
path: command.substr(1),
args: args
});
}
function catchError(evt) {
return new RSVP.Queue()
.push(function () {
return extractHashAndDispatch(evt);
})
.push(undefined, function (error) {
return gadget.renderError(error);
});
}
var result = loopEventListener(window, 'hashchange', false,
catchError),
event = document.createEvent("Event");
event.initEvent('hashchange', true, true);
event.newURL = window.location.toString();
window.dispatchEvent(event);
return result;
}
rJS(window)
.ready(function (gadget) {
gadget.props = {
options: {}
};
})
.ready(function (gadget) {
return gadget.getDeclaredGadget("jio_selection")
.push(function (jio_gadget) {
gadget.props.jio_gadget = jio_gadget;
return jio_gadget.createJio({
type: "sha",
sub_storage: {
type: "indexeddb",
database: "selection"
}
});
});
})
.ready(function (gadget) {
return gadget.getDeclaredGadget("jio_navigation_history")
.push(function (jio_gadget) {
gadget.props.jio_navigation_gadget = jio_gadget;
return jio_gadget.createJio({
type: "query",
sub_storage: {
type: "indexeddb",
database: "navigation_history"
}
});
});
})
.ready(function (gadget) {
return gadget.getDeclaredGadget("jio_document_state")
.push(function (jio_gadget) {
gadget.props.jio_state_gadget = jio_gadget;
return jio_gadget.createJio({
type: "indexeddb",
database: "document_state"
});
});
})
.ready(function (g) {
return g.getDeclaredGadget("jio_form_content")
.push(function (jio_form_content) {
g.props.jio_form_content = jio_form_content;
return jio_form_content.createJio({
type: "local",
sessiononly: true
});
});
})
.declareMethod('getCommandUrlFor', function (options) {
var command = options.command,
absolute_url = options.absolute_url,
hash,
args = options.options,
valid = true,
key;
// Only authorize 'command', 'options', 'absolute_url' keys
// Drop all other kind of parameters, to detect issue more easily
for (key in options) {
if (options.hasOwnProperty(key)) {
if ((key !== 'command') && (key !== 'options') && (key !== 'absolute_url')) {
valid = false;
}
}
}
if (valid && (options.command) && (VALID_URL_COMMAND_DICT.hasOwnProperty(options.command))) {
hash = getCommandUrlFor(this, command, args);
} else {
hash = getCommandUrlFor(this, 'error', options);
}
if (absolute_url) {
hash = new URL(hash, window.location.href).href;
}
return hash;
})
.declareMethod('redirect', function (options) {
this.props.form_content = options.form_content;
// XXX Should we make it a second method parameter
this.props.keep_message = true;
delete options.form_content;
return this.getCommandUrlFor(options)
.push(function (hash) {
return synchronousChangeState(hash);
});
})
.declareMethod('getUrlParameter', function (key) {
return this.props.options[key];
})
.declareMethod('route', function (command_options) {
var gadget = this,
result;
if (command_options.method === PREFIX_DISPLAY) {
result = routeDisplay(gadget, command_options);
} else if (command_options.method === PREFIX_COMMAND) {
result = routeCommand(gadget, command_options);
} else {
if (command_options.method) {
throw new Error('Unsupported hash method: ' + command_options.method);
}
result = routeMethodLess(gadget, command_options.args);
}
return new RSVP.Queue()
.push(function () {
return result;
})
.push(function (route_result) {
if ((route_result !== undefined) && (route_result.url !== undefined)) {
return gadget.renderApplication(route_result, gadget.props.keep_message)
.push(function (result) {
gadget.props.keep_message = false;
return result;
});
}
});
})
.declareMethod('start', function () {
var gadget = this;
return new RSVP.Queue()
.push(function () {
return RSVP.all([
gadget.getSetting("selected_language"),
gadget.getSetting("default_selected_language"),
gadget.getSetting("language_map")
]);
})
.push(function (results) {
if (results[1] !== results[0] && results[0] && JSON.parse(results[2]).hasOwnProperty(results[0])) {
return gadget.redirect({
command: COMMAND_CHANGE_LANGUAGE,
options: {
language: results[0]
}
});
}
return gadget.listenHashChange();
})
.push(undefined, function (error) {
if (error instanceof RSVP.CancellationError) {
return;
}
throw error;
});
})
.declareAcquiredMethod('renderApplication', 'renderApplication')
.declareAcquiredMethod('jio_allDocs', 'jio_allDocs')
.declareAcquiredMethod('jio_getAttachment', 'jio_getAttachment')
.declareAcquiredMethod('setSetting', 'setSetting')
.declareAcquiredMethod('getSetting', 'getSetting')
.declareAcquiredMethod('renderError', 'reportServiceError')
.declareJob('listenHashChange', function () {
return listenHashChange(this);
});
}(window, rJS, RSVP, loopEventListener, document, jIO, URI, URL, Blob));
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, user-scalable=no" />
<title>ERP5 Search Field</title>
<!-- renderjs -->
<script src="rsvp.js"></script>
<script src="renderjs.js"></script>
<!-- custom script -->
<script src="gadget_erp5_searchfield.js"></script>
</head>
<body>
<div class="ui-input-text ui-input-has-icon ui-corner-all ui-shadow-inset ui-input-has-clear">
<div data-gadget-url="gadget_html5_input.html"
data-gadget-scope="input"
data-gadget-sandbox="public"></div>
<button class="search_button ui-shadow-inset ui-btn ui-btn-inline ui-corner-all ui-btn-icon-notext ui-icon-search ui-override-theme ui-input-btn" data-role="button" data-enhanced="true" type="submit" />
</div>
</body>
</html>
\ No newline at end of file
/*global window, rJS */
/*jslint nomen: true, indent: 2, maxerr: 3 */
(function (window, rJS) {
"use strict";
rJS(window)
/////////////////////////////////////////////////////////////////
// declared methods
/////////////////////////////////////////////////////////////////
.declareMethod('render', function (options) {
var state_dict = {
extended_search: options.extended_search || "",
focus: options.focus
};
return this.changeState(state_dict);
})
.onStateChange(function () {
var gadget = this;
return gadget.getDeclaredGadget('input')
.push(function (input_gadget) {
var focus = gadget.state.focus; // undefined focus is fine as well
if (gadget.state.focus === undefined && !gadget.state.extended_search) {
focus = true;
}
return input_gadget.render({
type: "search",
value: gadget.state.extended_search,
focus: focus,
name: "search",
editable: true
});
});
})
.allowPublicAcquisition("notifyValid", function () {return; })
.declareMethod('getContent', function () {
return this.getDeclaredGadget('input')
.push(function (input_gadget) {
return input_gadget.getContent();
})
.push(function (result) {
if (result.search) {
// XXX trim from input gadget?
result.search = result.search.trim();
}
return result;
});
});
}(window, rJS));
\ No newline at end of file
/*global window, RSVP, FileReader */
/*jslint indent: 2, maxerr: 3, unparam: true */
(function (window, RSVP, FileReader) {
"use strict";
window.loopEventListener = function (target, type, useCapture, callback,
prevent_default) {
//////////////////////////
// Infinite event listener (promise is never resolved)
// eventListener is removed when promise is cancelled/rejected
//////////////////////////
var handle_event_callback,
callback_promise;
if (prevent_default === undefined) {
prevent_default = true;
}
function cancelResolver() {
if ((callback_promise !== undefined) &&
(typeof callback_promise.cancel === "function")) {
callback_promise.cancel();
}
}
function canceller() {
if (handle_event_callback !== undefined) {
target.removeEventListener(type, handle_event_callback, useCapture);
}
cancelResolver();
}
function itsANonResolvableTrap(resolve, reject) {
var result;
handle_event_callback = function (evt) {
if (prevent_default) {
evt.stopPropagation();
evt.preventDefault();
}
cancelResolver();
try {
result = callback(evt);
} catch (e) {
result = RSVP.reject(e);
}
callback_promise = result;
new RSVP.Queue()
.push(function () {
return result;
})
.push(undefined, function (error) {
if (!(error instanceof RSVP.CancellationError)) {
canceller();
reject(error);
}
});
};
target.addEventListener(type, handle_event_callback, useCapture);
}
return new RSVP.Promise(itsANonResolvableTrap, canceller);
};
window.promiseEventListener = function (target, type, useCapture) {
//////////////////////////
// Resolve the promise as soon as the event is triggered
// eventListener is removed when promise is cancelled/resolved/rejected
//////////////////////////
var handle_event_callback;
function canceller() {
target.removeEventListener(type, handle_event_callback, useCapture);
}
function resolver(resolve) {
handle_event_callback = function (evt) {
canceller();
evt.stopPropagation();
evt.preventDefault();
resolve(evt);
return false;
};
target.addEventListener(type, handle_event_callback, useCapture);
}
return new RSVP.Promise(resolver, canceller);
};
window.promiseReadAsText = function (file) {
return new RSVP.Promise(function (resolve, reject) {
var reader = new FileReader();
reader.onload = function (evt) {
resolve(evt.target.result);
};
reader.onerror = function (evt) {
reject(evt);
};
reader.readAsText(file);
});
};
window.promiseDoWhile = function (loopFunction, input) {
// calls loopFunction(input) until it returns a non positive value
// this queue is to protect the inner loop queue from the
// `promiseDoWhile` caller, avoiding it to enqueue the inner
// loop queue.
return new RSVP.Queue()
.push(function () {
// here is the inner loop queue
var loop_queue = new RSVP.Queue();
function iterate(previous_iteration_result) {
if (!previous_iteration_result) {
return input;
}
loop_queue.push(iterate);
return loopFunction(input);
}
return loop_queue
.push(function () {
return loopFunction(input);
})
.push(iterate);
});
};
}(window, RSVP, FileReader));
\ No newline at end of file
<!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>HTML5 Input</title>
<!-- renderjs -->
<script src="rsvp.js" type="text/javascript"></script>
<script src="renderjs.js" type="text/javascript"></script>
<script src="jiodev.js" type="text/javascript"></script>
<!-- custom script -->
<script src="gadget_html5_input.js" type="text/javascript"></script>
</head>
<body><input /></body>
</html>
\ No newline at end of file
/*jslint indent: 2, maxerr: 3, nomen: true, maxlen: 80 */
/*global window, rJS, RSVP, URI, Handlebars,
SimpleQuery, ComplexQuery, Query, QueryFactory*/
(function (window, rJS, RSVP, URI,
SimpleQuery, ComplexQuery, Query, QueryFactory, Handlebars) {
"use strict";
var gadget_klass = rJS(window),
relation_link_source = gadget_klass.__template_element
.getElementById("relation-link-template")
.innerHTML,
relation_link_template = Handlebars.compile(relation_link_source),
relation_input_source = gadget_klass.__template_element
.getElementById("relation-input-template")
.innerHTML,
relation_input_template = Handlebars.compile(relation_input_source),
relation_listview_source = gadget_klass.__template_element
.getElementById("relation-listview-template")
.innerHTML,
relation_listview_template = Handlebars.compile(relation_listview_source);
function displayNonEditableLink(gadget) {
return gadget.getUrlFor({
command: 'index',
options: {
jio_key: gadget.state.value_relative_url
}
})
.push(function (href) {
// XXX Use html5 element gadget
gadget.element.innerHTML = relation_link_template({
value: gadget.state.value_text,
href: href
});
});
}
function displayNonEditableText(gadget) {
gadget.element.textContent = gadget.state.value_text;
}
function displayEditableLink(gadget, class_name) {
return gadget.translateHtml(relation_input_template({
href: "#",
value: gadget.state.value_text,
title: gadget.state.title,
name: gadget.state.key,
class_name: class_name
}))
.push(function (html) {
gadget.element.innerHTML = html;
});
}
gadget_klass
/////////////////////////////////////////////////////////////////
// acquired methods
/////////////////////////////////////////////////////////////////
.declareAcquiredMethod("notifyInvalid", "notifyInvalid")
.declareAcquiredMethod("notifyChange", "notifyChange")
.declareAcquiredMethod("jio_allDocs", "jio_allDocs")
.declareAcquiredMethod("getUrlFor", "getUrlFor")
.declareAcquiredMethod("redirect", "redirect")
.declareAcquiredMethod("getFormContent", "getFormContent")
.declareAcquiredMethod("translateHtml", "translateHtml")
// .declareAcquiredMethod("addRelationInput", "addRelationInput")
/////////////////////////////////////////////////////////////////
// declared methods
/////////////////////////////////////////////////////////////////
.declareMethod('render', function (options) {
var state_dict = {
editable: options.editable,
query: options.query,
catalog_index: options.catalog_index,
allow_jump: options.allow_jump,
// required: field_json.required,
title: options.title,
key: options.key,
view: options.view,
url: options.url,
allow_creation: options.allow_creation,
portal_types: options.portal_types,
translated_portal_types: options.translated_portal_types,
has_focus: false,
relation_index: options.relation_index,
value_relative_url: options.value_relative_url,
value_uid: options.value_uid,
value_text: options.value_text,
value_portal_type: options.value_portal_type
};
return this.changeState(state_dict);
})
.declareJob('detachChangeState', function (value_uid, catalog_index) {
var gadget = this;
return gadget.jio_allDocs({
"query": Query.objectToSearchText(new SimpleQuery({
key: "catalog.uid",
value: value_uid
})),
"limit": [0, 1],
"select_list": [catalog_index]
})
.push(function (result) {
return gadget.changeState({
value_text: result.data.rows[0]
.value[catalog_index]
});
})
.push(function () {
return gadget.notifyChange();
});
})
.onStateChange(function (modification_dict) {
var gadget = this,
queue = new RSVP.Queue(),
value_text = gadget.state.value_text,
// target_url,
SEARCHING_CLASS_STR = "ui-btn ui-corner-all ui-btn-icon-notext" +
" ui-input-clear ui-icon-spinner ui-icon-spin",
JUMP_ON_CLASS_STR = "ui-btn ui-corner-all ui-btn-icon-notext " +
"ui-icon-plane ui-shadow-inset ui-btn-inline",
JUMP_OFF_CLASS_STR = "ui-btn ui-corner-all ui-btn-icon-notext " +
"ui-icon-plane ui-shadow-inset ui-btn-inline ui-disabled",
JUMP_ADD_CLASS_STR = "ui-btn ui-corner-all ui-btn-icon-notext " +
"ui-icon-plus ui-shadow-inset ui-btn-inline ui-disabled",
JUMP_UNKNOWN_CLASS_STR = "ui-btn ui-corner-all ui-btn-icon-notext " +
"ui-icon-warning ui-shadow-inset ui-btn-inline ui-disabled";
// Non editable
if (!gadget.state.editable) {
if ((gadget.state.value_relative_url) && (gadget.state.allow_jump)) {
return displayNonEditableLink(gadget);
}
return displayNonEditableText(gadget);
}
if (modification_dict.hasOwnProperty('editable')) {
// First display of the input
queue.push(function () {
return displayEditableLink(gadget, JUMP_UNKNOWN_CLASS_STR);
});
}
return queue
.push(function () {
var plane = gadget.element.querySelector("a"),
ul = gadget.element.querySelector(".search_ul"),
input = gadget.element.querySelector("input");
ul.innerHTML = "";
plane.href = '';
if (input.value !== gadget.state.value_text) {
input.value = gadget.state.value_text;
}
// uid is known
// User selected a document from a listbox
if ((gadget.state.value_uid) && (!gadget.state.value_text)) {
plane.className = SEARCHING_CLASS_STR;
return gadget.detachChangeState(gadget.state.value_uid,
gadget.state.catalog_index);
}
// Expected portal type has been selected.
// User want to create a new document
if (gadget.state.value_portal_type) {
plane.className = JUMP_ADD_CLASS_STR;
return;
}
// Relative URL is known. Display plane icon
if (gadget.state.value_relative_url) {
if (gadget.state.allow_jump) {
return gadget.getUrlFor({
command: 'index',
options: {
jio_key: gadget.state.value_relative_url
}
})
.push(function (url) {
plane.href = url;
plane.className = JUMP_ON_CLASS_STR;
});
}
if (modification_dict.hasOwnProperty('value_text')) {
plane.className = JUMP_UNKNOWN_CLASS_STR;
} else {
plane.className = JUMP_OFF_CLASS_STR;
}
return;
}
// No text, user want to delete the content
if (!gadget.state.value_text) {
plane.className = JUMP_OFF_CLASS_STR;
return;
}
// User entered text, but didn't select
// from the list
if (!gadget.state.has_focus) {
plane.className = JUMP_UNKNOWN_CLASS_STR;
return;
}
// User typed some text.
// Propose some documents in a list
plane.className = SEARCHING_CLASS_STR;
return new RSVP.Queue()
.push(function () {
// Wait a bit before launching the catalog query
// as user may still type new characters
return RSVP.delay(200);
})
.push(function () {
return gadget.jio_allDocs({
query: Query.objectToSearchText(new ComplexQuery({
operator: "AND",
query_list: [
QueryFactory.create(
new URI(gadget.state.query).query(true).query
),
new SimpleQuery({
key: gadget.state.catalog_index,
value: value_text
})
]
})),
limit: [0, 10],
select_list: [gadget.state.catalog_index, "uid"]
});
})
.push(function (result) {
var list = [],
i,
type = [];
if (gadget.state.allow_creation) {
for (i = 0; i < gadget.state.portal_types.length; i += 1) {
type.push({
name: gadget.state.translated_portal_types[i],
value: gadget.state.portal_types[i]
});
}
}
for (i = 0; i < result.data.rows.length; i += 1) {
list.push({
id: result.data.rows[i].id,
value: result.data.rows[i].value[gadget.state.catalog_index],
uid: result.data.rows[i].value.uid
});
}
plane.className = JUMP_UNKNOWN_CLASS_STR;
return gadget.translateHtml(relation_listview_template({
list: list,
type: type,
value: value_text
}));
})
.push(function (html) {
ul.innerHTML = html;
});
});
})
.declareMethod('getContent', function () {
var gadget = this,
result = {};
if (gadget.state.editable) {
result = {
value_relative_url: gadget.state.value_relative_url,
value_text: gadget.state.value_text,
value_portal_type: gadget.state.value_portal_type,
value_uid: gadget.state.value_uid
};
}
return result;
})
/////////////////////////////////////////////////////////////////
// declared services
/////////////////////////////////////////////////////////////////
.onEvent('click', function (evt) {
var gadget = this,
li,
data_relative_url,
data_uid,
data_portal_type,
data_explore,
new_state = {};
if (evt.target.tagName.toLowerCase() === 'li') {
li = evt.target;
data_relative_url = li.getAttribute("data-relative-url");
data_uid = li.getAttribute("data-uid");
data_portal_type = li.getAttribute("data-create-object");
data_explore = li.getAttribute("data-explore");
// User want to create a new document
if (data_portal_type) {
new_state.value_portal_type = data_portal_type;
return gadget.changeState(new_state);
}
// User selected an existing document
if (data_relative_url) {
new_state.value_text = li.textContent;
new_state.value_relative_url = data_relative_url;
new_state.value_uid = data_uid;
return gadget.changeState(new_state);
}
// Go to the search listbox
if (data_explore) {
return gadget.getFormContent({
format: "json"
})
.push(function (content) {
var input = gadget.element.querySelector('input');
return gadget.redirect({
command: 'index',
options: {
page: "relation_search",
url: gadget.state.url,
extended_search: Query.objectToSearchText(
new SimpleQuery({
key: gadget.state.catalog_index,
value: input.value
})
),
view: gadget.state.view,
back_field: gadget.state.key,
relation_index: gadget.state.relation_index
},
form_content: content
});
});
}
}
}, false, false)
.onEvent('blur', function (evt) {
if (evt.target === this.element.querySelector(".search_ul")) {
return this.changeState({
has_focus: false
});
}
}, true, false)
.declareMethod('checkValidity', function () {
return true;
})
// XXX Use html5 input
.onEvent('invalid', function (evt) {
// invalid event does not bubble
return this.notifyInvalid(evt.target.validationMessage);
}, true, false)
.onEvent('change', function () {
return RSVP.all([
this.checkValidity(),
this.notifyChange()
]);
}, false, false)
.onEvent('input', function (event) {
if (!this.state.editable) {
return;
}
var context = this;
return this.changeState({
value_text: event.target.value,
value_relative_url: null,
value_uid: null,
value_portal_type: null,
has_focus: true
})
.push(function () {
return context.notifyChange();
});
}, true, false);
}(window, rJS, RSVP, URI,
SimpleQuery, ComplexQuery, Query, QueryFactory, Handlebars));
<!DOCTYPE html>
<html>
<head>
<!--
<script data-renderjs-configuration="application_title" type="text/x-renderjs-configuration">ERP5</script>
<script data-renderjs-configuration="panel_gadget" type="text/x-renderjs-configuration">gadget_erp5_panel.html</script>
<script data-renderjs-configuration="action_view" type="text/x-renderjs-configuration">object_view</script>
......@@ -12,56 +14,21 @@
<script data-renderjs-configuration="default_selected_language" type="text/x-renderjs-configuration">en</script>
<script data-renderjs-configuration="website_url_set" type="text/x-renderjs-configuration">{"en": "http://softinst86155.host.vifib.net/erp5/web_site_module/renderjs_runner/"}</script>
<script data-renderjs-configuration="service_worker_url" type="text/x-renderjs-configuration"></script>
-->
<script src="rsvp.js"></script>
<script src="renderjs.js"></script>
<script src="erp5_launcher_nojqm.js"></script>
<script src="gadget_jupyter_page.js"></script>
</head>
<body>
<div data-role="page">
<div data-gadget-url="gadget_jio.html"
data-gadget-scope="setting_gadget"
data-gadget-sandbox="public"></div>
<div data-gadget-url="gadget_erp5_router.html"
data-gadget-scope="router"
data-gadget-sandbox="public"></div>
<!--
<div data-gadget-url="gadget_erp5_notification.html"
data-gadget-scope="notification"
data-gadget-sandbox="public"></div>
<div data-gadget-url="gadget_translation.html"
data-gadget-scope="translation_gadget"
data-gadget-sandbox="public"></div>
<div data-gadget-url="gadget_erp5_header.html"
data-gadget-scope="header"
data-gadget-sandbox="public"></div>
-->
<div data-gadget-url="gadget_erp5_jio.html"
data-gadget-scope="jio_gadget"
data-gadget-sandbox="public"></div>
<!--
<div data-gadget-url="gadget_erp5_editor_panel.html"
data-gadget-scope="editor_panel"
data-gadget-sandbox="public"></div>
-->
<!--
<div data-gadget-url="gadget_erp5_panel.html"
data-gadget-scope="panel"
data-gadget-sandbox="public"></div>
-->
<div role="main" class="ui-content gadget-content">
<div class="ui-icon-spinner ui-btn-icon-notext first-loader">Hello renderJS + Jupyter</div>
</div>
<div>Hello renderJS + Jupyter</div>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, user-scalable=no" />
<title>Translation Gadget</title>
<!-- renderjs -->
<script src="rsvp.js" type="text/javascript"></script>
<script src="renderjs.js" type="text/javascript"></script>
<!-- custom script -->
<script src="gadget_translation_data.js" type="text/javascript"></script>
<script src="gadget_translation.js" type="text/javascript"></script>
</head>
<body>
</body>
</html>
/*global document, window, rJS, translation_data, RSVP */
/*jslint nomen: true, indent: 2 */
(function (document, window, rJS, translation_data, RSVP) {
"use strict";
function translate(string, gadget) {
if (translation_data[gadget.state.language]) {
return translation_data[gadget.state.language][string] || string;
}
return string;
}
// translate a list of elements passed and returned as string
function translateHtml(string, gadget) {
var temp, element_list, i, i_len, element, lookup, translate_list, target,
route_text, has_breaks, l, l_len, j, j_len;
// NOTE: <div> cannot be used for everything... (like table rows)
// XXX: currently I only update where needed. Eventually all calls to
// translateHtml should pass "their" proper wrapping element
temp = document.createElement("div");
temp.innerHTML = string;
element_list = temp.querySelectorAll("[data-i18n]");
for (i = 0, i_len = element_list.length; i < i_len; i += 1) {
element = element_list[i];
lookup = element.getAttribute("data-i18n");
if (lookup) {
translate_list = lookup.split(";");
for (l = 0, l_len = translate_list.length; l < l_len; l += 1) {
target = translate_list[l].split("]");
switch (target[0]) {
case "[placeholder":
case "[alt":
case "[title":
element.setAttribute(target[0].substr(1), translate(target[1], gadget));
break;
case "[value":
has_breaks = element.previousSibling.textContent.match(/\n/g);
// JQM inputs > this avoids calling checkboxRadio("refresh")!
if (element.tagName === "INPUT") {
switch (element.type) {
case "submit":
case "reset":
case "button":
route_text = true;
break;
}
}
if (route_text && (has_breaks || []).length === 0) {
element.previousSibling.textContent = translate(target[1], gadget);
}
element.value = translate(target[1], gadget);
break;
case "[parent":
element.parentNode.childNodes[0].textContent =
translate(target[1], gadget);
break;
case "[node":
element.childNodes[0].textContent = translate(target[1], gadget);
break;
case "[last":
// if null, append, if textnode replace, if span, appned
if (element.lastChild && element.lastChild.nodeType === 3) {
element.lastChild.textContent = translate(target[1], gadget);
} else {
element.appendChild(document.createTextNode(translate(target[1], gadget)));
}
break;
case "[html":
element.innerHTML = translate(target[1], gadget);
break;
default:
if (element.hasChildNodes()) {
for (j = 0, j_len = element.childNodes.length; j < j_len; j += 1) {
if (element.childNodes[j].nodeType === 3) {
element.childNodes[j].textContent = translate(translate_list[l], gadget);
}
}
} else {
element.textContent = translate(translate_list[l], gadget);
}
break;
}
}
}
}
// return string
return temp.innerHTML;
}
rJS(window)
.declareAcquiredMethod("getSetting", "getSetting")
.declareMethod('translate', function (string) {
// XXX Allow to change the language
var gadget = this;
if (!gadget.state.language) {
return new RSVP.Queue()
.push(function () {
return RSVP.all([
gadget.getSetting("selected_language"),
gadget.getSetting("default_selected_language")
]);
})
.push(function (results) {
gadget.state.language = results[0] || results[1];
return translate(string, gadget);
});
}
return translate(string, gadget);
})
.declareMethod('translateHtml', function (string) {
var gadget = this;
if (!gadget.state.language) {
return new RSVP.Queue()
.push(function () {
return RSVP.all([
gadget.getSetting("selected_language"),
gadget.getSetting("default_selected_language")
]);
})
.push(function (results) {
gadget.state.language = results[0] || results[1];
return translateHtml(string, gadget);
});
}
return translateHtml(string, gadget);
});
}(document, window, rJS, translation_data, RSVP));
/*globals window*/
/*jslint indent: 2, nomen: true, maxlen: 80*/
(function (window) {
"use strict";
window.translation_data = {
en: {
}
};
}(window));
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -7,12 +7,13 @@ define(function(require) {
var $ = require('jquery');
var utils = require('base/js/utils');
var baseUrl = "nbextensions/jiocontents";
var gadget_div = document.createElement("div");
gadget_div.setAttribute("data-gadget-url", baseUrl + "/gadget_jupyter_page.html");
document.body.append(gadget_div);
var s1 = document.createElement("script"); s1.type = "text/javascript";
s1.onload = function() {
console.log("loaded rsvp");
......
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