Commit d220c102 authored by Sebastian's avatar Sebastian

Add: Most standard gadgets and jupyter main page gadget

parents
/*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>Jio 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>
<!-- custom script -->
<script src="gadget_erp5_jio.js" type="text/javascript"></script>
</head>
<body>
<div data-gadget-url='gadget_jio.html' data-gadget-scope='jio'></div>
</body>
</html>
\ No newline at end of file
/*global window, rJS, RSVP, UriTemplate, URI, Query, SimpleQuery, ComplexQuery, jIO */
/*jslint indent: 2, maxerr: 3, nomen: true */
(function (window, rJS, RSVP, UriTemplate, URI, Query, SimpleQuery, ComplexQuery, jIO) {
"use strict";
function wrapJioCall(gadget, method_name, argument_list) {
var storage = gadget.state_parameter_dict.jio_storage,
regexp = /^X-Delegate uri="(http[s]*:\/\/[\/\-\[\]{}()*+:?.,\\\^$|#\s\w%]+)"$/,
login_page;
return storage[method_name].apply(storage, argument_list)
.push(undefined, function (error) {
if ((error.target !== undefined) && (error.target.status === 401)) {
login_page = error.target.getResponseHeader('WWW-Authenticate');
// Only connect to https to login
if (regexp.test(login_page)) {
return gadget.getUrlFor({
command: 'login',
absolute_url: true
})
.push(function (came_from) {
return gadget.redirect({
command: 'raw',
options: {
url: UriTemplate.parse(regexp.exec(login_page)[1]).expand({came_from: came_from})
}
});
});
/*
window.location = UriTemplate.parse(
regexp.exec(login_page)[1]
).expand({came_from: window.location.href + "{&me}"});
return RSVP.timeout(5000);
*/
// Redirect to the login view
}
// return gadget.redirect({command: 'display', options: {page: 'login'}});
}
throw error;
});
}
function isSingleLocalRoles(parsed_query) {
if ((parsed_query instanceof SimpleQuery) &&
(parsed_query.key === 'local_roles')) {
// local_roles:"Assignee"
return parsed_query.value;
}
}
function isMultipleLocalRoles(parsed_query) {
var i,
sub_query,
is_multiple = true,
local_role_list = [];
if ((parsed_query instanceof ComplexQuery) &&
(parsed_query.operator === 'OR')) {
for (i = 0; i < parsed_query.query_list.length; i += 1) {
sub_query = parsed_query.query_list[i];
if ((sub_query instanceof SimpleQuery) &&
(sub_query.key === 'local_roles')) {
local_role_list.push(sub_query.value);
} else {
is_multiple = false;
}
}
if (is_multiple) {
// local_roles:"Assignee" OR local_roles:"Assignor"
return local_role_list;
}
}
}
rJS(window)
.ready(function (gadget) {
return gadget.getDeclaredGadget('jio')
.push(function (jio_gadget) {
// Initialize the gadget local parameters
gadget.state_parameter_dict = {jio_storage: jio_gadget};
});
})
.declareAcquiredMethod('getSetting', 'getSetting')
.declareAcquiredMethod('redirect', 'redirect')
.declareAcquiredMethod('getUrlFor', 'getUrlFor')
.declareMethod('createJio', function () {
var gadget = this;
return new RSVP.Queue()
.push(function () {
return RSVP.all([
gadget.getSetting('hateoas_url'),
gadget.getSetting('default_view_reference')
]);
})
.push(function (setting_list) {
return gadget.state_parameter_dict.jio_storage.createJio({
type: "erp5",
url: setting_list[0],
default_view_reference: setting_list[1]
});
});
})
.declareMethod('allDocs', function (option_dict) {
// throw new Error('do not use all docs');
if (option_dict.list_method_template === undefined) {
return wrapJioCall(this, 'allDocs', arguments);
}
var query = option_dict.query,
i,
parsed_query,
sub_query,
result_list,
tmp_list = [],
local_roles;
if (option_dict.query) {
parsed_query = jIO.QueryFactory.create(option_dict.query);
result_list = isSingleLocalRoles(parsed_query);
if (result_list) {
query = undefined;
local_roles = result_list;
} else {
result_list = isMultipleLocalRoles(parsed_query);
if (result_list) {
query = undefined;
local_roles = result_list;
} else if ((parsed_query instanceof ComplexQuery) &&
(parsed_query.operator === 'AND')) {
// portal_type:"Person" AND local_roles:"Assignee"
for (i = 0; i < parsed_query.query_list.length; i += 1) {
sub_query = parsed_query.query_list[i];
result_list = isSingleLocalRoles(sub_query);
if (result_list) {
local_roles = result_list;
parsed_query.query_list.splice(i, 1);
query = Query.objectToSearchText(parsed_query);
i = parsed_query.query_list.length;
} else {
result_list = isMultipleLocalRoles(sub_query);
if (result_list) {
local_roles = result_list;
parsed_query.query_list.splice(i, 1);
query = Query.objectToSearchText(parsed_query);
i = parsed_query.query_list.length;
}
}
}
}
}
option_dict.query = query;
option_dict.local_roles = local_roles;
}
if (option_dict.sort_on) {
for (i = 0; i < option_dict.sort_on.length; i += 1) {
tmp_list.push(JSON.stringify(option_dict.sort_on[i]));
}
option_dict.sort_on = tmp_list;
}
return wrapJioCall(
this,
'getAttachment',
[
// XXX Ugly hardcoded meaningless id...
"erp5",
new UriTemplate.parse(option_dict.list_method_template)
.expand(option_dict),
{format: "json"}
]
)
.push(function (catalog_json) {
var data = catalog_json._embedded.contents,
count = data.length,
k,
uri,
item,
result = [];
for (k = 0; k < count; k += 1) {
item = data[k];
uri = new URI(item._links.self.href);
delete item._links;
result.push({
id: uri.segment(2),
doc: {},
value: item
});
}
return {
data: {
rows: result,
total_rows: result.length
}
};
});
})
.declareMethod('get', function (id) {
return wrapJioCall(this, 'get', [id]);
})
.declareMethod('getAttachment', function (id, name) {
return wrapJioCall(this, 'getAttachment', [id, name, {format: "json"}]);
})
.declareMethod('putAttachment', function (id, name, json) {
return wrapJioCall(this, 'putAttachment', [id, name, JSON.stringify(json)]);
})
.declareMethod('put', function (key, doc) {
return wrapJioCall(this, 'put', [key, doc]);
})
.declareMethod('post', function (doc) {
return wrapJioCall(this, 'post', [doc]);
});
}(window, rJS, RSVP, UriTemplate, URI, Query, SimpleQuery, ComplexQuery, jIO));
\ 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 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>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, user-scalable=no" />
<title>Jio 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>
<!-- custom script -->
<script src="gadget_jio.js" type="text/javascript"></script>
</head>
<body>
</body>
</html>
\ No newline at end of file
/*global window, rJS, jIO, FormData */
/*jslint indent: 2, maxerr: 3 */
(function (window, rJS, jIO) {
"use strict";
rJS(window)
.ready(function (gadget) {
// Initialize the gadget local parameters
gadget.state_parameter_dict = {};
})
.declareMethod('createJio', function (jio_options) {
this.state_parameter_dict.jio_storage = jIO.createJIO(jio_options);
})
.declareMethod('allDocs', function () {
var storage = this.state_parameter_dict.jio_storage;
return storage.allDocs.apply(storage, arguments);
})
.declareMethod('allAttachments', function () {
var storage = this.state_parameter_dict.jio_storage;
return storage.allAttachments.apply(storage, arguments);
})
.declareMethod('get', function () {
var storage = this.state_parameter_dict.jio_storage;
return storage.get.apply(storage, arguments);
})
.declareMethod('put', function () {
var storage = this.state_parameter_dict.jio_storage;
return storage.put.apply(storage, arguments);
})
.declareMethod('post', function () {
var storage = this.state_parameter_dict.jio_storage;
return storage.post.apply(storage, arguments);
})
.declareMethod('remove', function () {
var storage = this.state_parameter_dict.jio_storage;
return storage.remove.apply(storage, arguments);
})
.declareMethod('getAttachment', function () {
var storage = this.state_parameter_dict.jio_storage;
return storage.getAttachment.apply(storage, arguments);
})
.declareMethod('putAttachment', function () {
var storage = this.state_parameter_dict.jio_storage;
return storage.putAttachment.apply(storage, arguments);
})
.declareMethod('removeAttachment', function () {
var storage = this.state_parameter_dict.jio_storage;
return storage.removeAttachment.apply(storage, arguments);
})
.declareMethod('repair', function () {
var storage = this.state_parameter_dict.jio_storage;
return storage.repair.apply(storage, arguments);
});
}(window, rJS, jIO));
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<title>Jiocontents Gadget</title>
<script src="gadget_jiocontents.js" type="text/javascript"></script>
</head>
<body>
</body>
</html>
/*global window, rJS, jIO, FormData */
/*jslint indent: 2, maxerr: 3 */
(function (window, rJS) {
"use strict";
rJS(window)
.ready(function (gadget) {
console.log("Jio Contents Gadget yes yes.")
return gadget;
});
}(window, rJS));
<!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>
<script data-renderjs-configuration="default_view_reference" type="text/x-renderjs-configuration">notebook_jio_view</script>
<script data-renderjs-configuration="hateoas_url" type="text/x-renderjs-configuration">hateoas/</script>
<script data-renderjs-configuration="frontpage_gadget" type="text/x-renderjs-configuration">jupytertest</script>
<script data-renderjs-configuration="jio_document_page_gadget" type="text/x-renderjs-configuration">form</script>
<script data-renderjs-configuration="language_map" type="text/x-renderjs-configuration">{"en": "English"}</script>
<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>
</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>
</html>
\ No newline at end of file
/*global window, rJS, RSVP, Handlebars, URI */
/*jslint nomen: true, indent: 2, maxerr: 3 */
(function (window, rJS, RSVP) {
"use strict";
var gadget_klass = rJS(window);
// SNIP
gadget_klass
.ready(function() {
console.log("Hello ready!");
});
}(window, rJS, RSVP));
\ 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.
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
define(function(require) {
"use strict";
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");
var s2 = document.createElement("script"); s2.type = "text/javascript";
s2.onload = function() {
console.log("loaded renderjs");
rJS.manualBootstrap();
}
s2.src = baseUrl + "/renderjs.js";
document.head.append(s2);
};
s1.src = baseUrl + "/rsvp.js";
document.head.append(s1);
/*
var gadget_div = document.createElement("div");
gadget_div.setAttribute("data-gadget-url", "/nbextensions/jiocontents/gadget_jiocontents.html");
document.body.append(gadget_div);
var baseUrl = "https://softinst86155.host.vifib.net/erp5/web_site_module/renderjs_runner";
var s1 = document.createElement("script"); s1.type = "text/javascript";
s1.onload = function() {
console.log("loaded rsvp");
var s2 = document.createElement("script"); s2.type = "text/javascript";
s2.onload = function() {
console.log("loaded renderjs");
rJS.manualBootstrap();
}
s2.src = baseUrl + "/renderjs.js";
document.head.append(s2);
};
s1.src = baseUrl + "/rsvp.js";
document.head.append(s1);
*/
/*
$.getScript(baseUrl + "/rsvp.js", function() {
console.log("~~~ rsvp loaded");
$.getScript(baseUrl + "/renderjs.js", function() {
console.log("~~~ renderjs loaded");
rJS.manualBootstrap();
});
});
$.getScript("/nbextensions/jiocontents/rsvp-2.0.4.min.js", function() {
console.log("~~ loading_gadget: rsvp.js loaded");
$.getScript("/nbextensions/jiocontents/renderjs-latest.js", function() {
console.log("~~ loading_gadget: renderjs.js loaded");
rJS.manualBootstrap();
});
});
*/
var Contents = function(options) {
/**
* Constructor
*
* Preliminary documentation for the REST API is at
* https://github.com/ipython/ipython/wiki/IPEP-27%3A-Contents-Service
*
* A contents handles passing file operations
* to the back-end. This includes checkpointing
* with the normal file operations.
*
* Parameters:
* options: dictionary
* Dictionary of keyword arguments.
* base_url: string
*
* Algebra: Inherent structure, i.e. distribution of primes, groups, ...
* Application: Better understanding of how processes work, i.e. gradient descent, dependencies,
* probability, projection projective geometry...
* Art: Computer graphics,
* Understand: Being able to understand what many disciplines just accept as black-box. Reason
* to stop with CS and study math at uni
*/
this.base_url = options.base_url;
};
/** Error type */
Contents.DIRECTORY_NOT_EMPTY_ERROR = 'DirectoryNotEmptyError';
Contents.DirectoryNotEmptyError = function() {
// Constructor
//
// An error representing the result of attempting to delete a
// non-empty directory.
this.message = 'A directory must be empty before being deleted.';
};
Contents.DirectoryNotEmptyError.prototype =
Object.create(Error.prototype);
Contents.DirectoryNotEmptyError.prototype.name =
Contents.DIRECTORY_NOT_EMPTY_ERROR;
Contents.prototype.api_url = function() {
var url_parts = [
this.base_url, 'api/contents',
utils.url_join_encode.apply(null, arguments),
];
return utils.url_path_join.apply(null, url_parts);
};
/**
* Creates a basic error handler that wraps a jqXHR error as an Error.
*
* Takes a callback that accepts an Error, and returns a callback that can
* be passed directly to $.ajax, which will wrap the error from jQuery
* as an Error, and pass that to the original callback.
*
* @method create_basic_error_handler
* @param{Function} callback
* @return{Function}
*/
Contents.prototype.create_basic_error_handler = function(callback) {
if (!callback) {
return utils.log_ajax_error;
}
return function(xhr, status, error) {
callback(utils.wrap_ajax_error(xhr, status, error));
};
};
/**
* File Functions (including notebook operations)
*/
/**
* Get a file.
*
* @method get
* @param {String} path
* @param {Object} options
* type : 'notebook', 'file', or 'directory'
* format: 'text' or 'base64'; only relevant for type: 'file'
* content: true or false; // whether to include the content
*/
Contents.prototype.get = function (path, options) {
console.log("GET: " + path);
return new Promise(function(resolve, reject) {
var jioworker =
new Worker("nbextensions/jiocontents/jioworker.js");
jioworker.onmessage = function(e) {
resolve(e.data);
};
jioworker.postMessage(["get", path]);
});
}
/**
* Creates a new untitled file or directory in the specified directory
* path.
*
* @method new
* @param {String} path: the directory in which to create the new
* file/directory
* @param {Object} options:
* ext: file extension to use
* type: model type to create ('notebook', 'file', or 'directory')
*/
Contents.prototype.new_untitled = function(path, options) {
console.log("creating new untitled");
return new Promise(function(resolve, reject) {
var jioworker =
new Worker("nbextensions/jiocontents/jioworker.js");
jioworker.onmessage = function(e) {
resolve(e.data);
};
jioworker.postMessage(["create", path]);
});
};
Contents.prototype.delete = function(path) {
var settings = {
processData : false,
type : "DELETE",
dataType : "json",
};
var url = this.api_url(path);
return utils.promising_ajax(url, settings).catch(
// Translate certain errors to more specific ones.
function(error) {
// TODO: update IPEP27 to specify errors more precisely, so
// that error types can be detected here with certainty.
if (error.xhr.status === 400) {
throw new Contents.DirectoryNotEmptyError();
}
throw error;
}
);
};
Contents.prototype.rename = function(path, new_path) {
var data = {path: new_path};
var settings = {
processData : false,
type : "PATCH",
data : JSON.stringify(data),
dataType: "json",
contentType: 'application/json',
};
var url = this.api_url(path);
return utils.promising_ajax(url, settings);
};
Contents.prototype.save = function(path, model) {
/**
* We do the call with settings so we can set cache to false.
*/
var settings = {
processData : false,
type : "PUT",
dataType: "json",
data : JSON.stringify(model),
contentType: 'application/json',
};
var url = this.api_url(path);
return utils.promising_ajax(url, settings);
};
Contents.prototype.copy = function(from_file, to_dir) {
/**
* Copy a file into a given directory via POST
* The server will select the name of the copied file
*/
var url = this.api_url(to_dir);
var settings = {
processData : false,
type: "POST",
data: JSON.stringify({copy_from: from_file}),
contentType: 'application/json',
dataType : "json",
};
return utils.promising_ajax(url, settings);
};
/**
* Checkpointing Functions
*/
Contents.prototype.create_checkpoint = function(path) {
var url = this.api_url(path, 'checkpoints');
var settings = {
type : "POST",
contentType: false, // no data
dataType : "json",
};
return utils.promising_ajax(url, settings);
};
Contents.prototype.list_checkpoints = function(path) {
var url = this.api_url(path, 'checkpoints');
var settings = {
type : "GET",
cache: false,
dataType: "json",
};
return utils.promising_ajax(url, settings);
};
Contents.prototype.restore_checkpoint = function(path, checkpoint_id) {
var url = this.api_url(path, 'checkpoints', checkpoint_id);
var settings = {
type : "POST",
contentType: false, // no data
};
return utils.promising_ajax(url, settings);
};
Contents.prototype.delete_checkpoint = function(path, checkpoint_id) {
var url = this.api_url(path, 'checkpoints', checkpoint_id);
var settings = {
type : "DELETE",
};
return utils.promising_ajax(url, settings);
};
/**
* File management functions
*/
/**
* List notebooks and directories at a given path
*
* On success, load_callback is called with an array of dictionaries
* representing individual files or directories. Each dictionary has
* the keys:
* type: "notebook" or "directory"
* created: created date
* last_modified: last modified dat
* @method list_notebooks
* @param {String} path The path to list notebooks in
*/
Contents.prototype.list_contents = function(path) {
return this.get(path, {type: 'directory'});
};
return {'Contents': Contents};
});
This source diff could not be displayed because it is too large. You can view the blob instead.
"use strict";
var window = self;
var global = self;
self.IDBTransaction = self.IDBTransaction || self.webkitIDBTransaction ||
self.msIDBTransaction || {READ_WRITE: "readwrite"};
self.IDBKeyRange = self.IDBKeyRange || self.webkitIDBKeyRange ||
self.msIDBKeyRange;
self.DOMParser = {};
self.sessionStorage = {};
self.localStorage = {};
self.openDatabase = {};
self.DOMError = {};
// ---------------------------------------------------------------------------
//self.importScripts("standard_erp5/rsvp.js", "standard_erp5/jiodev.js");
//console.log("HEllo WORLD");
onmessage = function(e) {
if(e.data[0] === "get") {
/*
Return a JSON
for the directory "" (which is root directory for jupyter)
containing the directories properties and content which is the
content of the directory (notebooks only in our case).
The content of the notebooks themself should be null as per spec.
*/
postMessage({"name": "",
"path": "",
"last_modified":
"2017-09-06T03:33:29.781159Z",
"created": "2017-09-06T03:33:29.781159Z",
"content": [{
"name": "notebook1.ipynb",
// TODO: THIS PATH TO NOTEBOOK FILE
"path": "foo/bar/notebook1.ipynb",
//"path": "/localhost/Test-Notebook.ipynb",
"type": "notebook",
"format": "json",
"writable": true,
"last_modified": "2013-10-02T11:29:27.616675+00:00",
"created": "2013-10-01T12:21:20.123456+00:00",
"content": null
}]
});
}
if(e.data[0] === "create") {
/*
Properly: Get all notebook titles and check for the first available
Untitled#.ipynb
For now: Use timestamp as title maybe?
*/
postMessage({
"name": "Untitled0.ipynb",
"path": "Untitled0.ipynb",
"type": "notebook",
"format": "json", // opt || null by default
"writable": true, // opt
"last_modified": "2017-09-07T08:01:40.871584Z",
"created": "2017-09-07T08:01:40.871584Z",
"content": null, // opt
});
}
};
/*
* js_channel is a very lightweight abstraction on top of
* postMessage which defines message formats and semantics
* to support interactions more rich than just message passing
* js_channel supports:
* + query/response - traditional rpc
* + query/update/response - incremental async return of results
* to a query
* + notifications - fire and forget
* + error handling
*
* js_channel is based heavily on json-rpc, but is focused at the
* problem of inter-iframe RPC.
*
* Message types:
* There are 5 types of messages that can flow over this channel,
* and you may determine what type of message an object is by
* examining its parameters:
* 1. Requests
* + integer id
* + string method
* + (optional) any params
* 2. Callback Invocations (or just "Callbacks")
* + integer id
* + string callback
* + (optional) params
* 3. Error Responses (or just "Errors)
* + integer id
* + string error
* + (optional) string message
* 4. Responses
* + integer id
* + (optional) any result
* 5. Notifications
* + string method
* + (optional) any params
*/
;var Channel = (function() {
"use strict";
// current transaction id, start out at a random *odd* number between 1 and a million
// There is one current transaction counter id per page, and it's shared between
// channel instances. That means of all messages posted from a single javascript
// evaluation context, we'll never have two with the same id.
var s_curTranId = Math.floor(Math.random()*1000001);
// no two bound channels in the same javascript evaluation context may have the same origin, scope, and window.
// futher if two bound channels have the same window and scope, they may not have *overlapping* origins
// (either one or both support '*'). This restriction allows a single onMessage handler to efficiently
// route messages based on origin and scope. The s_boundChans maps origins to scopes, to message
// handlers. Request and Notification messages are routed using this table.
// Finally, channels are inserted into this table when built, and removed when destroyed.
var s_boundChans = { };
// add a channel to s_boundChans, throwing if a dup exists
function s_addBoundChan(win, origin, scope, handler) {
function hasWin(arr) {
for (var i = 0; i < arr.length; i++) if (arr[i].win === win) return true;
return false;
}
// does she exist?
var exists = false;
if (origin === '*') {
// we must check all other origins, sadly.
for (var k in s_boundChans) {
if (!s_boundChans.hasOwnProperty(k)) continue;
if (k === '*') continue;
if (typeof s_boundChans[k][scope] === 'object') {
exists = hasWin(s_boundChans[k][scope]);
if (exists) break;
}
}
} else {
// we must check only '*'
if ((s_boundChans['*'] && s_boundChans['*'][scope])) {
exists = hasWin(s_boundChans['*'][scope]);
}
if (!exists && s_boundChans[origin] && s_boundChans[origin][scope])
{
exists = hasWin(s_boundChans[origin][scope]);
}
}
if (exists) throw "A channel is already bound to the same window which overlaps with origin '"+ origin +"' and has scope '"+scope+"'";
if (typeof s_boundChans[origin] != 'object') s_boundChans[origin] = { };
if (typeof s_boundChans[origin][scope] != 'object') s_boundChans[origin][scope] = [ ];
s_boundChans[origin][scope].push({win: win, handler: handler});
}
function s_removeBoundChan(win, origin, scope) {
var arr = s_boundChans[origin][scope];
for (var i = 0; i < arr.length; i++) {
if (arr[i].win === win) {
arr.splice(i,1);
}
}
if (s_boundChans[origin][scope].length === 0) {
delete s_boundChans[origin][scope];
}
}
function s_isArray(obj) {
if (Array.isArray) return Array.isArray(obj);
else {
return (obj.constructor.toString().indexOf("Array") != -1);
}
}
// No two outstanding outbound messages may have the same id, period. Given that, a single table
// mapping "transaction ids" to message handlers, allows efficient routing of Callback, Error, and
// Response messages. Entries are added to this table when requests are sent, and removed when
// responses are received.
var s_transIds = { };
// class singleton onMessage handler
// this function is registered once and all incoming messages route through here. This
// arrangement allows certain efficiencies, message data is only parsed once and dispatch
// is more efficient, especially for large numbers of simultaneous channels.
var s_onMessage = function(e) {
try {
var m = JSON.parse(e.data);
if (typeof m !== 'object' || m === null) throw "malformed";
} catch(e) {
// just ignore any posted messages that do not consist of valid JSON
return;
}
var w = e.source;
var o = e.origin;
var s, i, meth;
if (typeof m.method === 'string') {
var ar = m.method.split('::');
if (ar.length == 2) {
s = ar[0];
meth = ar[1];
} else {
meth = m.method;
}
}
if (typeof m.id !== 'undefined') i = m.id;
// w is message source window
// o is message origin
// m is parsed message
// s is message scope
// i is message id (or undefined)
// meth is unscoped method name
// ^^ based on these factors we can route the message
// if it has a method it's either a notification or a request,
// route using s_boundChans
if (typeof meth === 'string') {
var delivered = false;
if (s_boundChans[o] && s_boundChans[o][s]) {
for (var j = 0; j < s_boundChans[o][s].length; j++) {
if (s_boundChans[o][s][j].win === w) {
s_boundChans[o][s][j].handler(o, meth, m);
delivered = true;
break;
}
}
}
if (!delivered && s_boundChans['*'] && s_boundChans['*'][s]) {
for (var j = 0; j < s_boundChans['*'][s].length; j++) {
if (s_boundChans['*'][s][j].win === w) {
s_boundChans['*'][s][j].handler(o, meth, m);
break;
}
}
}
}
// otherwise it must have an id (or be poorly formed
else if (typeof i != 'undefined') {
if (s_transIds[i]) s_transIds[i](o, meth, m);
}
};
// Setup postMessage event listeners
if (window.addEventListener) window.addEventListener('message', s_onMessage, false);
else if(window.attachEvent) window.attachEvent('onmessage', s_onMessage);
/* a messaging channel is constructed from a window and an origin.
* the channel will assert that all messages received over the
* channel match the origin
*
* Arguments to Channel.build(cfg):
*
* cfg.window - the remote window with which we'll communicate
* cfg.origin - the expected origin of the remote window, may be '*'
* which matches any origin
* cfg.scope - the 'scope' of messages. a scope string that is
* prepended to message names. local and remote endpoints
* of a single channel must agree upon scope. Scope may
* not contain double colons ('::').
* cfg.debugOutput - A boolean value. If true and window.console.log is
* a function, then debug strings will be emitted to that
* function.
* cfg.debugOutput - A boolean value. If true and window.console.log is
* a function, then debug strings will be emitted to that
* function.
* cfg.postMessageObserver - A function that will be passed two arguments,
* an origin and a message. It will be passed these immediately
* before messages are posted.
* cfg.gotMessageObserver - A function that will be passed two arguments,
* an origin and a message. It will be passed these arguments
* immediately after they pass scope and origin checks, but before
* they are processed.
* cfg.onReady - A function that will be invoked when a channel becomes "ready",
* this occurs once both sides of the channel have been
* instantiated and an application level handshake is exchanged.
* the onReady function will be passed a single argument which is
* the channel object that was returned from build().
*/
return {
build: function(cfg) {
var debug = function(m) {
if (cfg.debugOutput && window.console && window.console.log) {
// try to stringify, if it doesn't work we'll let javascript's built in toString do its magic
try { if (typeof m !== 'string') m = JSON.stringify(m); } catch(e) { }
console.log("["+chanId+"] " + m);
}
};
/* browser capabilities check */
if (!window.postMessage) throw("jschannel cannot run this browser, no postMessage");
if (!window.JSON || !window.JSON.stringify || ! window.JSON.parse) {
throw("jschannel cannot run this browser, no JSON parsing/serialization");
}
/* basic argument validation */
if (typeof cfg != 'object') throw("Channel build invoked without a proper object argument");
if (!cfg.window || !cfg.window.postMessage) throw("Channel.build() called without a valid window argument");
/* we'd have to do a little more work to be able to run multiple channels that intercommunicate the same
* window... Not sure if we care to support that */
if (window === cfg.window) throw("target window is same as present window -- not allowed");
// let's require that the client specify an origin. if we just assume '*' we'll be
// propagating unsafe practices. that would be lame.
var validOrigin = false;
if (typeof cfg.origin === 'string') {
var oMatch;
if (cfg.origin === "*") validOrigin = true;
// allow valid domains under http and https. Also, trim paths off otherwise valid origins.
else if (null !== (oMatch = cfg.origin.match(/^https?:\/\/(?:[-a-zA-Z0-9_\.])+(?::\d+)?/))) {
cfg.origin = oMatch[0].toLowerCase();
validOrigin = true;
}
}
if (!validOrigin) throw ("Channel.build() called with an invalid origin");
if (typeof cfg.scope !== 'undefined') {
if (typeof cfg.scope !== 'string') throw 'scope, when specified, must be a string';
if (cfg.scope.split('::').length > 1) throw "scope may not contain double colons: '::'";
}
/* private variables */
// generate a random and psuedo unique id for this channel
var chanId = (function () {
var text = "";
var alpha = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
for(var i=0; i < 5; i++) text += alpha.charAt(Math.floor(Math.random() * alpha.length));
return text;
})();
// registrations: mapping method names to call objects
var regTbl = { };
// current oustanding sent requests
var outTbl = { };
// current oustanding received requests
var inTbl = { };
// are we ready yet? when false we will block outbound messages.
var ready = false;
var pendingQueue = [ ];
var createTransaction = function(id,origin,callbacks) {
var shouldDelayReturn = false;
var completed = false;
return {
origin: origin,
invoke: function(cbName, v) {
// verify in table
if (!inTbl[id]) throw "attempting to invoke a callback of a nonexistent transaction: " + id;
// verify that the callback name is valid
var valid = false;
for (var i = 0; i < callbacks.length; i++) if (cbName === callbacks[i]) { valid = true; break; }
if (!valid) throw "request supports no such callback '" + cbName + "'";
// send callback invocation
postMessage({ id: id, callback: cbName, params: v});
},
error: function(error, message) {
completed = true;
// verify in table
if (!inTbl[id]) throw "error called for nonexistent message: " + id;
// remove transaction from table
delete inTbl[id];
// send error
postMessage({ id: id, error: error, message: message });
},
complete: function(v) {
completed = true;
// verify in table
if (!inTbl[id]) throw "complete called for nonexistent message: " + id;
// remove transaction from table
delete inTbl[id];
// send complete
postMessage({ id: id, result: v });
},
delayReturn: function(delay) {
if (typeof delay === 'boolean') {
shouldDelayReturn = (delay === true);
}
return shouldDelayReturn;
},
completed: function() {
return completed;
}
};
};
var setTransactionTimeout = function(transId, timeout, method) {
return window.setTimeout(function() {
if (outTbl[transId]) {
// XXX: what if client code raises an exception here?
var msg = "timeout (" + timeout + "ms) exceeded on method '" + method + "'";
(1,outTbl[transId].error)("timeout_error", msg);
delete outTbl[transId];
delete s_transIds[transId];
}
}, timeout);
};
var onMessage = function(origin, method, m) {
// if an observer was specified at allocation time, invoke it
if (typeof cfg.gotMessageObserver === 'function') {
// pass observer a clone of the object so that our
// manipulations are not visible (i.e. method unscoping).
// This is not particularly efficient, but then we expect
// that message observers are primarily for debugging anyway.
try {
cfg.gotMessageObserver(origin, m);
} catch (e) {
debug("gotMessageObserver() raised an exception: " + e.toString());
}
}
// now, what type of message is this?
if (m.id && method) {
// a request! do we have a registered handler for this request?
if (regTbl[method]) {
var trans = createTransaction(m.id, origin, m.callbacks ? m.callbacks : [ ]);
inTbl[m.id] = { };
try {
// callback handling. we'll magically create functions inside the parameter list for each
// callback
if (m.callbacks && s_isArray(m.callbacks) && m.callbacks.length > 0) {
for (var i = 0; i < m.callbacks.length; i++) {
var path = m.callbacks[i];
var obj = m.params;
var pathItems = path.split('/');
for (var j = 0; j < pathItems.length - 1; j++) {
var cp = pathItems[j];
if (typeof obj[cp] !== 'object') obj[cp] = { };
obj = obj[cp];
}
obj[pathItems[pathItems.length - 1]] = (function() {
var cbName = path;
return function(params) {
return trans.invoke(cbName, params);
};
})();
}
}
var resp = regTbl[method](trans, m.params);
if (!trans.delayReturn() && !trans.completed()) trans.complete(resp);
} catch(e) {
// automagic handling of exceptions:
var error = "runtime_error";
var message = null;
// * if it's a string then it gets an error code of 'runtime_error' and string is the message
if (typeof e === 'string') {
message = e;
} else if (typeof e === 'object') {
// either an array or an object
// * if it's an array of length two, then array[0] is the code, array[1] is the error message
if (e && s_isArray(e) && e.length == 2) {
error = e[0];
message = e[1];
}
// * if it's an object then we'll look form error and message parameters
else if (typeof e.error === 'string') {
error = e.error;
if (!e.message) message = "";
else if (typeof e.message === 'string') message = e.message;
else e = e.message; // let the stringify/toString message give us a reasonable verbose error string
}
}
// message is *still* null, let's try harder
if (message === null) {
try {
message = JSON.stringify(e);
/* On MSIE8, this can result in 'out of memory', which
* leaves message undefined. */
if (typeof(message) == 'undefined')
message = e.toString();
} catch (e2) {
message = e.toString();
}
}
trans.error(error,message);
}
}
} else if (m.id && m.callback) {
if (!outTbl[m.id] ||!outTbl[m.id].callbacks || !outTbl[m.id].callbacks[m.callback])
{
debug("ignoring invalid callback, id:"+m.id+ " (" + m.callback +")");
} else {
// XXX: what if client code raises an exception here?
outTbl[m.id].callbacks[m.callback](m.params);
}
} else if (m.id) {
if (!outTbl[m.id]) {
debug("ignoring invalid response: " + m.id);
} else {
// XXX: what if client code raises an exception here?
if (m.error) {
(1,outTbl[m.id].error)(m.error, m.message);
} else {
if (m.result !== undefined) (1,outTbl[m.id].success)(m.result);
else (1,outTbl[m.id].success)();
}
delete outTbl[m.id];
delete s_transIds[m.id];
}
} else if (method) {
// tis a notification.
if (regTbl[method]) {
// yep, there's a handler for that.
// transaction has only origin for notifications.
regTbl[method]({ origin: origin }, m.params);
// if the client throws, we'll just let it bubble out
// what can we do? Also, here we'll ignore return values
}
}
};
// now register our bound channel for msg routing
s_addBoundChan(cfg.window, cfg.origin, ((typeof cfg.scope === 'string') ? cfg.scope : ''), onMessage);
// scope method names based on cfg.scope specified when the Channel was instantiated
var scopeMethod = function(m) {
if (typeof cfg.scope === 'string' && cfg.scope.length) m = [cfg.scope, m].join("::");
return m;
};
// a small wrapper around postmessage whose primary function is to handle the
// case that clients start sending messages before the other end is "ready"
var postMessage = function(msg, force) {
if (!msg) throw "postMessage called with null message";
// delay posting if we're not ready yet.
var verb = (ready ? "post " : "queue ");
debug(verb + " message: " + JSON.stringify(msg));
if (!force && !ready) {
pendingQueue.push(msg);
} else {
if (typeof cfg.postMessageObserver === 'function') {
try {
cfg.postMessageObserver(cfg.origin, msg);
} catch (e) {
debug("postMessageObserver() raised an exception: " + e.toString());
}
}
cfg.window.postMessage(JSON.stringify(msg), cfg.origin);
}
};
var onReady = function(trans, type) {
debug('ready msg received');
if (ready) throw "received ready message while in ready state. help!";
if (type === 'ping') {
chanId += '-R';
} else {
chanId += '-L';
}
obj.unbind('__ready'); // now this handler isn't needed any more.
ready = true;
debug('ready msg accepted.');
if (type === 'ping') {
obj.notify({ method: '__ready', params: 'pong' });
}
// flush queue
while (pendingQueue.length) {
postMessage(pendingQueue.pop());
}
// invoke onReady observer if provided
if (typeof cfg.onReady === 'function') cfg.onReady(obj);
};
var obj = {
// tries to unbind a bound message handler. returns false if not possible
unbind: function (method) {
if (regTbl[method]) {
if (!(delete regTbl[method])) throw ("can't delete method: " + method);
return true;
}
return false;
},
bind: function (method, cb) {
if (!method || typeof method !== 'string') throw "'method' argument to bind must be string";
if (!cb || typeof cb !== 'function') throw "callback missing from bind params";
if (regTbl[method]) throw "method '"+method+"' is already bound!";
regTbl[method] = cb;
return this;
},
call: function(m) {
if (!m) throw 'missing arguments to call function';
if (!m.method || typeof m.method !== 'string') throw "'method' argument to call must be string";
if (!m.success || typeof m.success !== 'function') throw "'success' callback missing from call";
// now it's time to support the 'callback' feature of jschannel. We'll traverse the argument
// object and pick out all of the functions that were passed as arguments.
var callbacks = { };
var callbackNames = [ ];
var pruneFunctions = function (path, obj) {
if (typeof obj === 'object') {
for (var k in obj) {
if (!obj.hasOwnProperty(k)) continue;
var np = path + (path.length ? '/' : '') + k;
if (typeof obj[k] === 'function') {
callbacks[np] = obj[k];
callbackNames.push(np);
delete obj[k];
} else if (typeof obj[k] === 'object') {
pruneFunctions(np, obj[k]);
}
}
}
};
pruneFunctions("", m.params);
// build a 'request' message and send it
var msg = { id: s_curTranId, method: scopeMethod(m.method), params: m.params };
if (callbackNames.length) msg.callbacks = callbackNames;
if (m.timeout)
// XXX: This function returns a timeout ID, but we don't do anything with it.
// We might want to keep track of it so we can cancel it using clearTimeout()
// when the transaction completes.
setTransactionTimeout(s_curTranId, m.timeout, scopeMethod(m.method));
// insert into the transaction table
outTbl[s_curTranId] = { callbacks: callbacks, error: m.error, success: m.success };
s_transIds[s_curTranId] = onMessage;
// increment current id
s_curTranId++;
postMessage(msg);
},
notify: function(m) {
if (!m) throw 'missing arguments to notify function';
if (!m.method || typeof m.method !== 'string') throw "'method' argument to notify must be string";
// no need to go into any transaction table
postMessage({ method: scopeMethod(m.method), params: m.params });
},
destroy: function () {
s_removeBoundChan(cfg.window, cfg.origin, ((typeof cfg.scope === 'string') ? cfg.scope : ''));
if (window.removeEventListener) window.removeEventListener('message', onMessage, false);
else if(window.detachEvent) window.detachEvent('onmessage', onMessage);
ready = false;
regTbl = { };
inTbl = { };
outTbl = { };
cfg.origin = null;
pendingQueue = [ ];
debug("channel destroyed");
chanId = "";
}
};
obj.bind('__ready', onReady);
setTimeout(function() {
postMessage({ method: scopeMethod('__ready'), params: "ping" }, true);
}, 0);
return obj;
}
};
})();
;/*
* DOMParser HTML extension
* 2012-09-04
*
* By Eli Grey, http://eligrey.com
* Public domain.
* NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
*/
/*! @source https://gist.github.com/1129031 */
(function (DOMParser) {
"use strict";
var DOMParser_proto = DOMParser.prototype,
real_parseFromString = DOMParser_proto.parseFromString;
// Firefox/Opera/IE throw errors on unsupported types
try {
// WebKit returns null on unsupported types
if ((new DOMParser()).parseFromString("", "text/html")) {
// text/html parsing is natively supported
return;
}
} catch (ignore) {}
DOMParser_proto.parseFromString = function (markup, type) {
var result, doc, doc_elt, first_elt;
if (/^\s*text\/html\s*(?:;|$)/i.test(type)) {
doc = document.implementation.createHTMLDocument("");
doc_elt = doc.documentElement;
doc_elt.innerHTML = markup;
first_elt = doc_elt.firstElementChild;
if (doc_elt.childElementCount === 1
&& first_elt.localName.toLowerCase() === "html") {
doc.replaceChild(first_elt, doc_elt);
}
result = doc;
} else {
result = real_parseFromString.apply(this, arguments);
}
return result;
};
}(DOMParser));
;// IE does not support have Document.prototype.contains.
if (typeof document.contains !== 'function') {
Document.prototype.contains = function(node) {
if (node === this || node.parentNode === this)
return true;
return this.documentElement.contains(node);
}
}
;/*! RenderJs */
/*jslint nomen: true*/
/*
* renderJs - Generic Gadget library renderer.
* https://renderjs.nexedi.com/
*/
(function wrapRenderJS(document, window, RSVP, DOMParser, Channel,
MutationObserver, Node, FileReader, Blob, navigator,
Event, URL) {
"use strict";
function readBlobAsDataURL(blob) {
var fr = new FileReader();
return new RSVP.Promise(function waitFormDataURLRead(resolve, reject) {
fr.addEventListener("load", function handleDataURLRead(evt) {
resolve(evt.target.result);
});
fr.addEventListener("error", reject);
fr.readAsDataURL(blob);
}, function cancelReadBlobAsDataURL() {
fr.abort();
});
}
function loopEventListener(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 handleEventCallback(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 waitForEventCallbackResult() {
return result;
})
.push(undefined, function handleEventCallbackError(error) {
if (!(error instanceof RSVP.CancellationError)) {
canceller();
reject(error);
}
});
};
target.addEventListener(type, handle_event_callback, useCapture);
}
return new RSVP.Promise(itsANonResolvableTrap, canceller);
}
function promiseAnimationFrame() {
var request_id;
function canceller() {
window.cancelAnimationFrame(request_id);
}
function resolver(resolve) {
request_id = window.requestAnimationFrame(resolve);
}
return new RSVP.Promise(resolver, canceller);
}
function ajax(url) {
var xhr;
function resolver(resolve, reject) {
function handler() {
try {
if (xhr.readyState === 0) {
// UNSENT
reject(xhr);
} else if (xhr.readyState === 4) {
// DONE
if ((xhr.status < 200) || (xhr.status >= 300) ||
(!/^text\/html[;]?/.test(
xhr.getResponseHeader("Content-Type") || ""
))) {
reject(xhr);
} else {
resolve(xhr);
}
}
} catch (e) {
reject(e);
}
}
xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.onreadystatechange = handler;
xhr.setRequestHeader('Accept', 'text/html');
xhr.withCredentials = true;
xhr.send();
}
function canceller() {
if ((xhr !== undefined) && (xhr.readyState !== xhr.DONE)) {
xhr.abort();
}
}
return new RSVP.Promise(resolver, canceller);
}
var gadget_model_defer_dict = {},
javascript_registration_dict = {},
stylesheet_registration_dict = {},
gadget_loading_klass_list = [],
renderJS,
Monitor,
scope_increment = 0,
isAbsoluteOrDataURL = new RegExp('^(?:[a-z]+:)?//|data:', 'i'),
is_page_unloaded = false,
error_list = [],
all_dependency_loaded_deferred;
window.addEventListener('error', function handleGlobalError(error) {
error_list.push(error);
});
window.addEventListener('beforeunload', function handleBeforeUnload() {
// XXX If another listener cancel the page unload,
// it will not restore renderJS crash report
is_page_unloaded = true;
});
/////////////////////////////////////////////////////////////////
// Helper functions
/////////////////////////////////////////////////////////////////
function removeHash(url) {
var index = url.indexOf('#');
if (index > 0) {
url = url.substring(0, index);
}
return url;
}
function letsCrash(e) {
var i,
body,
container,
paragraph,
link,
error;
if (is_page_unloaded) {
/*global console*/
console.info('-- Error dropped, as page is unloaded');
console.info(e);
return;
}
error_list.push(e);
// Add error handling stack
error_list.push(new Error('stopping renderJS'));
body = document.getElementsByTagName('body')[0];
while (body.firstChild) {
body.removeChild(body.firstChild);
}
container = document.createElement("section");
paragraph = document.createElement("h1");
paragraph.textContent = 'Unhandled Error';
container.appendChild(paragraph);
paragraph = document.createElement("p");
paragraph.textContent = 'Please report this error to the support team';
container.appendChild(paragraph);
paragraph = document.createElement("p");
paragraph.textContent = 'Location: ';
link = document.createElement("a");
link.href = link.textContent = window.location.toString();
paragraph.appendChild(link);
container.appendChild(paragraph);
paragraph = document.createElement("p");
paragraph.textContent = 'User-agent: ' + navigator.userAgent;
container.appendChild(paragraph);
paragraph = document.createElement("p");
paragraph.textContent = 'Date: ' + new Date(Date.now()).toISOString();
container.appendChild(paragraph);
body.appendChild(container);
for (i = 0; i < error_list.length; i += 1) {
error = error_list[i];
if (error instanceof Event) {
error = {
string: error.toString(),
message: error.message,
type: error.type,
target: error.target
};
if (error.target !== undefined) {
error_list.splice(i + 1, 0, error.target);
}
}
if (error instanceof XMLHttpRequest) {
error = {
message: error.toString(),
readyState: error.readyState,
status: error.status,
statusText: error.statusText,
response: error.response,
responseUrl: error.responseUrl,
response_headers: error.getAllResponseHeaders()
};
}
if (error.constructor === Array ||
error.constructor === String ||
error.constructor === Object) {
try {
error = JSON.stringify(error);
} catch (ignore) {
}
}
container = document.createElement("section");
paragraph = document.createElement("h2");
paragraph.textContent = error.message || error;
container.appendChild(paragraph);
if (error.fileName !== undefined) {
paragraph = document.createElement("p");
paragraph.textContent = 'File: ' +
error.fileName +
': ' + error.lineNumber;
container.appendChild(paragraph);
}
if (error.stack !== undefined) {
paragraph = document.createElement("pre");
paragraph.textContent = 'Stack: ' + error.stack;
container.appendChild(paragraph);
}
body.appendChild(container);
}
// XXX Do not crash the application if it fails
// Where to write the error?
/*global console*/
console.error(e.stack);
console.error(e);
}
/////////////////////////////////////////////////////////////////
// Service Monitor promise
/////////////////////////////////////////////////////////////////
function ResolvedMonitorError(message) {
this.name = "resolved";
if ((message !== undefined) && (typeof message !== "string")) {
throw new TypeError('You must pass a string.');
}
this.message = message || "Default Message";
}
ResolvedMonitorError.prototype = new Error();
ResolvedMonitorError.prototype.constructor = ResolvedMonitorError;
Monitor = function createMonitor() {
var monitor = this,
promise_list = [],
promise,
reject,
resolved;
if (!(this instanceof Monitor)) {
return new Monitor();
}
function canceller() {
var len = promise_list.length,
i;
for (i = 0; i < len; i += 1) {
promise_list[i].cancel();
}
// Clean it to speed up other canceller run
promise_list = [];
}
promise = new RSVP.Promise(function promiseMonitor(done, fail) {
reject = function rejectMonitor(rejectedReason) {
if (resolved) {
return;
}
monitor.isRejected = true;
monitor.rejectedReason = rejectedReason;
resolved = true;
canceller();
return fail(rejectedReason);
};
}, canceller);
monitor.cancel = function cancelMonitor() {
if (resolved) {
return;
}
resolved = true;
promise.cancel();
promise.fail(function rejectMonitorPromise(rejectedReason) {
monitor.isRejected = true;
monitor.rejectedReason = rejectedReason;
});
};
monitor.then = promise.then.bind(promise);
monitor.fail = promise.fail.bind(promise);
monitor.monitor = function startMonitor(promise_to_monitor) {
if (resolved) {
throw new ResolvedMonitorError();
}
var queue = new RSVP.Queue()
.push(function waitForPromiseToMonitor() {
return promise_to_monitor;
})
.push(function handlePromiseToMonitorSuccess(fulfillmentValue) {
// Promise to monitor is fullfilled, remove it from the list
var len = promise_list.length,
sub_promise_to_monitor,
new_promise_list = [],
i;
for (i = 0; i < len; i += 1) {
sub_promise_to_monitor = promise_list[i];
if (!(sub_promise_to_monitor.isFulfilled ||
sub_promise_to_monitor.isRejected)) {
new_promise_list.push(sub_promise_to_monitor);
}
}
promise_list = new_promise_list;
}, function handlePromiseToMonitorError(rejectedReason) {
if (rejectedReason instanceof RSVP.CancellationError) {
if (!(promise_to_monitor.isFulfilled &&
promise_to_monitor.isRejected)) {
// The queue could be cancelled before the first push is run
promise_to_monitor.cancel();
}
}
reject(rejectedReason);
throw rejectedReason;
});
promise_list.push(queue);
return this;
};
};
Monitor.prototype = Object.create(RSVP.Promise.prototype);
Monitor.prototype.constructor = Monitor;
/////////////////////////////////////////////////////////////////
// RenderJSGadget
/////////////////////////////////////////////////////////////////
function RenderJSGadget() {
if (!(this instanceof RenderJSGadget)) {
return new RenderJSGadget();
}
}
RenderJSGadget.prototype.__title = "";
RenderJSGadget.prototype.__interface_list = [];
RenderJSGadget.prototype.__path = "";
RenderJSGadget.prototype.__html = "";
RenderJSGadget.prototype.__required_css_list = [];
RenderJSGadget.prototype.__required_js_list = [];
function createGadgetMonitor(g) {
if (g.__monitor !== undefined) {
g.__monitor.cancel();
}
g.__monitor = new Monitor();
g.__job_dict = {};
g.__job_list = [];
g.__job_triggered = false;
g.__monitor.fail(function handleGadgetMonitorError(error) {
if (!(error instanceof RSVP.CancellationError)) {
return g.aq_reportServiceError(error);
}
// Crash the application if the acquisition generates an error.
}).fail(letsCrash);
}
function clearGadgetInternalParameters() {
this.__sub_gadget_dict = {};
createGadgetMonitor(this);
}
function loadSubGadgetDOMDeclaration() {
var element_list = this.element.querySelectorAll('[data-gadget-url]'),
element,
promise_list = [],
scope,
url,
sandbox,
i,
context = this;
function prepareReportGadgetDeclarationError(scope) {
return function reportGadgetDeclarationError(error) {
var aq_dict = context.__acquired_method_dict || {},
method_name = 'reportGadgetDeclarationError';
if (aq_dict.hasOwnProperty(method_name)) {
return aq_dict[method_name].apply(context,
[arguments, scope]);
}
throw error;
};
}
for (i = 0; i < element_list.length; i += 1) {
element = element_list[i];
scope = element.getAttribute("data-gadget-scope");
url = element.getAttribute("data-gadget-url");
sandbox = element.getAttribute("data-gadget-sandbox");
if (url !== null) {
promise_list.push(
context.declareGadget(url, {
element: element,
scope: scope || undefined,
sandbox: sandbox || undefined
})
.push(undefined, prepareReportGadgetDeclarationError(scope))
);
}
}
return RSVP.all(promise_list);
}
RenderJSGadget.__ready_list = [clearGadgetInternalParameters,
loadSubGadgetDOMDeclaration];
RenderJSGadget.ready = function ready(callback) {
this.__ready_list.push(callback);
return this;
};
RenderJSGadget.setState = function setState(state_dict) {
var json_state = JSON.stringify(state_dict);
this.__ready_list.unshift(function setStateDefaultValue() {
this.state = JSON.parse(json_state);
});
return this;
};
RenderJSGadget.onStateChange = function onStateChange(callback) {
this.prototype.__state_change_callback = callback;
return this;
};
RenderJSGadget.__service_list = [];
RenderJSGadget.declareService = function declareService(callback) {
this.__service_list.push(callback);
return this;
};
RenderJSGadget.onEvent = function onEvent(type, callback, use_capture,
prevent_default) {
this.__service_list.push(function startLoopEventListenerService() {
return loopEventListener(this.element, type, use_capture,
callback.bind(this), prevent_default);
});
return this;
};
RenderJSGadget.onLoop = function onLoop(callback, delay) {
if (delay === undefined) {
delay = 0;
}
this.__service_list.push(function handleServiceCallback() {
var queue_loop = new RSVP.Queue(),
context = this,
wait = function waitForLoopIteration() {
queue_loop
.push(function waitNextOnLoopDelay() {
return RSVP.delay(delay);
})
.push(function waitNextOnLoopAnimationFrame() {
// Only loop when the app has the focus
return promiseAnimationFrame();
})
.push(function executeOnLoopCallback() {
return callback.apply(context, []);
})
.push(wait);
};
wait();
return queue_loop;
});
return this;
};
function runJob(gadget, name, callback, argument_list) {
var job_promise = new RSVP.Queue()
.push(function waitForJobCallback() {
return callback.apply(gadget, argument_list);
});
if (gadget.__job_dict.hasOwnProperty(name)) {
gadget.__job_dict[name].cancel();
}
gadget.__job_dict[name] = job_promise;
gadget.__monitor.monitor(new RSVP.Queue()
.push(function waitForJobPromise() {
return job_promise;
})
.push(undefined, function handleJobError(error) {
if (!(error instanceof RSVP.CancellationError)) {
throw error;
}
}));
}
function startService(gadget) {
gadget.__monitor.monitor(new RSVP.Queue()
.push(function monitorAllServiceList() {
var i,
service_list = gadget.constructor.__service_list,
job_list = gadget.__job_list;
for (i = 0; i < service_list.length; i += 1) {
gadget.__monitor.monitor(service_list[i].apply(gadget));
}
for (i = 0; i < job_list.length; i += 1) {
runJob(gadget, job_list[i][0], job_list[i][1], job_list[i][2]);
}
gadget.__job_list = [];
gadget.__job_triggered = true;
})
);
}
/////////////////////////////////////////////////////////////////
// RenderJSGadget.declareJob
// gadget internal method, which trigger execution
// of a function inside a service
/////////////////////////////////////////////////////////////////
RenderJSGadget.declareJob = function declareJob(name, callback) {
this.prototype[name] = function triggerJob() {
var context = this,
argument_list = arguments;
if (context.__job_triggered) {
runJob(context, name, callback, argument_list);
} else {
context.__job_list.push([name, callback, argument_list]);
}
};
// Allow chain
return this;
};
/////////////////////////////////////////////////////////////////
// RenderJSGadget.declareMethod
/////////////////////////////////////////////////////////////////
RenderJSGadget.declareMethod = function declareMethod(name, callback) {
this.prototype[name] = function triggerMethod() {
var context = this,
argument_list = arguments;
return new RSVP.Queue()
.push(function waitForMethodCallback() {
return callback.apply(context, argument_list);
});
};
// Allow chain
return this;
};
RenderJSGadget
.declareMethod('getInterfaceList', function getInterfaceList() {
// Returns the list of gadget prototype
return this.__interface_list;
})
.declareMethod('getRequiredCSSList', function getRequiredCSSList() {
// Returns a list of CSS required by the gadget
return this.__required_css_list;
})
.declareMethod('getRequiredJSList', function getRequiredJSList() {
// Returns a list of JS required by the gadget
return this.__required_js_list;
})
.declareMethod('getPath', function getPath() {
// Returns the path of the code of a gadget
return this.__path;
})
.declareMethod('getTitle', function getTitle() {
// Returns the title of a gadget
return this.__title;
})
.declareMethod('getElement', function getElement() {
// Returns the DOM Element of a gadget
// XXX Kept for compatibility. Use element property directly
if (this.element === undefined) {
throw new Error("No element defined");
}
return this.element;
})
.declareMethod('changeState', function changeState(state_dict) {
var next_onStateChange = new RSVP.Queue(),
previous_onStateCHange,
context = this;
if (context.hasOwnProperty('__previous_onStateChange')) {
previous_onStateCHange = context.__previous_onStateChange;
next_onStateChange
.push(function waitForPreviousStateChange() {
return previous_onStateCHange;
})
.push(undefined, function handlePreviousStateChangeError() {
// Run callback even if previous failed
return;
});
}
context.__previous_onStateChange = next_onStateChange;
return next_onStateChange
.push(function checkStateModification() {
var key,
modified = false,
previous_cancelled = context.hasOwnProperty('__modification_dict'),
modification_dict;
if (previous_cancelled) {
modification_dict = context.__modification_dict;
modified = true;
} else {
modification_dict = {};
}
for (key in state_dict) {
if (state_dict.hasOwnProperty(key) &&
(state_dict[key] !== context.state[key])) {
context.state[key] = state_dict[key];
modification_dict[key] = state_dict[key];
modified = true;
}
}
if (modified && context.__state_change_callback !== undefined) {
context.__modification_dict = modification_dict;
return new RSVP.Queue()
.push(function waitForStateChangeCallback() {
return context.__state_change_callback(modification_dict);
})
.push(function handleStateChangeSuccess(result) {
delete context.__modification_dict;
return result;
});
}
});
});
/////////////////////////////////////////////////////////////////
// RenderJSGadget.declareAcquiredMethod
/////////////////////////////////////////////////////////////////
function acquire(child_gadget, method_name, argument_list) {
var gadget = this,
key,
gadget_scope;
for (key in gadget.__sub_gadget_dict) {
if (gadget.__sub_gadget_dict.hasOwnProperty(key)) {
if (gadget.__sub_gadget_dict[key] === child_gadget) {
gadget_scope = key;
}
}
}
return new RSVP.Queue()
.push(function waitForAcquireMethod() {
// Do not specify default __acquired_method_dict on prototype
// to prevent modifying this default value (with
// allowPublicAcquiredMethod for example)
var aq_dict = gadget.__acquired_method_dict || {};
if (aq_dict.hasOwnProperty(method_name)) {
return aq_dict[method_name].apply(gadget,
[argument_list, gadget_scope]);
}
throw new renderJS.AcquisitionError("aq_dynamic is not defined");
})
.push(undefined, function handleAcquireMethodError(error) {
if (error instanceof renderJS.AcquisitionError) {
return gadget.__aq_parent(method_name, argument_list);
}
throw error;
});
}
RenderJSGadget.declareAcquiredMethod =
function declareAcquiredMethod(name, method_name_to_acquire) {
this.prototype[name] = function acquireMethod() {
var argument_list = Array.prototype.slice.call(arguments, 0),
gadget = this;
return new RSVP.Queue()
.push(function waitForAqParent() {
return gadget.__aq_parent(method_name_to_acquire, argument_list);
});
};
// Allow chain
return this;
};
RenderJSGadget.declareAcquiredMethod("aq_reportServiceError",
"reportServiceError");
RenderJSGadget.declareAcquiredMethod("aq_reportGadgetDeclarationError",
"reportGadgetDeclarationError");
/////////////////////////////////////////////////////////////////
// RenderJSGadget.allowPublicAcquisition
/////////////////////////////////////////////////////////////////
RenderJSGadget.allowPublicAcquisition =
function allowPublicAcquisition(method_name, callback) {
this.prototype.__acquired_method_dict[method_name] = callback;
// Allow chain
return this;
};
// Set aq_parent on gadget_instance which call acquire on parent_gadget
function setAqParent(gadget_instance, parent_gadget) {
gadget_instance.__aq_parent =
function __aq_parent(method_name, argument_list) {
return acquire.apply(parent_gadget, [gadget_instance, method_name,
argument_list]);
};
}
/////////////////////////////////////////////////////////////////
// RenderJSEmbeddedGadget
/////////////////////////////////////////////////////////////////
// Class inheritance
function RenderJSEmbeddedGadget() {
if (!(this instanceof RenderJSEmbeddedGadget)) {
return new RenderJSEmbeddedGadget();
}
RenderJSGadget.call(this);
}
RenderJSEmbeddedGadget.__ready_list = RenderJSGadget.__ready_list.slice();
RenderJSEmbeddedGadget.__service_list =
RenderJSGadget.__service_list.slice();
RenderJSEmbeddedGadget.ready =
RenderJSGadget.ready;
RenderJSEmbeddedGadget.setState =
RenderJSGadget.setState;
RenderJSEmbeddedGadget.onStateChange =
RenderJSGadget.onStateChange;
RenderJSEmbeddedGadget.declareService =
RenderJSGadget.declareService;
RenderJSEmbeddedGadget.onEvent =
RenderJSGadget.onEvent;
RenderJSEmbeddedGadget.onLoop =
RenderJSGadget.onLoop;
RenderJSEmbeddedGadget.prototype = new RenderJSGadget();
RenderJSEmbeddedGadget.prototype.constructor = RenderJSEmbeddedGadget;
/////////////////////////////////////////////////////////////////
// privateDeclarePublicGadget
/////////////////////////////////////////////////////////////////
function privateDeclarePublicGadget(url, options, parent_gadget) {
return renderJS.declareGadgetKlass(url)
// gadget loading should not be interrupted
// if not, gadget's definition will not be complete
//.then will return another promise
//so loading_klass_promise can't be cancel
.then(function createPrivateInstanceFromKlass(Klass) {
// Get the gadget class and instanciate it
if (options.element === undefined) {
options.element = document.createElement("div");
}
var i,
gadget_instance,
template_node_list = Klass.__template_element.body.childNodes,
fragment = document.createDocumentFragment();
gadget_instance = new Klass();
gadget_instance.element = options.element;
gadget_instance.state = {};
for (i = 0; i < template_node_list.length; i += 1) {
fragment.appendChild(
template_node_list[i].cloneNode(true)
);
}
gadget_instance.element.appendChild(fragment);
setAqParent(gadget_instance, parent_gadget);
return gadget_instance;
});
}
/////////////////////////////////////////////////////////////////
// RenderJSIframeGadget
/////////////////////////////////////////////////////////////////
function RenderJSIframeGadget() {
if (!(this instanceof RenderJSIframeGadget)) {
return new RenderJSIframeGadget();
}
RenderJSGadget.call(this);
}
RenderJSIframeGadget.__ready_list = RenderJSGadget.__ready_list.slice();
RenderJSIframeGadget.ready =
RenderJSGadget.ready;
RenderJSIframeGadget.setState =
RenderJSGadget.setState;
RenderJSIframeGadget.onStateChange =
RenderJSGadget.onStateChange;
RenderJSIframeGadget.__service_list = RenderJSGadget.__service_list.slice();
RenderJSIframeGadget.declareService =
RenderJSGadget.declareService;
RenderJSIframeGadget.onEvent =
RenderJSGadget.onEvent;
RenderJSIframeGadget.onLoop =
RenderJSGadget.onLoop;
RenderJSIframeGadget.prototype = new RenderJSGadget();
RenderJSIframeGadget.prototype.constructor = RenderJSIframeGadget;
/////////////////////////////////////////////////////////////////
// privateDeclareIframeGadget
/////////////////////////////////////////////////////////////////
function privateDeclareIframeGadget(url, options, parent_gadget) {
var gadget_instance,
iframe,
iframe_loading_deferred = RSVP.defer();
if (options.element === undefined) {
throw new Error("DOM element is required to create Iframe Gadget " +
url);
}
// Check if the element is attached to the DOM
if (!document.contains(options.element)) {
throw new Error("The parent element is not attached to the DOM for " +
url);
}
gadget_instance = new RenderJSIframeGadget();
setAqParent(gadget_instance, parent_gadget);
iframe = document.createElement("iframe");
iframe.addEventListener('error', function handleIframeError(error) {
iframe_loading_deferred.reject(error);
});
iframe.addEventListener('load', function handleIframeLoad() {
return RSVP.timeout(5000)
.fail(function triggerIframeTimeout() {
iframe_loading_deferred.reject(
new Error('Timeout while loading: ' + url)
);
});
});
// gadget_instance.element.setAttribute("seamless", "seamless");
iframe.setAttribute("src", url);
gadget_instance.__path = url;
gadget_instance.element = options.element;
gadget_instance.state = {};
// Attach it to the DOM
options.element.appendChild(iframe);
// XXX Manage unbind when deleting the gadget
// Create the communication channel with the iframe
gadget_instance.__chan = Channel.build({
window: iframe.contentWindow,
// origin: (new URL(url, window.location)).origin,
origin: '*',
scope: "renderJS"
});
// Create new method from the declareMethod call inside the iframe
gadget_instance.__chan.bind(
"declareMethod",
function handleChannelDeclareMethod(trans, method_name) {
gadget_instance[method_name] = function triggerChannelDeclareMethod() {
var argument_list = arguments,
wait_promise = new RSVP.Promise(
function handleChannelCall(resolve, reject) {
gadget_instance.__chan.call({
method: "methodCall",
params: [
method_name,
Array.prototype.slice.call(argument_list, 0)],
success: resolve,
error: reject
});
}
);
return new RSVP.Queue()
.push(function waitForChannelCall() {
return wait_promise;
});
};
return "OK";
}
);
// Wait for the iframe to be loaded before continuing
gadget_instance.__chan.bind("ready", function handleChannelReady(trans) {
iframe_loading_deferred.resolve(gadget_instance);
return "OK";
});
gadget_instance.__chan.bind("failed",
function handleChannelFail(trans, params) {
iframe_loading_deferred.reject(params);
return "OK";
});
gadget_instance.__chan.bind("acquire",
function handleChannelAcquire(trans, params) {
gadget_instance.__aq_parent.apply(gadget_instance, params)
.then(trans.complete)
.fail(function handleChannelAcquireError(e) {
trans.error(e.toString());
});
trans.delayReturn(true);
});
return iframe_loading_deferred.promise;
}
/////////////////////////////////////////////////////////////////
// privateDeclareDataUrlGadget
/////////////////////////////////////////////////////////////////
function privateDeclareDataUrlGadget(url, options, parent_gadget) {
return new RSVP.Queue()
.push(function waitForDataUrlAjax() {
return ajax(url);
})
.push(function handleDataURLAjaxResponse(xhr) {
// Insert a "base" element, in order to resolve all relative links
// which could get broken with a data url
var doc = (new DOMParser()).parseFromString(xhr.responseText,
'text/html'),
base = doc.createElement('base'),
blob;
base.href = url;
doc.head.insertBefore(base, doc.head.firstChild);
blob = new Blob([doc.documentElement.outerHTML],
{type: "text/html;charset=UTF-8"});
return readBlobAsDataURL(blob);
})
.push(function handleDataURL(data_url) {
return privateDeclareIframeGadget(data_url, options, parent_gadget);
});
}
/////////////////////////////////////////////////////////////////
// RenderJSGadget.declareGadget
/////////////////////////////////////////////////////////////////
RenderJSGadget
.declareMethod('declareGadget', function declareGadget(url, options) {
var parent_gadget = this;
if (options === undefined) {
options = {};
}
if (options.sandbox === undefined) {
options.sandbox = "public";
}
// transform url to absolute url if it is relative
url = renderJS.getAbsoluteURL(url, this.__path);
return new RSVP.Queue()
.push(function waitForPrivateDeclareGadget() {
var method;
if (options.sandbox === "public") {
method = privateDeclarePublicGadget;
} else if (options.sandbox === "iframe") {
method = privateDeclareIframeGadget;
} else if (options.sandbox === "dataurl") {
method = privateDeclareDataUrlGadget;
} else {
throw new Error("Unsupported sandbox options '" +
options.sandbox + "'");
}
return method(url, options, parent_gadget);
})
// Set the HTML context
.push(function setGadgetInstanceHTMLContext(gadget_instance) {
var i,
scope,
queue = new RSVP.Queue();
// Trigger calling of all ready callback
function ready_wrapper() {
return gadget_instance;
}
function ready_executable_wrapper(fct) {
return function executeReadyWrapper() {
return fct.call(gadget_instance, gadget_instance);
};
}
for (i = 0; i < gadget_instance.constructor.__ready_list.length;
i += 1) {
// Put a timeout?
queue.push(ready_executable_wrapper(
gadget_instance.constructor.__ready_list[i]
));
// Always return the gadget instance after ready function
queue.push(ready_wrapper);
}
// Store local reference to the gadget instance
scope = options.scope;
if (scope === undefined) {
scope = 'RJS_' + scope_increment;
scope_increment += 1;
while (parent_gadget.__sub_gadget_dict.hasOwnProperty(scope)) {
scope = 'RJS_' + scope_increment;
scope_increment += 1;
}
}
parent_gadget.__sub_gadget_dict[scope] = gadget_instance;
gadget_instance.element.setAttribute("data-gadget-scope",
scope);
// Put some attribute to ease page layout comprehension
gadget_instance.element.setAttribute("data-gadget-url", url);
gadget_instance.element.setAttribute("data-gadget-sandbox",
options.sandbox);
gadget_instance.element._gadget = gadget_instance;
if (document.contains(gadget_instance.element)) {
// Put a timeout
queue.push(startService);
}
// Always return the gadget instance after ready function
queue.push(ready_wrapper);
return queue;
});
})
.declareMethod('getDeclaredGadget',
function getDeclaredGadget(gadget_scope) {
if (!this.__sub_gadget_dict.hasOwnProperty(gadget_scope)) {
throw new Error("Gadget scope '" + gadget_scope + "' is not known.");
}
return this.__sub_gadget_dict[gadget_scope];
})
.declareMethod('dropGadget', function dropGadget(gadget_scope) {
if (!this.__sub_gadget_dict.hasOwnProperty(gadget_scope)) {
throw new Error("Gadget scope '" + gadget_scope + "' is not known.");
}
// http://perfectionkills.com/understanding-delete/
delete this.__sub_gadget_dict[gadget_scope];
});
/////////////////////////////////////////////////////////////////
// renderJS selector
/////////////////////////////////////////////////////////////////
renderJS = function getLoadingGadget(selector) {
var result;
if (selector === window) {
// window is the 'this' value when loading a javascript file
// In this case, use the current loading gadget constructor
result = gadget_loading_klass_list[0];
}
if (result === undefined) {
throw new Error("Unknown selector '" + selector + "'");
}
return result;
};
/////////////////////////////////////////////////////////////////
// renderJS.AcquisitionError
/////////////////////////////////////////////////////////////////
renderJS.AcquisitionError = function createAcquisitionError(message) {
this.name = "AcquisitionError";
if ((message !== undefined) && (typeof message !== "string")) {
throw new TypeError('You must pass a string.');
}
this.message = message || "Acquisition failed";
};
renderJS.AcquisitionError.prototype = new Error();
renderJS.AcquisitionError.prototype.constructor =
renderJS.AcquisitionError;
/////////////////////////////////////////////////////////////////
// renderJS.getAbsoluteURL
/////////////////////////////////////////////////////////////////
renderJS.getAbsoluteURL = function getAbsoluteURL(url, base_url) {
if (base_url && url) {
return new URL(url, base_url).href;
}
return url;
};
/////////////////////////////////////////////////////////////////
// renderJS.declareJS
/////////////////////////////////////////////////////////////////
renderJS.declareJS = function declareJS(url, container, pop) {
// https://www.html5rocks.com/en/tutorials/speed/script-loading/
// Prevent infinite recursion if loading render.js
// more than once
var result;
if (javascript_registration_dict.hasOwnProperty(url)) {
result = RSVP.resolve();
} else {
javascript_registration_dict[url] = null;
result = new RSVP.Promise(
function waitForJSLoadEvent(resolve, reject) {
var newScript;
newScript = document.createElement('script');
newScript.async = false;
newScript.type = 'text/javascript';
newScript.onload = function triggerJSLoaded() {
if (pop === true) {
// Drop the current loading klass info used by selector
gadget_loading_klass_list.shift();
}
resolve();
};
newScript.onerror = function triggerJSNotLoaded(e) {
if (pop === true) {
// Drop the current loading klass info used by selector
gadget_loading_klass_list.shift();
}
reject(e);
};
newScript.src = url;
container.appendChild(newScript);
}
);
}
return result;
};
/////////////////////////////////////////////////////////////////
// renderJS.declareCSS
/////////////////////////////////////////////////////////////////
renderJS.declareCSS = function declareCSS(url, container) {
// https://github.com/furf/jquery-getCSS/blob/master/jquery.getCSS.js
// No way to cleanly check if a css has been loaded
// So, always resolve the promise...
// http://requirejs.org/docs/faq-advanced.html#css
var result;
if (stylesheet_registration_dict.hasOwnProperty(url)) {
result = RSVP.resolve();
} else {
result = new RSVP.Promise(function waitForCSSLoadEvent(resolve, reject) {
var link;
link = document.createElement('link');
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = url;
link.onload = function triggerCSSLoaded() {
stylesheet_registration_dict[url] = null;
resolve();
};
link.onerror = reject;
container.appendChild(link);
});
}
return result;
};
/////////////////////////////////////////////////////////////////
// renderJS.declareGadgetKlass
/////////////////////////////////////////////////////////////////
function parse(xhr, url) {
var tmp_constructor,
key,
parsed_html;
// Class inheritance
tmp_constructor = function createSuperKlass() {
RenderJSGadget.call(this);
};
tmp_constructor.__ready_list = RenderJSGadget.__ready_list.slice();
tmp_constructor.__service_list = RenderJSGadget.__service_list.slice();
tmp_constructor.declareMethod =
RenderJSGadget.declareMethod;
tmp_constructor.declareJob =
RenderJSGadget.declareJob;
tmp_constructor.declareAcquiredMethod =
RenderJSGadget.declareAcquiredMethod;
tmp_constructor.allowPublicAcquisition =
RenderJSGadget.allowPublicAcquisition;
tmp_constructor.ready =
RenderJSGadget.ready;
tmp_constructor.setState =
RenderJSGadget.setState;
tmp_constructor.onStateChange =
RenderJSGadget.onStateChange;
tmp_constructor.declareService =
RenderJSGadget.declareService;
tmp_constructor.onEvent =
RenderJSGadget.onEvent;
tmp_constructor.onLoop =
RenderJSGadget.onLoop;
tmp_constructor.prototype = new RenderJSGadget();
tmp_constructor.prototype.constructor = tmp_constructor;
tmp_constructor.prototype.__path = url;
tmp_constructor.prototype.__acquired_method_dict = {};
// https://developer.mozilla.org/en-US/docs/HTML_in_XMLHttpRequest
// https://developer.mozilla.org/en-US/docs/Web/API/DOMParser
// https://developer.mozilla.org/en-US/docs/Code_snippets/HTML_to_DOM
tmp_constructor.__template_element =
(new DOMParser()).parseFromString(xhr.responseText, "text/html");
parsed_html = renderJS.parseGadgetHTMLDocument(
tmp_constructor.__template_element,
url
);
for (key in parsed_html) {
if (parsed_html.hasOwnProperty(key)) {
tmp_constructor.prototype['__' + key] = parsed_html[key];
}
}
return tmp_constructor;
}
renderJS.declareGadgetKlass = function declareGadgetKlass(url) {
var tmp_constructor,
defer;
if (gadget_model_defer_dict.hasOwnProperty(url)) {
// Return klass object if it already exists
if (gadget_model_defer_dict[url].hasOwnProperty('defer_list')) {
// Klass not yet loaded.
// Add a new defer
defer = RSVP.defer();
gadget_model_defer_dict[url].defer_list.push(defer);
return defer.promise;
}
if (gadget_model_defer_dict[url].is_resolved) {
return RSVP.resolve(gadget_model_defer_dict[url].result);
}
return RSVP.reject(gadget_model_defer_dict[url].result);
}
gadget_model_defer_dict[url] = {
defer_list: []
};
// Fetch the HTML page and parse it
return new RSVP.Queue()
.push(function waitForGadgetKlassAjax() {
return ajax(url);
})
.push(function handleGadgetKlassAjax(result) {
tmp_constructor = parse(result, url);
var fragment = document.createDocumentFragment(),
promise_list = [],
i,
js_list = tmp_constructor.prototype.__required_js_list,
css_list = tmp_constructor.prototype.__required_css_list;
// Load JS
if (js_list.length) {
gadget_loading_klass_list.push(tmp_constructor);
for (i = 0; i < js_list.length - 1; i += 1) {
promise_list.push(renderJS.declareJS(js_list[i], fragment));
}
promise_list.push(renderJS.declareJS(js_list[i], fragment, true));
}
// Load CSS
for (i = 0; i < css_list.length; i += 1) {
promise_list.push(renderJS.declareCSS(css_list[i], fragment));
}
document.head.appendChild(fragment);
return RSVP.all(promise_list);
})
.push(function handleGadgetKlassLoadingSuccess() {
var i,
len = gadget_model_defer_dict[url].defer_list.length;
for (i = 0; i < len; i += 1) {
gadget_model_defer_dict[url].defer_list[i].resolve(tmp_constructor);
}
delete gadget_model_defer_dict[url].defer_list;
gadget_model_defer_dict[url].result = tmp_constructor;
gadget_model_defer_dict[url].is_resolved = true;
return tmp_constructor;
})
.push(undefined, function handleGadgetKlassLoadingError(e) {
// Drop the current loading klass info used by selector
// even in case of error
var i,
len = gadget_model_defer_dict[url].defer_list.length;
for (i = 0; i < len; i += 1) {
gadget_model_defer_dict[url].defer_list[i].reject(e);
}
delete gadget_model_defer_dict[url].defer_list;
gadget_model_defer_dict[url].result = e;
gadget_model_defer_dict[url].is_resolved = false;
throw e;
});
};
/////////////////////////////////////////////////////////////////
// renderJS.clearGadgetKlassList
/////////////////////////////////////////////////////////////////
// For test purpose only
renderJS.clearGadgetKlassList = function clearGadgetKlassList() {
gadget_model_defer_dict = {};
javascript_registration_dict = {};
stylesheet_registration_dict = {};
};
/////////////////////////////////////////////////////////////////
// renderJS.parseGadgetHTMLDocument
/////////////////////////////////////////////////////////////////
renderJS.parseGadgetHTMLDocument =
function parseGadgetHTMLDocument(document_element, url) {
var settings = {
title: "",
interface_list: [],
required_css_list: [],
required_js_list: []
},
i,
element;
if (!url || !isAbsoluteOrDataURL.test(url)) {
throw new Error("The url should be absolute: " + url);
}
if (document_element.nodeType === 9) {
settings.title = document_element.title;
if (document_element.head !== null) {
for (i = 0; i < document_element.head.children.length; i += 1) {
element = document_element.head.children[i];
if (element.href !== null) {
// XXX Manage relative URL during extraction of URLs
// element.href returns absolute URL in firefox but "" in chrome;
if (element.rel === "stylesheet") {
settings.required_css_list.push(
renderJS.getAbsoluteURL(element.getAttribute("href"), url)
);
} else if (element.nodeName === "SCRIPT" &&
(element.type === "text/javascript" ||
!element.type)) {
settings.required_js_list.push(
renderJS.getAbsoluteURL(element.getAttribute("src"), url)
);
} else if (element.rel ===
"http://www.renderjs.org/rel/interface") {
settings.interface_list.push(
renderJS.getAbsoluteURL(element.getAttribute("href"), url)
);
}
}
}
}
} else {
throw new Error("The first parameter should be an HTMLDocument");
}
return settings;
};
/////////////////////////////////////////////////////////////////
// global
/////////////////////////////////////////////////////////////////
window.rJS = window.renderJS = renderJS;
window.__RenderJSGadget = RenderJSGadget;
window.__RenderJSEmbeddedGadget = RenderJSEmbeddedGadget;
window.__RenderJSIframeGadget = RenderJSIframeGadget;
///////////////////////////////////////////////////
// Bootstrap process. Register the self gadget.
///////////////////////////////////////////////////
// Detect when all JS dependencies have been loaded
all_dependency_loaded_deferred = new RSVP.defer();
// Manually initializes the self gadget if the DOMContentLoaded event
// is triggered before everything was ready.
// (For instance, the HTML-tag for the self gadget gets inserted after
// page load)
renderJS.manualBootstrap = function manualBootstrap() {
all_dependency_loaded_deferred.resolve();
};
document.addEventListener('DOMContentLoaded',
all_dependency_loaded_deferred.resolve, false);
function configureMutationObserver(TmpConstructor, url, root_gadget) {
// XXX HTML properties can only be set when the DOM is fully loaded
var settings = renderJS.parseGadgetHTMLDocument(document, url),
j,
key,
fragment = document.createDocumentFragment();
for (key in settings) {
if (settings.hasOwnProperty(key)) {
TmpConstructor.prototype['__' + key] = settings[key];
}
}
TmpConstructor.__template_element = document.createElement("div");
root_gadget.element = document.body;
root_gadget.state = {};
for (j = 0; j < root_gadget.element.childNodes.length; j += 1) {
fragment.appendChild(
root_gadget.element.childNodes[j].cloneNode(true)
);
}
TmpConstructor.__template_element.appendChild(fragment);
return RSVP.all([root_gadget.getRequiredJSList(),
root_gadget.getRequiredCSSList()])
.then(function handleRequireDependencyList(all_list) {
var i,
js_list = all_list[0],
css_list = all_list[1];
for (i = 0; i < js_list.length; i += 1) {
javascript_registration_dict[js_list[i]] = null;
}
for (i = 0; i < css_list.length; i += 1) {
stylesheet_registration_dict[css_list[i]] = null;
}
gadget_loading_klass_list.shift();
}).then(function createMutationObserver() {
// select the target node
var target = document.querySelector('body'),
// create an observer instance
observer = new MutationObserver(function observeMutatios(mutations) {
var i, k, len, len2, node, added_list;
mutations.forEach(function observerMutation(mutation) {
if (mutation.type === 'childList') {
len = mutation.removedNodes.length;
for (i = 0; i < len; i += 1) {
node = mutation.removedNodes[i];
if (node.nodeType === Node.ELEMENT_NODE) {
if (node.hasAttribute("data-gadget-url") &&
(node._gadget !== undefined)) {
createGadgetMonitor(node._gadget);
}
added_list =
node.querySelectorAll("[data-gadget-url]");
len2 = added_list.length;
for (k = 0; k < len2; k += 1) {
node = added_list[k];
if (node._gadget !== undefined) {
createGadgetMonitor(node._gadget);
}
}
}
}
len = mutation.addedNodes.length;
for (i = 0; i < len; i += 1) {
node = mutation.addedNodes[i];
if (node.nodeType === Node.ELEMENT_NODE) {
if (node.hasAttribute("data-gadget-url") &&
(node._gadget !== undefined)) {
if (document.contains(node)) {
startService(node._gadget);
}
}
added_list =
node.querySelectorAll("[data-gadget-url]");
len2 = added_list.length;
for (k = 0; k < len2; k += 1) {
node = added_list[k];
if (document.contains(node)) {
if (node._gadget !== undefined) {
startService(node._gadget);
}
}
}
}
}
}
});
}),
// configuration of the observer:
config = {
childList: true,
subtree: true,
attributes: false,
characterData: false
};
// pass in the target node, as well as the observer options
observer.observe(target, config);
return root_gadget;
});
}
function createLastAcquisitionGadget() {
var last_acquisition_gadget = new RenderJSGadget();
last_acquisition_gadget.__acquired_method_dict = {
reportServiceError: function reportServiceError(param_list) {
letsCrash(param_list[0]);
}
};
// Stop acquisition on the last acquisition gadget
// Do not put this on the klass, as their could be multiple instances
last_acquisition_gadget.__aq_parent = function __aq_parent(method_name) {
throw new renderJS.AcquisitionError(
"No gadget provides " + method_name
);
};
return last_acquisition_gadget;
}
/*
function notifyAllMethodToParent() {
;
}
*/
function createLoadingGadget(url) {
var TmpConstructor,
root_gadget,
embedded_channel,
notifyDeclareMethod,
declare_method_list_waiting,
loading_result,
channel_defer,
real_result_list;
// gadget_failed = false,
// connection_ready = false;
// Create the gadget class for the current url
if (gadget_model_defer_dict.hasOwnProperty(url)) {
throw new Error("bootstrap should not be called twice");
}
// Create the root gadget instance and put it in the loading stack
TmpConstructor = RenderJSEmbeddedGadget;
TmpConstructor.__ready_list = RenderJSGadget.__ready_list.slice();
TmpConstructor.__service_list = RenderJSGadget.__service_list.slice();
TmpConstructor.prototype.__path = url;
root_gadget = new RenderJSEmbeddedGadget();
setAqParent(root_gadget, createLastAcquisitionGadget());
declare_method_list_waiting = [
"getInterfaceList",
"getRequiredCSSList",
"getRequiredJSList",
"getPath",
"getTitle"
];
// Inform parent gadget about declareMethod calls here.
notifyDeclareMethod = function notifyDeclareMethod(name) {
declare_method_list_waiting.push(name);
};
real_result_list = [TmpConstructor, root_gadget, embedded_channel,
declare_method_list_waiting];
if (window.self === window.top) {
loading_result = real_result_list;
} else {
channel_defer = RSVP.defer();
loading_result = RSVP.any([
channel_defer.promise,
new RSVP.Queue()
.push(function waitForParentChannelCreation() {
// Expect the channel to parent to be usable after 1 second
// If not, consider the gadget as the root
// Drop all iframe channel communication
return RSVP.delay(1000);
})
.push(function handleParentChannelCreation() {
real_result_list[2] = undefined;
return real_result_list;
})
]);
// Create the communication channel
embedded_channel = Channel.build({
window: window.parent,
origin: "*",
scope: "renderJS",
onReady: function onChannelReady() {
var k,
len;
// Channel is ready, so now declare all methods
notifyDeclareMethod = function notifyDeclareMethod(name) {
declare_method_list_waiting.push(
new RSVP.Promise(
function promiseChannelDeclareMethodCall(resolve, reject) {
embedded_channel.call({
method: "declareMethod",
params: name,
success: resolve,
error: reject
});
}
)
);
};
len = declare_method_list_waiting.length;
for (k = 0; k < len; k += 1) {
notifyDeclareMethod(declare_method_list_waiting[k]);
}
channel_defer.resolve(real_result_list);
}
});
real_result_list[2] = embedded_channel;
}
// Surcharge declareMethod to inform parent window
TmpConstructor.declareMethod = function declareMethod(name, callback) {
var result = RenderJSGadget.declareMethod.apply(
this,
[name, callback]
);
notifyDeclareMethod(name);
return result;
};
TmpConstructor.declareService =
RenderJSGadget.declareService;
TmpConstructor.declareJob =
RenderJSGadget.declareJob;
TmpConstructor.onEvent =
RenderJSGadget.onEvent;
TmpConstructor.onLoop =
RenderJSGadget.onLoop;
TmpConstructor.declareAcquiredMethod =
RenderJSGadget.declareAcquiredMethod;
TmpConstructor.allowPublicAcquisition =
RenderJSGadget.allowPublicAcquisition;
TmpConstructor.prototype.__acquired_method_dict = {};
gadget_loading_klass_list.push(TmpConstructor);
return loading_result;
}
function triggerReadyList(TmpConstructor, root_gadget) {
// XXX Probably duplicated
var i,
ready_queue = new RSVP.Queue();
function ready_wrapper() {
return root_gadget;
}
function ready_executable_wrapper(fct) {
return function wrapReadyFunction(g) {
return fct.call(g, g);
};
}
TmpConstructor.ready(function startServiceInReady() {
return startService(this);
});
ready_queue.push(ready_wrapper);
for (i = 0; i < TmpConstructor.__ready_list.length; i += 1) {
// Put a timeout?
ready_queue
.push(ready_executable_wrapper(TmpConstructor.__ready_list[i]))
// Always return the gadget instance after ready function
.push(ready_wrapper);
}
return ready_queue;
}
function finishAqParentConfiguration(TmpConstructor, root_gadget,
embedded_channel) {
// Define __aq_parent to inform parent window
root_gadget.__aq_parent =
TmpConstructor.prototype.__aq_parent = function aq_parent(method_name,
argument_list,
time_out) {
return new RSVP.Promise(
function waitForChannelAcquire(resolve, reject) {
embedded_channel.call({
method: "acquire",
params: [
method_name,
argument_list
],
success: resolve,
error: reject,
timeout: time_out
});
}
);
};
// bind calls to renderJS method on the instance
embedded_channel.bind("methodCall", function methodCall(trans, v) {
root_gadget[v[0]].apply(root_gadget, v[1])
.push(trans.complete,
function handleMethodCallError(e) {
trans.error(e.toString());
});
trans.delayReturn(true);
});
}
function bootstrap(url) {
// Create the loading gadget
var wait_for_gadget_loaded = createLoadingGadget(url),
TmpConstructor,
root_gadget,
embedded_channel,
declare_method_list_waiting;
return new RSVP.Queue()
.push(function waitForLoadingGadget() {
// Wait for the loading gadget to be created
return wait_for_gadget_loaded;
})
.push(function handleLoadingGadget(result_list) {
TmpConstructor = result_list[0];
root_gadget = result_list[1];
embedded_channel = result_list[2];
declare_method_list_waiting = result_list[3];
// Wait for all the gadget dependencies to be loaded
return all_dependency_loaded_deferred.promise;
})
.push(function waitForDeclareMethodList() {
// Wait for all methods to be correctly declared
return RSVP.all(declare_method_list_waiting);
})
.push(function waitForMutationObserver(result_list) {
if (embedded_channel !== undefined) {
finishAqParentConfiguration(TmpConstructor, root_gadget,
embedded_channel);
}
// Check all DOM modifications to correctly start/stop services
return configureMutationObserver(TmpConstructor, url, root_gadget);
})
.push(function waitForReadyList() {
// Trigger all ready functions
return triggerReadyList(TmpConstructor, root_gadget);
})
.push(function notifyReady() {
if (embedded_channel !== undefined) {
embedded_channel.notify({method: "ready"});
}
})
.push(undefined, function handleBootstrapError(e) {
letsCrash(e);
if (embedded_channel !== undefined) {
embedded_channel.notify({method: "failed", params: e.toString()});
}
throw e;
});
}
bootstrap(
removeHash(window.location.href)
);
}(document, window, RSVP, DOMParser, Channel, MutationObserver, Node,
FileReader, Blob, navigator, Event, URL));
!function(a){var b,c;!function(){var a={},d={};b=function(b,c,d){a[b]={deps:c,callback:d}},c=function(b){if(d[b])return d[b];d[b]={};var e=a[b];if(!e)throw new Error("Module '"+b+"' not found.");for(var f,g=e.deps,h=e.callback,i=[],j=0,k=g.length;k>j;j++)"exports"===g[j]?i.push(f={}):i.push(c(g[j]));var l=h.apply(this,i);return d[b]=f||l}}(),b("rsvp/all",["rsvp/promise","exports"],function(a,b){"use strict";function c(a,b){function c(){for(var a,c=0;c<b.length;c++)a=b[c],a&&"function"==typeof a.then&&"function"==typeof a.cancel&&a.cancel()}if("[object Array]"!==Object.prototype.toString.call(b))throw new TypeError("You must pass an array to all.");return new f(function(d,e,f){function g(a){return function(b){h(a,b)}}function h(a,b){l[a]=b,--m===n&&(0===n?d(l):(d(b),c()))}function i(a){return function(b){f({index:a,value:b})}}function j(a){e(a),c()}var k,l=[],m=b.length,n=b.length-a;0===m&&(1===a?d():d([]));for(var o=0;o<b.length;o++)k=b[o],k&&"function"==typeof k.then?k.then(g(o),j,i(o)):h(o,k)},c)}function d(a){return c(a.length,a)}function e(a){return c(1,a)}var f=a.Promise;b.all=d,b.any=e}),b("rsvp/async",["exports"],function(a){"use strict";function b(){if("function"==typeof Promise&&"function"==typeof Promise.resolve)try{var a=new Promise(function(){});if("[object Promise]"==={}.toString.call(a))return!0}catch(b){}return!1}function c(){var a=Promise.resolve();return function(b,c){a.then(function(){b(c)})}}function d(){return function(a,b){process.nextTick(function(){a(b)})}}function e(){return function(a,b){setImmediate(function(){a(b)})}}function f(){var a=[],b=new j(function(){var b=a.slice();a=[],b.forEach(function(a){var b=a[0],c=a[1];b(c)})}),c=document.createElement("div");return b.observe(c,{attributes:!0}),window.addEventListener("unload",function(){b.disconnect(),b=null},!1),function(b,d){a.push([b,d]),c.setAttribute("drainQueue","drainQueue")}}function g(){return function(a,b){k.setTimeout(function(){a(b)},1)}}var h,i="undefined"!=typeof window?window:{},j=i.MutationObserver||i.WebKitMutationObserver,k="undefined"!=typeof global?global:this;h="function"==typeof setImmediate?e():"undefined"!=typeof process&&"[object process]"==={}.toString.call(process)?d():j?f():b()?c():g(),a.async=h}),b("rsvp/cancellation_error",["exports"],function(a){"use strict";function b(a){if(this.name="cancel",void 0!==a&&"string"!=typeof a)throw new TypeError("You must pass a string.");this.message=a||"Default Message"}b.prototype=new Error,b.prototype.constructor=b,a.CancellationError=b}),b("rsvp/config",["rsvp/async","exports"],function(a,b){"use strict";var c=a.async,d={};d.async=c,b.config=d}),b("rsvp/defer",["rsvp/promise","exports"],function(a,b){"use strict";function c(){var a={resolve:void 0,reject:void 0,promise:void 0};return a.promise=new d(function(b,c){a.resolve=b,a.reject=c}),a}var d=a.Promise;b.defer=c}),b("rsvp/events",["exports"],function(a){"use strict";var b=function(a,b){this.type=a;for(var c in b)b.hasOwnProperty(c)&&(this[c]=b[c])},c=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c][0]===b)return c;return-1},d=function(a){var b=a._promiseCallbacks;return b||(b=a._promiseCallbacks={}),b},e={mixin:function(a){return a.on=this.on,a.off=this.off,a.trigger=this.trigger,a},on:function(a,b,e){var f,g,h=d(this);for(a=a.split(/\s+/),e=e||this;g=a.shift();)f=h[g],f||(f=h[g]=[]),-1===c(f,b)&&f.push([b,e])},off:function(a,b){var e,f,g,h=d(this);for(a=a.split(/\s+/);f=a.shift();)b?(e=h[f],g=c(e,b),-1!==g&&e.splice(g,1)):h[f]=[]},trigger:function(a,c){var e,f,g,h,i,j=d(this);if(e=j[a])for(var k=0;k<e.length;k++)f=e[k],g=f[0],h=f[1],"object"!=typeof c&&(c={detail:c}),i=new b(a,c),g.call(h,i)}};a.EventTarget=e}),b("rsvp/hash",["rsvp/defer","exports"],function(a,b){"use strict";function c(a){var b=0;for(var c in a)b++;return b}function d(a){var b={},d=e(),f=c(a);0===f&&d.resolve({});var g=function(a){return function(b){h(a,b)}},h=function(a,c){b[a]=c,0===--f&&d.resolve(b)},i=function(a){d.reject(a)};for(var j in a)a[j]&&"function"==typeof a[j].then?a[j].then(g(j),i):h(j,a[j]);return d.promise}var e=a.defer;b.hash=d}),b("rsvp/node",["rsvp/promise","rsvp/all","exports"],function(a,b,c){"use strict";function d(a,b){return function(c,d){c?b(c):a(arguments.length>2?Array.prototype.slice.call(arguments,1):d)}}function e(a){return function(){var b,c,e=Array.prototype.slice.call(arguments),h=this,i=new f(function(a,d){b=a,c=d});return g(e).then(function(e){e.push(d(b,c));try{a.apply(h,e)}catch(f){c(f)}}),i}}var f=a.Promise,g=b.all;c.denodeify=e}),b("rsvp/promise",["rsvp/config","rsvp/events","rsvp/cancellation_error","exports"],function(a,b,c,d){"use strict";function e(a){return f(a)||"object"==typeof a&&null!==a}function f(a){return"function"==typeof a}function g(a){m.onerror&&m.onerror(a.detail)}function h(a,b){a===b?j(a,b):i(a,b)||j(a,b)}function i(a,b){var c,d=null;try{if(a===b)throw new TypeError("A promises callback cannot return that same promise.");if(e(b)&&(d=b.then,f(d)))return f(b.on)&&b.on("promise:notified",function(b){l(a,b.detail)}),a.on("promise:cancelled",function(a){f(b.cancel)&&b.cancel()}),d.call(b,function(d){return c?!0:(c=!0,void(b!==d?h(a,d):j(a,d)))},function(b){return c?!0:(c=!0,void k(a,b))}),!0}catch(g){return k(a,g),!0}return!1}function j(a,b){m.async(function(){a.isFulfilled||a.isRejected||(a.trigger("promise:resolved",{detail:b}),a.isFulfilled=!0,a.fulfillmentValue=b)})}function k(a,b){m.async(function(){a.isFulfilled||a.isRejected||(a.trigger("promise:failed",{detail:b}),a.isRejected=!0,a.rejectedReason=b)})}function l(a,b){m.async(function(){a.trigger("promise:notified",{detail:b})})}var m=a.config,n=b.EventTarget,o=c.CancellationError,p=function(a,b){var c=this,d=!1;if("function"!=typeof a)throw new TypeError("You must pass a resolver function as the sole argument to the promise constructor");if(void 0!==b&&"function"!=typeof b)throw new TypeError("You can only pass a canceller function as the second argument to the promise constructor");if(!(c instanceof p))return new p(a,b);var e=function(a){d||(d=!0,h(c,a))},f=function(a){d||(d=!0,k(c,a))},i=function(a){d||l(c,a)};this.on("promise:failed",function(a){this.trigger("error",{detail:a.detail})},this),this.on("error",g),this.cancel=function(){if(!d){if(void 0!==b)try{b()}catch(a){return void f(a)}f(new o)}};try{a(e,f,i)}catch(j){f(j)}},q=function(a,b,c,d){var e,g,j,l,m=f(c);if(!b.isFulfilled&&!b.isRejected){if(m)try{e=c(d.detail),j=!0}catch(n){l=!0,g=n}else e=d.detail,j=!0;i(b,e)||(m&&j?h(b,e):l?k(b,g):"resolve"===a?h(b,e):"reject"===a&&k(b,e))}},r=function(a,b,c){var d;if("function"==typeof b){try{d=b(c.detail)}catch(e){return}l(a,d)}else l(a,c.detail)};p.prototype={constructor:p,isRejected:void 0,isFulfilled:void 0,rejectedReason:void 0,fulfillmentValue:void 0,then:function(a,b,c){this.off("error",g);var d=new this.constructor(function(){},function(){d.trigger("promise:cancelled",{})});return this.isFulfilled&&m.async(function(b){q("resolve",d,a,{detail:b.fulfillmentValue})},this),this.isRejected&&m.async(function(a){q("reject",d,b,{detail:a.rejectedReason})},this),this.on("promise:resolved",function(b){q("resolve",d,a,b)}),this.on("promise:failed",function(a){q("reject",d,b,a)}),this.on("promise:notified",function(a){r(d,c,a)}),d},fail:function(a){return this.then(null,a)},always:function(a){return this.then(a,a)}},n.mixin(p.prototype),d.Promise=p}),b("rsvp/queue",["rsvp/promise","rsvp/resolve","exports"],function(a,b,c){"use strict";function d(a){if(this.name="resolved",void 0!==a&&"string"!=typeof a)throw new TypeError("You must pass a string.");this.message=a||"Default Message"}var e=a.Promise,f=b.resolve;d.prototype=new Error,d.prototype.constructor=d;var g=function(){function a(){for(var a=0;2>a;a++)l[a].cancel()}var b,c,h,i,j,k=this,l=[];return this instanceof g?(b=new e(function(a,b,d){c=function(b){return j?void 0:(k.isFulfilled=!0,k.fulfillmentValue=b,j=!0,a(b))},h=function(a){return j?void 0:(k.isRejected=!0,k.rejectedReason=a,j=!0,b(a))},i=d},a),l.push(f()),l.push(l[0].then(function(){l.splice(0,2),0===l.length&&c()})),k.cancel=function(){j||(j=!0,b.cancel(),b.fail(function(a){k.isRejected=!0,k.rejectedReason=a}))},k.then=function(){return b.then.apply(b,arguments)},void(k.push=function(a,b,e){var f,g=l[l.length-1];if(j)throw new d;return f=g.then(a,b,e),l.push(f),g=f.then(function(a){return l.splice(0,2),0!==l.length?a:void c(a)},function(a){if(l.splice(0,2),0!==l.length)throw a;h(a)},function(a){return l[l.length-1]===g&&i(a),a}),l.push(g),this})):new g};g.prototype=Object.create(e.prototype),g.prototype.constructor=g,c.Queue=g,c.ResolvedQueueError=d}),b("rsvp/reject",["rsvp/promise","exports"],function(a,b){"use strict";function c(a){return new d(function(b,c){c(a)})}var d=a.Promise;b.reject=c}),b("rsvp/resolve",["rsvp/promise","exports"],function(a,b){"use strict";function c(a){return new d(function(b,c){if("object"==typeof a&&null!==a){var d=a.then;if(void 0!==d&&"function"==typeof d)return d.apply(a,[b,c])}return b(a)},function(){void 0!==a&&void 0!==a.cancel&&a.cancel()})}var d=a.Promise;b.resolve=c}),b("rsvp/rethrow",["exports"],function(a){"use strict";function b(a){throw c.setTimeout(function(){throw a}),a}var c="undefined"==typeof global?this:global;a.rethrow=b}),b("rsvp/timeout",["rsvp/promise","exports"],function(a,b){"use strict";function c(a,b,c){function d(d,e){g=setTimeout(function(){b?e(c):d(c)},a)}function e(){clearTimeout(g)}var g;return new f(d,e)}function d(a,b){return c(a,!1,b)}function e(a){return c(a,!0,"Timed out after "+a+" ms")}var f=a.Promise;f.prototype.delay=function(a){return this.then(function(b){return d(a,b)})},b.delay=d,b.timeout=e}),b("rsvp",["rsvp/events","rsvp/cancellation_error","rsvp/promise","rsvp/node","rsvp/all","rsvp/queue","rsvp/timeout","rsvp/hash","rsvp/rethrow","rsvp/defer","rsvp/config","rsvp/resolve","rsvp/reject","exports"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n){"use strict";function o(a,b){C[a]=b}var p=a.EventTarget,q=b.CancellationError,r=c.Promise,s=d.denodeify,t=e.all,u=e.any,v=f.Queue,w=f.ResolvedQueueError,x=g.delay,y=g.timeout,z=h.hash,A=i.rethrow,B=j.defer,C=k.config,D=l.resolve,E=m.reject;n.CancellationError=q,n.Promise=r,n.EventTarget=p,n.all=t,n.any=u,n.Queue=v,n.ResolvedQueueError=w,n.delay=x,n.timeout=y,n.hash=z,n.rethrow=A,n.defer=B,n.denodeify=s,n.configure=o,n.resolve=D,n.reject=E}),window.RSVP=c("rsvp")}(window);
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment