From e7de04494b70ca62ac1abda7f9a3ef41994f4f51 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 14 Oct 2021 16:59:23 +0200 Subject: Adding web-tools (WIP). Signed-off-by: Daniel Baumann --- web-tools/web/_static/jsuites/jsuites.mobile.js | 793 ++++++++++++++++++++++++ 1 file changed, 793 insertions(+) create mode 100644 web-tools/web/_static/jsuites/jsuites.mobile.js (limited to 'web-tools/web/_static/jsuites/jsuites.mobile.js') diff --git a/web-tools/web/_static/jsuites/jsuites.mobile.js b/web-tools/web/_static/jsuites/jsuites.mobile.js new file mode 100644 index 0000000..a6beee8 --- /dev/null +++ b/web-tools/web/_static/jsuites/jsuites.mobile.js @@ -0,0 +1,793 @@ +jSuites.app = (function(el, options) { + var obj = {}; + obj.options = {}; + + // Default configuration + var defaults = { + path: 'views', + onbeforechangepage: null, + onchangepage: null, + oncreatepage: null, + onloadpage: null, + toolbar: null, + } + + // 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'); + + obj.setToolbar = function(toolbar) { + if (toolbar) { + obj.options.toolbar = toolbar; + } + var toolbar = document.createElement('div'); + obj.toolbar = jSuites.toolbar(toolbar, { + app: obj, + items: obj.options.toolbar, + }); + el.appendChild(toolbar); + } + + /** + * Pages + */ + obj.pages = function() { + /** + * Create or access a page + */ + var component = function(route, o, callback) { + // Page options + if (o && typeof(o) == 'object') { + var options = o; + } else { + var options = {}; + if (! callback && typeof(o) == 'function') { + callback = o; + } + } + + // If exists just open + if (component.container[route]) { + component.show(component.container[route], options, callback); + } else { + // Create a new page + if (! route) { + console.error('JSUITES: Error, no route provided'); + } else { + // Closed + options.closed = options.closed ? 1 : 0; + // Keep Route + options.route = route; + + // New page url + if (! options.url) { + options.url = obj.options.path + route + '.html'; + } + + // Create new page + component.create(options, callback); + } + } + } + + /** + * Create a new page + */ + component.create = function(o, callback) { + // Create page + var page = document.createElement('div'); + page.classList.add('page'); + + // Container + component.container[o.route] = page; + + // Always hidden + page.style.display = 'none'; + + // Keep options + page.options = o ? o : {}; + + if (! component.current) { + pages.appendChild(page); + } else { + pages.insertBefore(page, component.current.nextSibling); + } + + 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, page, result); + } + + // Push to refresh controls + if (typeof(page.options.onpush) == 'function') { + jSuites.refresh(page, page.options.onpush); + } + + // Ignore create page actions + if (ret !== false) { + // Open page + page.innerHTML = result; + // Get javascript + parseScript(page); + } + + // Global onload callback + if (typeof(obj.options.onloadpage) == 'function') { + obj.options.onloadpage(page); + } + + // Specific online callback + if (typeof(o.onload) == 'function') { + o.onload(page); + } + + // Show page + if (! page.options.closed) { + component.show(page, o, callback); + } + } + }); + + return page; + } + + component.show = function(page, o, callback) { + var pageIsReady = function() { + // New page + if (typeof(obj.options.onchangepage) == 'function') { + obj.options.onchangepage(obj, component.current, page, 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; + } + + if (component.current) { + if (component.current != page) { + // Keep scroll in the top + window.scrollTo({ top: 0 }); + + // Show page + page.style.display = ''; + + var a = Array.prototype.indexOf.call(pages.children, component.current); + var b = Array.prototype.indexOf.call(pages.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); + } + + jSuites.animation.slideLeft(pages, (a < b ? 0 : 1), function() { + if (component.current != page) { + component.current.style.display = 'none'; + + // Page is ready + pageIsReady(); + } + }); + } + } else { + // Show + page.style.display = ''; + + // Page is ready + pageIsReady(); + } + + // Select toolbar item + if (page.options.toolbarItem) { + obj.toolbar.selectItem(page.options.toolbarItem); + } + + // Add history + if (! o || ! o.ignoreHistory) { + // Add history + window.history.pushState({ route: page.options.route }, page.options.title, page.options.route); + } + } + + /** + * Get a page by route + */ + component.get = function(route) { + if (component.container[route]) { + return component.container[route]; + } + } + + /** + * Destroy a page + */ + component.destroy = function() { + // TODO: create a destroy method + } + + /** + * Page container controller + */ + component.container = {}; + + /** + * Pages DOM container + */ + var pages = el.querySelector('.pages'); + if (! pages) { + pages = document.createElement('div'); + pages.className = 'pages'; + // Append page container to the application + el.appendChild(pages); + } + + return component; + }(); + + /** + * 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; + }(); + + obj.actionsheet = function() { + // Actionsheet container + var actionsheet = el.querySelector('.actionsheet'); + if (! actionsheet) { + 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); + } + + var component = function(options) { + if (options) { + obj.actionsheet.options = options; + } + + // Reset container + actionContent.innerHTML = ''; + + // Create new elements + for (var i = 0; i < obj.actionsheet.options.length; i++) { + var actionGroup = document.createElement('div'); + actionGroup.className = 'jactionsheet-group'; + + for (var j = 0; j < obj.actionsheet.options[i].length; j++) { + var v = obj.actionsheet.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(obj, 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); + } + + component.close = function() { + if (actionsheet.style.display != 'none') { + // Remove any existing actionsheet + jSuites.animation.slideBottom(actionContent, false, function() { + actionsheet.style.display = 'none'; + }); + } + } + + component.get = function() { + return actionsheet; + } + + return component; + }(); + + /* + * 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') { + eval(script[i].innerHTML); + } + } + } + + 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 actionElement = null; + + var actionDown = 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'); + } + } + + // App links + actionElement = jSuites.findElement(e.target, function(e) { + return e.tagName == 'A' && e.getAttribute('href') ? e : false; + }); + + if (actionElement) { + var link = actionElement.getAttribute('href'); + if (link == '#back') { + window.history.back(); + } else if (link == '#panel') { + obj.panel(); + } else { + if (actionElement.classList.contains('link')) { + actionElement = null; + } else { + obj.pages(link); + } + } + } + } + + var actionUp = function(e) { + obj.actionsheet.close(); + + if (actionElement) { + e.preventDefault(); + actionElement = 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('mouseup', 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 }); + } + } + } + + if (obj.options.toolbar) { + obj.setToolbar(); + } + + el.app = 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