From f73f2356820468344757dbb9d7f3ec73ece7bf66 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 13 Feb 2023 20:43:51 +0100 Subject: Updating. Signed-off-by: Daniel Baumann --- web/_static/jsuites/jsuites.mobile.js | 1016 +++++++++++++++++++++++++++++++++ 1 file changed, 1016 insertions(+) create mode 100644 web/_static/jsuites/jsuites.mobile.js (limited to 'web/_static/jsuites/jsuites.mobile.js') diff --git a/web/_static/jsuites/jsuites.mobile.js b/web/_static/jsuites/jsuites.mobile.js new file mode 100644 index 0000000..e152da1 --- /dev/null +++ b/web/_static/jsuites/jsuites.mobile.js @@ -0,0 +1,1016 @@ +jSuites.app = (function(el, options) { + var obj = {}; + obj.options = {}; + + // Default configuration + var defaults = { + path: 'views', + onbeforechangepage: null, + onchangepage: null, + onbeforecreatepage: null, + oncreatepage: null, + onerrorpage: null, + onloadpage: null, + toolbar: null, + route: null, + ident: null, + detachHiddenPages: false + } + + // Loop through our object + for (var property in defaults) { + if (options && options.hasOwnProperty(property)) { + obj.options[property] = options[property]; + } else { + obj.options[property] = defaults[property]; + } + } + + // App + el.classList.add('japp'); + + // Toolbar + var toolbar = document.createElement('div'); + + obj.setToolbar = function(o) { + if (o) { + obj.options.toolbar = o; + } + // Force application + obj.options.toolbar.app = obj; + // Set toolbar + obj.toolbar = jSuites.toolbar(toolbar, obj.options.toolbar); + // Add to the DOM + el.appendChild(toolbar); + } + + obj.hideToolbar = function() { + if (toolbar.style.display == '') { + toolbar.style.display = 'none'; + } + } + + obj.showToolbar = function() { + if (toolbar.style.display == 'none') { + toolbar.style.display = ''; + } + } + + /** + * Page identification + */ + var ident = function(route) { + route = route.split('?')[0]; + + if (typeof(obj.options.ident) == 'function') { + var ret = obj.options.ident(route); + if (typeof(ret) !== 'undefined') { + return ret; + } + } + + return route; + } + + /* + * Parse javascript from an element + */ + var parseScript = function(element) { + // Get javascript + var script = element.getElementsByTagName('script'); + // Run possible inline scripts + for (var i = 0; i < script.length; i++) { + // Get type + var type = script[i].getAttribute('type'); + if (! type || type == 'text/javascript' || type == 'text/loader') { + eval(script[i].text); + } + } + } + + /** + * Pages + */ + obj.pages = function() { + /** + * Create or access a page + */ + var component = function(route, o, callback, element) { + var options = {}; + + if (o) { + if (typeof(o) == 'object') { + var options = o; + } else { + if (! callback && typeof(o) == 'function') { + callback = o; + } + } + } + + if (typeof(obj.options.route) == 'function') { + route = obj.options.route(route, options); + } + + if (route === false) { + console.error('JSUITES: Permission denied'); + } else { + // Query string does not make part in the route + options.ident = ident(route); + // Current Route + options.route = route; + + // If exists just open + var page = component.container[options.ident]; + if (page) { + page.options.closed = 0; + component.show(page, options, callback); + } else { + // Closed + options.closed = options.closed ? 1 : 0; + + // New page url + if (! options.url) { + options.url = obj.options.path + options.ident + '.html'; + } + + // Create new page + page = component.create(options, callback, element); + + // Container + component.container[options.ident] = page; + } + } + } + + /** + * Create a new page + */ + component.create = function(o, callback, fromElement) { + // Create page + if (fromElement) { + var page = fromElement; + } else { + var page = document.createElement('div'); + } + + page.classList.add('page'); + + // Keep options + page.options = o ? o : {}; + + // Create page overwrite + var ret = null; + if (typeof(obj.options.onbeforecreatepage) == 'function') { + ret = obj.options.onbeforecreatepage(obj, page); + if (ret === false) { + return false; + } + } + + var success = function(result) { + // Remove to avoid id conflicts + if (component.current && obj.options.detachHiddenPages == true) { + while (component.element.children[0]) { + component.element.children[0].parentNode.removeChild(component.element.children[0]); + } + } + + if (! component.current) { + component.element.appendChild(page); + } else { + component.element.insertBefore(page, component.current.nextSibling); + } + + // Open page + if (result) { + page.innerHTML = result; + } + + // Get javascript + try { + parseScript(page); + } catch (e) { + console.log(e); + } + + // Create page overwrite + if (typeof(obj.options.oncreatepage) == 'function') { + obj.options.oncreatepage(obj, page, result); + } + + // Push to refresh controls + if (typeof(page.options.onpush) == 'function') { + jSuites.refresh(page, page.options.onpush); + } + + // Navbar + if (page.querySelector('.navbar')) { + page.classList.add('with-navbar'); + } + + // Global onload callback + if (typeof(obj.options.onloadpage) == 'function') { + obj.options.onloadpage(obj, page); + } + + // Specific online callback + if (typeof(o.onload) == 'function') { + o.onload(page); + } + + // Always hidden when created + page.style.display = 'none'; + + // Show page + if (! page.options.closed) { + component.show(page, o, callback); + } + } + + // URL + if (o.url.indexOf('?') == -1) { + var url = o.url + '?'; + } else { + var url = o.url + '&'; + } + + if (fromElement) { + success(); + } else { + jSuites.ajax({ + url: url + 'ts=' + new Date().getTime(), + method: 'GET', + dataType: 'html', + queue: true, + success: success, + error: function(a,b) { + if (typeof(obj.options.onerrorpage) == 'function') { + obj.options.onerrorpage(obj, page, a, b); + } + + component.destroy(page); + } + }); + } + + return page; + } + + component.show = function(page, o, callback) { + if (o) { + if (o.onenter) { + page.options.onenter = o.onenter; + } + if (o.onleave) { + page.options.onleave = o.onleave; + } + if (o.route) { + page.options.route = o.route; + } + } + + // Add history + if (! o || ! o.ignoreHistory) { + // Route + if (o && o.route) { + var route = o.route; + } else { + var route = page.options.route; + } + // Add history + window.history.pushState({ route: route }, page.options.title, route); + } + + var pageIsReady = function() { + if (component.current) { + component.current.style.display = 'none'; + + if (component.current && obj.options.detachHiddenPages == true) { + if (component.current.parentNode) { + component.current.parentNode.removeChild(component.current); + } + } + } + + // New page + if (typeof(obj.options.onchangepage) == 'function') { + obj.options.onchangepage(obj, page, component.current, o); + } + + // Enter event + if (typeof(page.options.onenter) == 'function') { + page.options.onenter(obj, page, component.current); + } + + // Callback + if (typeof(callback) == 'function') { + callback(obj, page); + } + + // Current page + component.current = page; + } + + // Append page in case was detached + if (! page.parentNode) { + component.element.appendChild(page); + } + + if (component.current) { + // Show page + page.style.display = ''; + + if (component.current != page) { + var a = Array.prototype.indexOf.call(component.element.children, component.current); + var b = Array.prototype.indexOf.call(component.element.children, page); + + // Before leave the page + if (typeof(obj.options.onbeforechangepage) == 'function') { + var ret = obj.options.onbeforechangepage(obj, component.current, page, o); + if (ret === false) { + return false; + } + } + + // Leave event + if (typeof(page.options.onleave) == 'function') { + page.options.onleave(obj, component.current); + } + + // Animation only on mobile + var rect = component.element.getBoundingClientRect(); + + // Move to the top + window.scrollTo({ top: 0 }); + + // Page is ready + if (rect.width < 800 && obj.options.detachHiddenPages == false) { + jSuites.animation.slideLeft(component.element, (a < b ? 0 : 1), function() { + if (component.current != page) { + pageIsReady(); + } + }); + } else { + if (component.current != page) { + pageIsReady(); + } + } + } else { + component.current = null; + + pageIsReady(); + } + } else { + // Show + page.style.display = ''; + + // Page is ready + pageIsReady(); + } + + // Select toolbar item + if (page.options.toolbarItem) { + obj.toolbar.selectItem(page.options.toolbarItem); + } + } + + /** + * Get a page by route + */ + component.get = function(route) { + var key = ident(route); + if (component.container[key]) { + return component.container[key]; + } + } + + /** + * Reset the page container + */ + component.reset = function() { + // Container + component.element.innerHTML = ''; + // Current + component.current = null; + } + + /** + * Reset the page container + */ + component.destroy = function(page) { + if (page) { + if (page.parentNode) { + page.remove(); + } + delete component.container[page.options.ident]; + } else { + // Reset container + component.reset(); + // Destroy references + component.container = {}; + } + } + + /** + * Page container controller + */ + component.container = {}; + + component.init = function() { + /** + * Pages DOM container + */ + var pagesContainer = el.querySelector('.pages'); + if (pagesContainer) { + component.element = pagesContainer; + } else { + component.element = document.createElement('div'); + component.element.className = 'pages'; + } + + // Prefetched content + var current = null; + if (el.innerHTML) { + // Create with the prefetched content + current = document.createElement('div'); + while (el.childNodes[0]) { + current.appendChild(el.childNodes[0]); + } + } + + // Append page container to the application + el.appendChild(component.element); + + // Go to the current page + if (current) { + component(window.location.pathname, null, null, current); + } + } + + return component; + }(); + + // Start page object + obj.pages.init(); + + /** + * Panel methods + */ + obj.panel = function() { + var panel = null; + + var component = function(route, o) { + if (! panel) { + // Create element + panel = document.createElement('div'); + panel.classList.add('panel'); + panel.classList.add('panel-left'); + panel.style.display = 'none'; + + // Bind to the app + el.appendChild(panel); + } + + // Remote content + if (route) { + // URL + if (! o) { + o = {}; + } + if (! o.url) { + o.url = obj.options.path + route + '.html'; + } + // Route + o.route = route; + // Panel + panel.options = o; + + // Request remote data + jSuites.ajax({ + url: o.url, + method: 'GET', + dataType: 'html', + success: function(result) { + // Create page overwrite + var ret = null; + if (typeof(obj.options.oncreatepage) == 'function') { + ret = obj.options.oncreatepage(obj, panel, result); + } + + // Ignore create page actions + if (ret !== false) { + // Open page + panel.innerHTML = result; + // Get javascript + parseScript(page); + } + } + }); + } else { + component.show(); + } + } + + component.show = function() { + // Show panel + if (panel && panel.style.display == 'none') { + panel.style.display = ''; + // Add animation + if (panel.classList.contains('panel-left')) { + jSuites.animation.slideLeft(panel, 1); + } else { + jSuites.animation.slideRight(panel, 1); + } + } + } + + component.hide = function() { + if (panel && panel.style.display == '') { + // Animation + if (panel.classList.contains('panel-left')) { + jSuites.animation.slideLeft(panel, 0, function() { + panel.style.display = 'none'; + }); + } else { + jSuites.animation.slideRight(panel, 0, function() { + panel.animation.style.display = 'none'; + }); + } + } + } + + component.get = function() { + return panel; + } + + component.destroy = function() { + el.removeChild(panel); + panel = null; + } + + return component; + }(); + + // Actionsheet + obj.actionsheet = jSuites.actionsheet(el, obj); + + var controlSwipeLeft = function(e) { + var element = jSuites.findElement(e.target, 'option'); + + if (element && element.querySelector('.option-actions')) { + element.scrollTo({ + left: 100, + behavior: 'smooth' + }); + } else { + obj.panel.hide(); + } + } + + var controlSwipeRight = function(e) { + var element = jSuites.findElement(e.target, 'option'); + if (element && element.querySelector('.option-actions')) { + element.scrollTo({ + left: 0, + behavior: 'smooth' + }); + } else { + obj.panel.show(); + } + } + + var action = null; + + var clicked = function(e) { + // Grouped options + if (e.target.classList.contains('option-title')) { + if (e.target.classList.contains('selected')) { + e.target.classList.remove('selected'); + } else { + e.target.classList.add('selected'); + } + } + + // Grouped buttons + if (e.target.parentNode && e.target.parentNode.classList && e.target.parentNode.classList.contains('jbuttons-group')) { + for (var j = 0; j < e.target.parentNode.children.length; j++) { + e.target.parentNode.children[j].classList.remove('selected'); + } + e.target.classList.add('selected'); + } + + // App links + var tmp = jSuites.findElement(e.target, function(el) { + return el.tagName == 'A' && el.getAttribute('href') ? el : false; + }); + + if (tmp) { + var h = tmp.getAttribute('href'); + if (h.substr(0,2) == '//' || h.substr(0,4) == 'http' || tmp.classList.contains('link') || h.indexOf('#') >= 0) { + action = null; + } else { + var p = jSuites.getPosition(e); + action = { + h: h, + element: tmp, + target: e.target, + x: p[0], + y: p[1], + }; + + // Cancel click operation in 400ms + setTimeout(function() { + action = null; + }, 400); + } + } + } + + var actionDown = function(e) { + e = e || window.event; + if (e.buttons) { + var mouseButton = e.buttons; + } else if (e.button) { + var mouseButton = e.button; + } else { + var mouseButton = e.which; + } + + if (mouseButton < 2) { + clicked(e); + } + } + + var actionUp = function(e) { + obj.actionsheet.close(); + // Action + if (action) { + var p = jSuites.getPosition(e); + // If mouse move cancel the click action + if (Math.abs(action.x - p[0]) < 5 && Math.abs(action.y - p[1]) < 5) { + // Go to the page + obj.pages(action.h); + } + // Prevent default + e.preventDefault(); + action = null; + } + } + + el.addEventListener('swipeleft', controlSwipeLeft); + el.addEventListener('swiperight', controlSwipeRight); + + if ('ontouchstart' in document.documentElement === true) { + document.addEventListener('touchstart', actionDown); + document.addEventListener('touchend', actionUp); + } else { + document.addEventListener('mousedown', actionDown); + document.addEventListener('click', actionUp); + } + + window.onpopstate = function(e) { + if (e.state && e.state.route) { + if (obj.pages.get(e.state.route)) { + obj.pages(e.state.route, { ignoreHistory: true }); + } else { + window.location.href = e.state.route; + } + } else { + window.location.reload(); + } + } + + if (obj.options.toolbar) { + obj.setToolbar(); + } + + el.app = obj; + + return obj; +}); + +jSuites.actionsheet = (function(el, component) { + var obj = function(options) { + // Reset container + actionContent.innerHTML = ''; + + // Create new elements + for (var i = 0; i < options.length; i++) { + var actionGroup = document.createElement('div'); + actionGroup.className = 'jactionsheet-group'; + + for (var j = 0; j < options[i].length; j++) { + var v = options[i][j]; + var actionItem = document.createElement('div'); + var actionInput = document.createElement('input'); + actionInput.type = 'button'; + actionInput.value = v.title; + if (v.className) { + actionInput.className = v.className; + } + if (v.onclick) { + actionInput.event = v.onclick; + actionInput.onclick = function() { + this.event(component, this); + } + } + if (v.action == 'cancel') { + actionInput.style.color = 'red'; + } + actionItem.appendChild(actionInput); + actionGroup.appendChild(actionItem); + } + + actionContent.appendChild(actionGroup); + } + + // Show + actionsheet.style.display = ''; + + // Animation + jSuites.animation.slideBottom(actionContent, true); + } + + obj.close = function() { + if (actionsheet.style.display != 'none') { + // Remove any existing actionsheet + jSuites.animation.slideBottom(actionContent, false, function() { + actionsheet.style.display = 'none'; + }); + } + } + + obj.get = function() { + return actionsheet; + } + + // Init action sheet + var actionsheet = document.createElement('div'); + actionsheet.className = 'jactionsheet'; + actionsheet.style.display = 'none'; + + var actionContent = document.createElement('div'); + actionContent.className = 'jactionsheet-content'; + actionsheet.appendChild(actionContent); + + // Append actionsheet container to the application + el.appendChild(actionsheet); + + el.actionsheet = obj; + + return obj; +}); + +jSuites.dialog = (function() { + var obj = {}; + obj.options = {}; + + var dialog = null; + var dialogTitle = null; + var dialogHeader = null; + var dialogMessage = null; + var dialogFooter = null; + var dialogContainer = null; + var dialogConfirm = null; + var dialogConfirmButton = null; + var dialogCancel = null; + var dialogCancelButton = null; + + obj.open = function(options) { + if (! jSuites.dialog.hasEvents) { + obj.init(); + + jSuites.dialog.hasEvents = true; + } + obj.options = options; + + if (obj.options.title) { + dialogTitle.innerHTML = obj.options.title; + } + + if (obj.options.message) { + dialogMessage.innerHTML = obj.options.message; + } + + if (! obj.options.confirmLabel) { + obj.options.confirmLabel = 'OK'; + } + dialogConfirmButton.value = obj.options.confirmLabel; + + if (! obj.options.cancelLabel) { + obj.options.cancelLabel = 'Cancel'; + } + dialogCancelButton.value = obj.options.cancelLabel; + + if (obj.options.type == 'confirm') { + dialogCancelButton.parentNode.style.display = ''; + } else { + dialogCancelButton.parentNode.style.display = 'none'; + } + + // Append element to the app + dialog.style.opacity = 100; + + // Append to the page + if (jSuites.el) { + jSuites.el.appendChild(dialog); + } else { + document.body.appendChild(dialog); + } + + // Focus + dialog.focus(); + + // Show + setTimeout(function() { + dialogContainer.style.opacity = 100; + }, 0); + } + + obj.close = function() { + dialog.style.opacity = 0; + dialogContainer.style.opacity = 0; + setTimeout(function() { + dialog.remove(); + }, 100); + } + + obj.init = function() { + dialog = document.createElement('div'); + dialog.setAttribute('tabindex', '901'); + dialog.className = 'jdialog'; + dialog.id = 'dialog'; + + dialogHeader = document.createElement('div'); + dialogHeader.className = 'jdialog-header'; + + dialogTitle = document.createElement('div'); + dialogTitle.className = 'jdialog-title'; + dialogHeader.appendChild(dialogTitle); + + dialogMessage = document.createElement('div'); + dialogMessage.className = 'jdialog-message'; + dialogHeader.appendChild(dialogMessage); + + dialogFooter = document.createElement('div'); + dialogFooter.className = 'jdialog-footer'; + + dialogContainer = document.createElement('div'); + dialogContainer.className = 'jdialog-container'; + dialogContainer.appendChild(dialogHeader); + dialogContainer.appendChild(dialogFooter); + + // Confirm + dialogConfirm = document.createElement('div'); + dialogConfirmButton = document.createElement('input'); + dialogConfirmButton.value = obj.options.confirmLabel; + dialogConfirmButton.type = 'button'; + dialogConfirmButton.onclick = function() { + if (typeof(obj.options.onconfirm) == 'function') { + obj.options.onconfirm(); + } + obj.close(); + }; + dialogConfirm.appendChild(dialogConfirmButton); + dialogFooter.appendChild(dialogConfirm); + + // Cancel + dialogCancel = document.createElement('div'); + dialogCancelButton = document.createElement('input'); + dialogCancelButton.value = obj.options.cancelLabel; + dialogCancelButton.type = 'button'; + dialogCancelButton.onclick = function() { + if (typeof(obj.options.oncancel) == 'function') { + obj.options.oncancel(); + } + obj.close(); + } + dialogCancel.appendChild(dialogCancelButton); + dialogFooter.appendChild(dialogCancel); + + // Dialog + dialog.appendChild(dialogContainer); + + document.addEventListener('keydown', function(e) { + if (e.which == 13) { + if (typeof(obj.options.onconfirm) == 'function') { + jSuites.dialog.options.onconfirm(); + } + obj.close(); + } else if (e.which == 27) { + obj.close(); + } + }); + } + + return obj; +})(); + +jSuites.confirm = (function(message, onconfirm) { + if (jSuites.getWindowWidth() < 800) { + jSuites.dialog.open({ + type: 'confirm', + message: message, + title: 'Confirmation', + onconfirm: onconfirm, + }); + } else { + if (confirm(message)) { + onconfirm(); + } + } +}); + + +jSuites.refresh = (function(el, options) { + // Controls + var touchPosition = null; + var pushToRefresh = null; + + // Page touch move + var pageTouchMove = function(e, page) { + if (typeof(page.options.onpush) == 'function') { + if (page.scrollTop < 5) { + if (! touchPosition) { + touchPosition = {}; + touchPosition.x = e.touches[0].clientX; + touchPosition.y = e.touches[0].clientY; + } + + var height = e.touches[0].clientY - touchPosition.y; + if (height > 70) { + if (! pushToRefresh.classList.contains('ready')) { + pushToRefresh.classList.add('ready'); + } + } else { + if (pushToRefresh.classList.contains('ready')) { + pushToRefresh.classList.remove('ready'); + } + pushToRefresh.style.height = height + 'px'; + + if (height > 20) { + if (! pushToRefresh.classList.contains('holding')) { + pushToRefresh.classList.add('holding'); + page.insertBefore(pushToRefresh, page.firstChild); + } + } + } + } + } + } + + // Page touch end + var pageTouchEnd = function(e, page) { + if (typeof(page.options.onpush) == 'function') { + // Remove holding + pushToRefresh.classList.remove('holding'); + // Refresh or not refresh + if (! pushToRefresh.classList.contains('ready')) { + // Reset and not refresh + pushToRefresh.style.height = ''; + obj.hide(); + } else { + pushToRefresh.classList.remove('ready'); + // Loading indication + setTimeout(function() { + obj.hide(); + }, 1000); + + // Refresh + if (typeof(page.options.onpush) == 'function') { + page.options.onpush(page); + } + } + } + } + + var obj = function(element, callback) { + if (! pushToRefresh) { + pushToRefresh = document.createElement('div'); + pushToRefresh.className = 'jrefresh'; + } + + element.addEventListener('touchmove', function(e) { + pageTouchMove(e, element); + }); + element.addEventListener('touchend', function(e) { + pageTouchEnd(e, element); + }); + if (! element.options) { + element.options = {}; + } + if (typeof(callback) == 'function') { + element.options.onpush = callback; + } + } + + obj.hide = function() { + if (pushToRefresh.parentNode) { + pushToRefresh.parentNode.removeChild(pushToRefresh); + } + touchPosition = null; + } + + return obj; +})(); -- cgit v1.2.3