UNPKG

elation

Version:

Elation Javascript Component Framework

547 lines (532 loc) 20.1 kB
/** * Window UI component * * @class window * @augments elation.ui.base * @memberof elation.ui * * @param {object} args[] * @param {array} args.items * @param {boolean} args.controls * @param {string} args.title * @param {string} args.content * @param {array} args.position * @param {boolean} args.center * @param {int} args.width * @param {int} args.height * @param {int} args.top * @param {int} args.bottom * @param {int} args.left * @param {int} args.right */ elation.require(['elements.elements', 'elements.ui.button', 'elements.ui.buttonbar'], function() { elation.requireCSS('ui.window'); elation.elements.define('ui.window', class extends elation.elements.ui.panel { init() { super.init(); this.defineAttributes({ title: { type: 'string', default: '' }, windowtitle: { type: 'string', default: '' }, // Using 'title' involves browser default tooltip behavior, consider forcing a rename, in the meantime we support 'windowtitle' as well //toolbar: { type: 'object' }, position: { type: 'vector2' }, content: { type: 'object' }, movable: { type: 'boolean' }, controls: { type: 'boolean' }, center: { type: 'boolean' }, left: { type: 'boolean' }, right: { type: 'boolean' }, top: { type: 'boolean' }, bottom: { type: 'boolean' }, width: { type: 'string' }, height: { type: 'height' }, resizable: { type: 'boolean' }, scrollable: { type: 'boolean' }, minimizable: { type: 'boolean' }, maximizable: { type: 'boolean' }, closable: { type: 'boolean' }, }); this.offsetpos = [0, 0]; } initUIWindow() { this.initialized = true; this.windownum = (elation.elements.ui.window.numwindows ? elation.elements.ui.window.numwindows : 0); elation.elements.ui.window.numwindows = this.windownum + 1; //this.style.top = 0; //this.style.left = 0; var content = this.getElementsByTagName('ui-window-content'); if (content.length > 0) { this.content = content[0]; } else { if (this.childNodes.length > 0) { this.content = document.createElement('ui-window-content'); var children = []; while (this.childNodes.length > 0) { var child = this.childNodes[0]; this.removeChild(child); this.content.appendChild(child); } } } if (!this.titlebar) { this.titlebar = elation.elements.create('ui.window.titlebar', {append: this}); } this.toolbar = false; this.minimized = false; this.maximized = false; this.transformorigin = "50% 50%"; this.labels = { minimize: '–', maximize: '□', restore: '₪', close: 'x' }; if (this.controls !== false && this.controls != 0 && this.controls !== 'false') { this.createcontrols(); } if (this.windowtitle) { this.settitle(this.windowtitle); } else { this.settitle(this.title); } if (this.toolbar) { this.settoolbar(this.toolbar); } if (this.position) { this.setposition(this.position); } this.setcontent(this.content); elation.events.add(window, 'resize,orientationchange', this); var curpos = elation.html.position(this); //this.addclass("ui_window"); if (this.movable !== false) { this.addclass('state_movable'); elation.events.add(this, 'mousedown,touchstart', this); } if (this.left) this.addclass('orientation_left'); if (this.right) this.addclass('orientation_right'); if (this.top) this.addclass('orientation_top'); if (this.bottom) this.addclass('orientation_bottom'); if (this.center) this.addclass('orientation_center'); if (this.width) { this.style.width = this.width; } if (this.height) { this.content.style.height = this.height; } this.refresh(); if (this.titlebar.parentNode !== this) this.appendChild(this.titlebar); this.focus(true); } render() { //super.render(); if (!this.initialized) { this.initUIWindow(); } this.size = this.getsize(); if (this.center) { this.centerwindow(); } // TODO - should "center" be an orientation too? if (this.orientation) { this.setOrientation(this.orientation); } if (this.top) { this.setposition([this.offsetpos[0], this.top]); } else if (this.bottom) { this.setposition([this.offsetpos[0], window.innerHeight - this.offsetHeight - this.bottom]); } if (this.left) { this.setposition([this.left, this.offsetpos[1]]); } else if (this.right) { this.setposition([window.innerWidth - this.offsetWidth - this.right, this.offsetpos[1]]); } } focus(skipmove) { if (!this.active) { this.windownum = elation.elements.ui.window.numwindows++; // first remove focus from any existing active windows var activewindows = elation.find('.ui_window.state_active'); if (activewindows.length > 0) { for (var i = 0; i < activewindows.length; i++) { //elation.component.fetch(activewindows[i]).blur(); } } this.addclass('state_active'); if (this.minimized) { //this.minimize(); } else {[] //this.setposition((this.maximized ? [0,0] : this.offsetpos), false); elation.html.transform(this, this.gettransform(), this.transformorigin, (skipmove ? '' : 'all 100ms ease-in-out')); } this.active = true; elation.events.fire({type: 'focus', element: this}); } } blur() { if (this.active) { this.active = false; elation.html.removeclass(this, 'state_active'); elation.events.fire({type: 'blur', element: this}); } } createcontrols() { var buttons = {}; if (this.minimizable !== false && this.minimizable != 0) { buttons.minimize = { label: this.labels.minimize, classname: 'ui_window_control_minimize', onclick: (ev) => this.minimize(ev) }; } if (this.maximizable !== false && this.maximizable != 0) { buttons.maximize = { label: this.labels.maximize, classname: 'ui_window_control_maximize', onclick: (ev) => this.maximize(ev) }; } if (this.closable !== false && this.closable != 0) { buttons.close = { label: this.labels.close, classname: 'ui_window_control_close', onclick: (ev) => this.close(ev), ontouchend: (ev) => this.close(ev) } } /* this.controls = elation.ui.buttonbar(null, elation.html.create({classname: 'ui_window_controls'}), { buttons: buttons }); */ this.controlbuttons = document.createElement('ui-buttonbar'); this.controlbuttons.buttons = buttons; this.addclass('ui_window_withcontrols'); if (this.resizable !== false && this.resizable !== 0 && this.resizable !== '0') { this.resizer = elation.html.create({tag: 'div', classname: 'ui_window_resizer', append: this}); } } open() { this.show(); this.visible = true; elation.events.fire({type: 'ui_window_open', element: this}); } close(ev) { if (this.parentNode) { //this.parentNode.removeChild(this); } this.hide(); this.visible = false; elation.events.fire({type: 'ui_window_close', element: this}); if (ev) ev.stopPropagation(); } minimize(ev) { if (this.maximized) { this.maximize(); } if (!this.minimized) { // minimize if (!this.oldtransform) { this.oldtransform = elation.html.transform(this); } this.windownum = -1; elation.html.transform(this, this.gettransform(false, false, .25), this.transformorigin, 'all 100ms ease-out'); this.addclass('state_minimized'); //this.controls.buttons.minimize.setLabel(this.labels.restore); //this.controls.buttons.maximize.setLabel(this.labels.maximize); this.minimized = true; this.blur(); elation.events.fire({type: 'ui_window_minimize', element: this}); } else { // restore elation.html.removeclass(this, 'state_minimized'); if (this.oldtransform) { this.oldtransform = false; } //this.controls.buttons.minimize.setLabel(this.labels.minimize); this.minimized = false; elation.html.transform(this, this.gettransform(), this.transformorigin, 'all 100ms ease-out'); elation.events.fire({type: 'ui_window_restore', element: this}); } if (ev) ev.stopPropagation(); } maximize(ev) { if (!this.maximized) { // maximize this.focus(); this.addclass('state_maximized'); /* elation.html.transform(this, this.gettransform([0,0]), this.transformorigin, 'none'); //'all 100ms ease-out'); this.style.width = window.innerWidth + 'px'; this.style.height = window.innerHeight + 'px'; */ this.restorestate = [this.getposition(), this.getsize()]; this.setposition([0,0]); this.setsize([window.innerWidth, window.innerHeight]); //this.controls.buttons.minimize.setLabel(this.labels.minimize); //this.controls.buttons.maximize.setLabel(this.labels.restore); this.maximized = true; elation.events.fire({type: 'ui_window_maximize', element: this}); } else { // restore elation.html.removeclass(this, 'state_maximized'); this.setposition(this.restorestate[0]); this.setsize(this.restorestate[1]); //elation.html.transform(this, this.gettransform(), this.transformorigin, 'none'); //'all 100ms ease-out'); //this.controls.buttons.maximize.setLabel(this.labels.maximize); this.maximized = false; elation.events.fire({type: 'ui_window_restore', element: this}); } if (this.minimized) { elation.html.removeclass(this, 'state_minimized'); // clear minimized flag if set this.minimized = false; } if (ev) ev.stopPropagation(); } getsize() { return [this.offsetWidth, this.offsetHeight]; } setsize(size) { elation.html.transform(this, this.gettransform(), this.transformorigin, 'none'); if (this.style.width != 'auto') this.style.width = 'auto'; if (this.style.height != 'auto') this.style.height = 'auto'; this.content.style.width = size[0] + 'px'; this.content.style.height = (Math.min(window.innerHeight, size[1]) - this.titlebar.offsetHeight - (this.toolbar ? this.toolbar.offsetHeight : 0)) + 'px'; this.size[0] = size[0]; this.size[1] = size[1]; //alert('setted:' + this.size[0] + 'x' + this.size[1] + ", " + this.content.style.width + " x " + this.content.style.height); } centerwindow() { var dim = elation.html.dimensions(this); if (dim.w > window.innerWidth && !this.maximized) { this.maximize(); //this.setsize([window.innerWidth, window.innerHeight]); } var realx = (window.innerWidth - this.offsetWidth) / 2; var realy = (window.innerHeight - this.offsetHeight) / 2; // TODO - border width should be detected automatically using getComputedStyle var borderwidth = 4; this.content.style.maxHeight = (window.innerHeight - this.content.offsetTop - borderwidth) + 'px'; this.setposition([realx, realy]); } drag(diff) { } getposition() { return [this.offsetpos[0], this.offsetpos[1]]; } setposition(pos, animate) { this.offsetpos[0] = pos[0]; this.offsetpos[1] = pos[1]; elation.html.transform(this, this.gettransform(), this.transformorigin, (animate ? 'all 100ms ease-in-out' : 'none')); } settitle(newtitle) { if (newtitle instanceof HTMLElement) { if (this.titlebar) { this.replaceChild(newtitle, this.titlebar); } else { this.appendChild(newtitle); } this.titlebar = newtitle; if (!elation.html.hasclass(this.titlebar, 'ui_window_titlebar')) { elation.html.addclass(this.titlebar, 'ui_window_titlebar'); } } else { if (!this.titlebar) { this.titlebar = elation.elements.create('ui.window.titlebar', {append: this}); } this.titlebar.innerHTML = "<span class='ui_window_titlebar_span'>"+newtitle+"</span>" || ''; } if (this.controlbuttons) { //this.titlebar.appendChild(this.controlbuttons); this.titlebar.insertBefore(this.controlbuttons, this.titlebar.firstChild); } } settoolbar(newtoolbar) { if (this.toolbar) { this.removeChild(this.toolbar); } if (newtoolbar instanceof elation.component.base) { newtoolbar = newtoolbar.container; } else if (newtoolbar instanceof HTMLElement) { // ... } else { newtoolbar = elation.html.create({tag: 'div', content: newtoolbar}); } this.toolbar = newtoolbar; this.insertBefore(newtoolbar, this.titlebar.nextSibling); if (!elation.html.hasclass(this.toolbar, 'ui_window_toolbar')) { elation.html.addclass(this.toolbar, 'ui_window_toolbar'); } } setcontent(newcontent) { if (newcontent instanceof HTMLElement) { this.setcontenthtml(newcontent); } else if (newcontent instanceof elation.component.base) { this.setcontenthtml(newcontent.container); } else { if (!this.content) { this.content = elation.html.create({tag: 'ui-window-content', classname: 'ui_window_content', append: this}); } if (!elation.utils.isNull(newcontent)) { this.content.innerHTML = newcontent; } } elation.component.init(); this.refresh(); //elation.html.addclass(newcontent, 'ui_window_content'); } setcontenthtml(newcontent) { if (this.content && this.content.parentNode) { this.content.parentNode.removeChild(this.content); } if (newcontent.parentNode) newcontent.parentNode.removeChild(newcontent); this.appendChild(newcontent); this.content = newcontent; } gettransform(pos, layer, scale) { if (!pos && pos !== 0) pos = this.offsetpos; if (!layer && layer !== 0) layer = this.windownum; if (!scale) scale = (this.minimized ? .25 : 1); return 'translate3d(' + Math.round(pos[0]) + 'px, ' + Math.round(pos[1]) + 'px, ' + layer + 'px) scale(' + scale + ')'; } animationstart() { this.animating = true; this.animate(); } animationend() { this.animating = false; } animate(animating) { if (this.animating && (this.dirtysize || this.dirtyposition)) { if (!this.boundfunc) this.boundfunc = elation.bind(this, this.animate); if (window.requestAnimationFrame) requestAnimationFrame(this.boundfunc); else if (window.webkitRequestAnimationFrame) webkitRequestAnimationFrame(this.boundfunc); else if (window.mozRequestAnimationFrame) mozRequestAnimationFrame(this.boundfunc); else if (window.msRequestAnimationFrame) msRequestAnimationFrame(this.boundfunc); else { setTimeout(this.boundfunc, 1/60); } } if (this.dirtysize) { this.dirtysize = false; this.setsize(this.size); } if (this.dirtyposition) { this.dirtyposition = false; this.setposition(this.offsetpos, false); } } dragstart(ev) { this.dragstartpos = (ev.touches ? [ev.touches[0].clientX, ev.touches[0].clientY] : [ev.clientX, ev.clientY]); this.dirtyposition = this.dirtysize = false; this.newpos = [0, 0]; if (elation.utils.isin(this.titlebar, ev.target) || this.minimized) { // titlebar dragging elation.html.addclass(this.titlebar, 'state_dragging'); this.dragging = false; elation.events.add(window, 'mousemove,mouseup,touchmove,touchend', this); this.animationstart(); ev.preventDefault(); } else if (ev.target == this.resizer) { this.size = this.getsize(); this.resizing = true; elation.events.add(window, 'mousemove,mouseup,touchmove,touchend', this); this.animationstart(); ev.preventDefault(); } } dragmove(ev) { this.newpos[0] = (ev.touches ? ev.touches[0].clientX : ev.clientX); this.newpos[1] = (ev.touches ? ev.touches[0].clientY : ev.clientY); //var diff = [this.dragstartpos[0] - newpos[0], this.dragstartpos[1] - newpos[1]]; // limit left side offset to prevent windows from getting lost //this.style.left = Math.max(newpos[0] + this.dragdims.x - this.dragstartpos[0], this.dragdims.w * -.9) + 'px'; //this.style.top = (newpos[1] + this.dragdims.y - this.dragstartpos[1]) + 'px'; //this.offsetpos = [Math.max(newpos[0] + this.dragdims.x - this.dragstartpos[0], this.dragdims.w * -.9), (newpos[1] + this.dragdims.y - this.dragstartpos[1])]; var wasanimating = (this.animating && (this.dirtysize || this.dirtyposition)); if (this.resizing) { if (this.right) { this.offsetpos[0] -= (this.dragstartpos[0] - this.newpos[0]); this.size[0] += (this.dragstartpos[0] - this.newpos[0]); } else { this.size[0] -= (this.dragstartpos[0] - this.newpos[0]); } if (this.bottom) { this.offsetpos[1] -= (this.dragstartpos[1] - this.newpos[1]); this.size[1] += (this.dragstartpos[1] - this.newpos[1]); } else { this.size[1] -= (this.dragstartpos[1] - this.newpos[1]); } this.dirtysize = true; } else { this.dirtyposition = true; this.offsetpos = [this.offsetpos[0] - (this.dragstartpos[0] - this.newpos[0]), this.offsetpos[1] - (this.dragstartpos[1] - this.newpos[1])]; } if (!wasanimating && (this.dirtysize || this.dirtyposition)) { this.animate(); } this.dragstartpos[0] = this.newpos[0]; this.dragstartpos[1] = this.newpos[1]; this.dragging = true; } dragend(ev) { if (this.resizing) { elation.events.remove(window, 'mousemove,mouseup,touchmove,touchend', this); this.resizing = false; } else { elation.events.remove(window, 'mousemove,mouseup,touchmove,touchend', this); elation.html.removeclass(this.titlebar, 'state_dragging'); if (this.minimized && !this.dragging) { this.minimize(); } } this.dragging = false; //this.dragstartpos = [0,0]; this.animationend(); } onmousedown(ev) { if (ev.button == 0) { this.dragstart(ev); } this.focus(); ev.stopPropagation(); } onmousemove(ev) { this.dragmove(ev); } onmouseup(ev) { if (ev.button == 0) { this.dragend(ev); } } ontouchstart(ev) { if (ev.touches.length == 1 && !this.maximized) { this.dragstart(ev); } this.focus(); } ontouchmove(ev) { if (ev.touches.length == 1 && !this.maximized) { this.dragmove(ev); } } ontouchend(ev) { if (ev.touches.length == 0) { this.dragend(ev); } } onresize(ev) { if (this.maximized) { this.setsize([window.innerWidth, window.innerHeight]); } this.refresh(); } onorientationchange(ev) { if (this.maximized) { this.setsize([window.innerWidth, window.innerHeight]); } this.refresh(); } }); elation.elements.define('ui.window.titlebar', class extends elation.elements.base { }); elation.elements.define('ui.window.content', class extends elation.elements.base { }); });