347 lines
12 KiB
JavaScript
Executable File
347 lines
12 KiB
JavaScript
Executable File
/**
|
|
* @category MyEic
|
|
* @subcategory Libraries
|
|
* @extends Controller
|
|
*/
|
|
class EICController extends Controller {
|
|
|
|
constructor() {
|
|
super();
|
|
this.view2url = {};
|
|
}
|
|
|
|
getViewByClass(className) {
|
|
let content = Controller._contents.find(o => o.view._className == className)
|
|
return content ? content.view: null;
|
|
}
|
|
|
|
getViewByURL(url) {
|
|
let content = Controller._contents.find(o => o.view._url == url)
|
|
return content ? content.view: null;
|
|
}
|
|
|
|
loadContent(name, options, data) {
|
|
options = options || {};
|
|
options.onContentLoaded = this.createContent;
|
|
return super.loadView(name, options, data);
|
|
}
|
|
|
|
createContent(options, data, html) {
|
|
|
|
let container = ui.create('<div></div>');
|
|
container.innerHTML = Controller.processTemplate(options.name, html, data);
|
|
|
|
let view = new app.LoadedClasses[options.className](options);
|
|
view._className = options.name.replace('.html', '');
|
|
view._controller = this;
|
|
view.el = container;
|
|
view.DOMContentLoaded(data);
|
|
|
|
container.setAttribute('sparc-id', view._sparcId);
|
|
|
|
return view;
|
|
}
|
|
|
|
loadWindow(name, options, data) {
|
|
let url = app.Router.currentRoute.realUrl;
|
|
options = options || {};
|
|
// if static, redirect to existing instance
|
|
if(options.static) {
|
|
/*
|
|
MFA:
|
|
Using stored real url instead of classname allows different instance of window based on parameter:
|
|
eg:
|
|
having route "/window/{id}", calling "/window/345" won't replace "/window/123"
|
|
|
|
Makes me wonder: shouldn't all windows be static anyway then ?
|
|
*/
|
|
//let existing = this.getViewByClass(name);
|
|
let existing = this.getViewByURL(url);
|
|
|
|
if(existing) {
|
|
this.focus(existing._sparcId, data);
|
|
return;
|
|
}
|
|
}
|
|
options.onContentLoaded = this.createWindow;
|
|
super.loadView(name, options, data).then(
|
|
view => {
|
|
this.view2url[view._sparcId] = url;
|
|
view._url = url
|
|
}
|
|
);
|
|
}
|
|
|
|
createWindow(options, data, html) {
|
|
|
|
if(!(options.className in app.LoadedClasses)){
|
|
console.error(`Missing view ${options.className} !\nLoad explicitely it from your controller, or as set it as a dependency...`);
|
|
return null;
|
|
}
|
|
|
|
let view = new app.LoadedClasses[options.className]();
|
|
view._className = options.name.replace('.html', '');
|
|
view._controller = this;
|
|
|
|
Controller._contents.push({
|
|
view: view,
|
|
type: 'window',
|
|
expanded: options.expanded || false,
|
|
visible: true,
|
|
active: false
|
|
});
|
|
|
|
let content = ui.create(`<div class="window">
|
|
<header class="cols-2 right">
|
|
<h1>${options.title || ''}</h1>
|
|
<div class="controls">
|
|
<button eicbutton data-id="${view._sparcId}" basic primary rounded xsmall class="icon-copy shrink" title="shrink"></button>
|
|
<button eicbutton data-id="${view._sparcId}" basic primary rounded xsmall class="icon-square-o expand" title="expand"></button>
|
|
<button eicbutton data-id="${view._sparcId}" basic primary rounded xsmall class="icon-cancel close" title="close"></button>
|
|
</div>
|
|
</header>
|
|
<section></section>
|
|
</div>`);
|
|
if(options.expanded) content.setAttribute('expanded','');
|
|
|
|
let container = content.querySelector('section');
|
|
container.innerHTML = Controller.processTemplate(options.name, html, data);
|
|
|
|
view.el = content;
|
|
|
|
// setting up window controls
|
|
let expand = content.querySelector('header button.expand');
|
|
expand.addEventListener('click', view.expand.bind(view));
|
|
|
|
let shrink = content.querySelector('header button.shrink');
|
|
shrink.addEventListener('click', view.shrink.bind(view));
|
|
|
|
let close = content.querySelector('header button.close');
|
|
close.addEventListener('click', this.onclose.bind(this));
|
|
|
|
content.addEventListener('click', this.onFocusRequest.bind(this));
|
|
content.addEventListener('expanded', this.onFocusRequest.bind(this));
|
|
content.addEventListener('shrinked', this.onFocusRequest.bind(this));
|
|
|
|
content.setAttribute('sparc-id', view._sparcId);
|
|
|
|
let parent = Controller._template.view.find('.app-workspace');
|
|
parent.appendChild(content);
|
|
|
|
this.addThesaurus(view._sparcId, options.title || 'a window')
|
|
|
|
view.DOMContentLoaded(data);
|
|
view.initWindowEvents();
|
|
this.focus(view._sparcId);
|
|
|
|
return view;
|
|
}
|
|
|
|
openDialog(view) {
|
|
|
|
let promise = new Promise(function(resolve) {
|
|
if(typeof view === 'string') {
|
|
let message = view;
|
|
view = new EICDialogContent();
|
|
view.el = message;
|
|
}
|
|
|
|
let dialog = EICController.createDialog(view);
|
|
|
|
function commit(result) {
|
|
EICController.closeDialog(view._sparcId);
|
|
resolve(result);
|
|
}
|
|
|
|
function abort(result) {
|
|
EICController.closeDialog(view._sparcId);
|
|
resolve(result);
|
|
}
|
|
|
|
view.commit = commit;
|
|
view.abort = abort;
|
|
view.buttons.forEach(function(button) {
|
|
dialog.querySelector('[eicdialog] > [eiccard] > footer').append(button.el);
|
|
});
|
|
|
|
view.DOMContentFocused();
|
|
});
|
|
|
|
return promise;
|
|
}
|
|
|
|
/**
|
|
* Used by a view that needs to change the current URL
|
|
* Typically happens when the view allows you to switch to another instance of the business-object
|
|
* handled by this controiller. Doinbg a history.replaceState in the view is not enough, because
|
|
* the controller needs to keep track of his current URL (in view2url) for changing
|
|
* the Browser URL bar when -later- changing window focus.
|
|
* @param {string} newUrl
|
|
*/
|
|
changeUrl(view, newUrl) {
|
|
history.replaceState(null, null, newUrl)
|
|
this.view2url[view._sparcId] = newUrl
|
|
view._url = newUrl
|
|
}
|
|
|
|
static createDialog(dialog, options) {
|
|
|
|
options = options || {};
|
|
|
|
let container = ui.create(`<div eicdialog sparc-id="${dialog._sparcId}">
|
|
<article eiccard ${ options.closable || ''}>
|
|
${ dialog.options.title ? `<header><h1>${dialog.options.title}</h1>${dialog.options.subtitle ? `<h2>${dialog.options.subtitle}</h2>`: '' }</header>`: ''}
|
|
<section></section>
|
|
<footer></footer>
|
|
</article>
|
|
</div>`);
|
|
|
|
ui.eicfy(container);
|
|
|
|
container.querySelector('[eiccard] section').append(dialog.el);
|
|
dialog.parentContainer = container;
|
|
|
|
document.body.append(container)
|
|
|
|
return container;
|
|
}
|
|
|
|
static closeDialog(id) { document.body.querySelector(`[sparc-id="${id}"]`).remove(); }
|
|
|
|
addThesaurus(id, title) {
|
|
let container = Controller._template.view.find('.app-content-thesaurus');
|
|
container.querySelectorAll('[eicchip]').forEach(el => el.setAttribute('secondary', ''))
|
|
|
|
let chip = new Chip(null, {label: title, destroyable: true});
|
|
chip.el.setAttribute('data-id', id);
|
|
chip.addEventListener('click', this.onSelectThesaurus.bind(this));
|
|
chip.addEventListener('destroy', this.onRemoveThesaurus.bind(this));
|
|
|
|
container.append(chip.el);
|
|
}
|
|
|
|
removeThesaurus(id) {
|
|
let container = Controller._template.view.find('.app-content-thesaurus');
|
|
|
|
let chip = container.querySelector(`[data-id="${id}"]`)
|
|
if(chip) chip.remove();
|
|
|
|
let remaining = container.querySelectorAll('[eicchip]');
|
|
|
|
if(remaining.length > 0) {
|
|
remaining.item(remaining.length - 1).dispatchEvent(new MouseEvent('click'));
|
|
}
|
|
}
|
|
|
|
close(id) {
|
|
this.removeThesaurus(id);
|
|
this.unloadView(Controller.getContentById(id));
|
|
if(this.view2url.hasOwnProperty(id)) delete(this.view2url[id]);
|
|
}
|
|
|
|
focus(id, data) {
|
|
this.selectThesaurus(id);
|
|
let curUrl = new URL(document.location.href).pathname
|
|
if(this.view2url.hasOwnProperty(id)) {
|
|
history.replaceState(null, null, this.view2url[id]);
|
|
curUrl = this.view2url[id]
|
|
}
|
|
|
|
if(app.matomo) app.matomo.logAction('Focus window / '+curUrl)
|
|
|
|
let next = Controller.getContentById(id);
|
|
|
|
if(Controller._currentContent) {
|
|
if(next.expanded) {
|
|
this.blur(Controller._currentContent);
|
|
Controller._contents.filter(item => item.type == 'window').forEach(item => this.desactivate(item));
|
|
Controller._contents.filter(item => !item.expanded).forEach(item => this.blur(item));
|
|
} else {
|
|
Controller._contents.filter(item => !item.expanded && item.type == 'window').forEach(item => this.attach(item));
|
|
this.attach(Controller._currentContent);
|
|
|
|
let altexpand = Controller._contents.filter(item => item.expanded && item.visible && item.type == 'window');
|
|
|
|
if(altexpand.length == 0) {
|
|
let latest = Controller._contents.reverse().find(item => item.expanded);
|
|
if(latest) { this.attach(latest); }
|
|
}
|
|
}
|
|
}
|
|
Controller._currentContent = next;
|
|
this.activate(Controller._currentContent, data);
|
|
Controller._currentContent.view.DOMContentResized();
|
|
}
|
|
|
|
desactivate(content) {
|
|
content.view.el.classList.remove('active');
|
|
content.active = false;
|
|
if(content.expanded) this.blur(content);
|
|
}
|
|
|
|
activate(content, data) {
|
|
Controller._contents.forEach(o => o.active = false);
|
|
Controller._contents.forEach(o => o.view.el.classList.remove('active'));
|
|
content.view.el.classList.add('active');
|
|
content.active = true;
|
|
|
|
if(!content.visible) this.attach(content);
|
|
|
|
content.view.DOMContentFocused(data);
|
|
}
|
|
|
|
attach(content) {
|
|
ui.show(content.view.el,!content.expanded ? 'grid': 'block');
|
|
content.visible = true;
|
|
}
|
|
|
|
blur(content) {
|
|
ui.hide(content.view.el);
|
|
content.visible = false;
|
|
content.active = false;
|
|
content.view.DOMContentBlured();
|
|
}
|
|
|
|
onclose(event) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
|
|
this.close(event.currentTarget.dataset.id)
|
|
}
|
|
|
|
onSelectThesaurus(event) {
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
|
|
this.focus(event.currentTarget.dataset.id);
|
|
}
|
|
|
|
selectThesaurus(id) {
|
|
let container = Controller._template.view.find('.app-content-thesaurus');
|
|
container.querySelectorAll('[eicchip]').forEach(el => el.setAttribute('secondary', ''))
|
|
container.querySelector(`[eicchip][data-id="${id}"]`).removeAttribute('secondary');
|
|
}
|
|
onRemoveThesaurus(event) {
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
|
|
this.close(event.currentTarget.dataset.id);
|
|
}
|
|
|
|
onFocusRequest(event) {
|
|
|
|
let view = event.currentTarget;
|
|
while(!view.hasAttribute('sparc-id') && !view.classList.contains('window')) view = view.parentElement;
|
|
let content = Controller.getContentById(view.getAttribute('sparc-id'));
|
|
if(!content.active || content.expanded != content.view.expanded || (Controller._currentContent.view._sparcId != content.view._sparcId)) {
|
|
content.expanded = content.view.expanded;
|
|
this.focus(view.getAttribute('sparc-id'));
|
|
}
|
|
}
|
|
|
|
static resize() {
|
|
Controller._contents.forEach(item => item.view.DOMContentResized())
|
|
}
|
|
}
|
|
|
|
app.registerClass('EICController', EICController); |