{
"version": 3,
"sources": ["../../../../wh/whtree/modules/system/js/dompack/src/promise.es", "../../../../wh/whtree/modules/system/js/dompack/extra/storage.es", "../../../../wh/whtree/modules/system/js/dompack/extra/cookie.es", "../../../../wh/whtree/modules/system/js/dompack/src/debug.es", "../../../../wh/whtree/modules/system/js/dompack/src/events.es", "../../../../wh/whtree/modules/system/js/dompack/src/busy.es", "../../../../wh/whtree/modules/system/js/dompack/src/tree.es", "../../../../wh/whtree/modules/system/js/dompack/src/create.es", "../../../../wh/whtree/modules/system/js/dompack/src/components.es", "../../../../wh/whtree/modules/system/js/dompack/index.es", "../../../../wh/whtree/modules/system/js/wh/integration.es", "../../../../wh/whtree/modules/system/js/dompack/extra/keyboard.es", "../../../../wh/whtree/node_modules/events/events.js", "../../../../wh/whtree/modules/system/js/net/requester.es", "../../../../wh/whtree/modules/system/js/net/jsonrpc.es", "../../../../wh/whtree/modules/wrd/js/auth.es", "../../../../wh/whtree/modules/system/js/dompack/browserfix/focus.es", "../../../../wh/whtree/modules/system/js/dompack/types/text.es", "../../../../wh/whtree/modules/tollium/js/gettid.es", "../../../../wh/whtree/modules/tollium/web/ui/common.lang.json", "../../../../wh/whtree/modules/tollium/web/ui/js/support.es", "../../../../wh/whtree/modules/tollium/web/ui/js/componentbase.es", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/components/base/compbase.es", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/components/action/actionforwardbase.es", "../../../../wh/whtree/modules/system/js/dompack/extra/browser.es", "../../../../wh/whtree/modules/publisher/js/feedback/screenshot.es", "../../../../wh/whtree/modules/publisher/js/feedback/dompointer.es", "../../../../wh/whtree/modules/system/js/wh/rpc.es", "../../../../wh/whtree/modules/publisher/js/feedback/internal/feedback.rpc.json", "../../../../wh/whtree/modules/publisher/js/feedback/index.es", "../../../../wh/whtree/modules/tollium/js/internal/todd.rpc.json", "../../../../wh/whtree/modules/tollium/js/icons.es", "../../../../wh/whtree/modules/tollium/web/ui/js/dialogs/simplescreen.es", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/js/feedback.es", "../../../../wh/whtree/modules/system/js/compat/download.es", "../../../../wh/whtree/modules/tollium/web/ui/js/dialogs/uploadcontroller.es", "../../../../wh/whtree/modules/tollium/web/ui/components/imageeditor/imageeditor.lang.json", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/node_modules/exif-parser/lib/jpeg.js", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/node_modules/exif-parser/lib/exif.js", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/node_modules/exif-parser/lib/date.js", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/node_modules/exif-parser/lib/simplify.js", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/node_modules/exif-parser/lib/exif-tags.js", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/node_modules/exif-parser/lib/parser.js", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/node_modules/exif-parser/lib/dom-bufferstream.js", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/node_modules/exif-parser/lib/bufferstream.js", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/node_modules/exif-parser/index.js", "../../../../wh/whtree/modules/tollium/web/ui/components/toolbar/toolbars.es", "../../../../wh/whtree/modules/tollium/web/ui/components/imageeditor/surface.es", "../../../../wh/whtree/modules/system/js/dompack/browserfix/movable.es", "../../../../wh/whtree/modules/tollium/web/ui/components/imageeditor/surfacetool.es", "../../../../wh/whtree/modules/tollium/web/ui/components/imageeditor/smartcrop.js", "../../../../wh/whtree/modules/tollium/web/ui/components/imageeditor/crop.es", "../../../../wh/whtree/modules/tollium/web/ui/components/imageeditor/scaling.es", "../../../../wh/whtree/modules/tollium/web/ui/components/imageeditor/refpoint.es", "../../../../wh/whtree/modules/tollium/web/ui/components/imageeditor/filters.es", "../../../../wh/whtree/modules/tollium/web/ui/components/imageeditor/index.es", "../../../../wh/whtree/modules/tollium/web/ui/js/dialogs/imgeditcontroller.es", "../../../../wh/whtree/modules/system/js/compat/upload.es", "../../../../wh/whtree/modules/tollium/web/ui/js/upload.es", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/components/action/action.es", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/components/base/actionable.es", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/components/button/button.es", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/components/base/tools.es", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/components/buttongroup/buttongroup.es", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/components/checkbox/checkbox.es", "../../../../wh/whtree/modules/tollium/js/internal/scrollmonitor.es", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/components/codeedit/codeedit.es", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/components/customhtml/customhtml.es", "../../../../wh/whtree/modules/publisher/js/forms/internal/datehelpers.es", "../../../../wh/whtree/modules/publisher/js/forms/internal/datepicker.es", "../../../../wh/whtree/modules/publisher/js/forms/fields/datetime.es", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/components/datetime/datetime.lang.json", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/components/datetime/datetime.es", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/components/frame/dirtylistener.es", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/components/action/forward.es", "../../../../wh/whtree/modules/tollium/js/internal/keyboard.es", "../../../../wh/whtree/modules/tollium/web/ui/js/dragdrop.es", "../../../../wh/whtree/modules/tollium/web/ui/components/focuszones.es", "../../../../wh/whtree/modules/tollium/web/ui/components/basecontrols/menu.es", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/components/frame/frame.es", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/components/hr/hr.es", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/components/iframe/iframe.es", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/node_modules/@webhare/dompack-overlays/src/index.es", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/components/image/image.es", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/components/text/text.es", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/components/panel/panel.es", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/components/inlineblock/inlineblock.es", "../../../../wh/whtree/modules/system/js/dompack/types/email.es", "../../../../wh/whtree/modules/system/js/util/emailvalidation.es", "../../../../wh/whtree/modules/tollium/web/ui/components/listview/listcolumns.es", "../../../../wh/whtree/modules/system/js/internal/findasyoutype.es", "../../../../wh/whtree/modules/tollium/web/ui/components/listview/listview.es", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/components/list/list.es", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/components/menuitem/menuitem.es", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/components/progress/progress.es", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/components/frame/proxy.es", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/components/base/html.es", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/components/pulldown/pulldown.es", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/components/radiobutton/radiobutton.es", "../../../../wh/whtree/modules/system/js/internal/utf8.es", "../../../../wh/whtree/modules/tollium/web/ui/components/basecontrols/counter.es", "../../../../wh/whtree/modules/tollium/web/ui/components/richeditor/internal/support.es", "../../../../wh/whtree/modules/tollium/web/ui/components/richeditor/internal/dom/range.es", "../../../../wh/whtree/modules/system/js/frameworks/rangy/rangy13.js", "../../../../wh/whtree/modules/tollium/web/ui/components/richeditor/internal/domlevel.es", "../../../../wh/whtree/modules/tollium/web/ui/components/richeditor/internal/richdebug.es", "../../../../wh/whtree/modules/publisher/js/forms/internal/form.rpc.json", "../../../../wh/whtree/modules/tollium/web/ui/components/richeditor/internal/structured/lists.es", "../../../../wh/whtree/modules/tollium/web/ui/components/richeditor/internal/parsedstructure.es", "../../../../wh/whtree/modules/system/js/dompack/extra/preload.es", "../../../../wh/whtree/modules/tollium/web/ui/components/richeditor/internal/selection.es", "../../../../wh/whtree/modules/tollium/web/ui/components/richeditor/internal/tableeditor.es", "../../../../wh/whtree/modules/tollium/web/ui/components/richeditor/internal/editorbase.es", "../../../../wh/whtree/modules/tollium/web/ui/components/richeditor/internal/pastecleanup.es", "../../../../wh/whtree/modules/tollium/web/ui/components/richeditor/internal/structurededitor.es", "../../../../wh/whtree/modules/tollium/web/ui/components/richeditor/internal/free-editor.es", "../../../../wh/whtree/modules/tollium/web/ui/components/richeditor/internal/toolbar.es", "../../../../wh/whtree/modules/tollium/web/ui/components/richeditor/richeditor.lang.json", "../../../../wh/whtree/modules/system/js/internal/converthtmltoplaintext.es", "../../../../wh/whtree/modules/tollium/web/ui/components/richeditor/editor.es", "../../../../wh/whtree/modules/tollium/web/ui/components/richeditor/styling.es", "../../../../wh/whtree/modules/tollium/web/ui/components/richeditor/index.es", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/components/rte/rte.es", "../../../../wh/whtree/modules/tollium/web/ui/components/basecontrols/slider.es", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/components/slider/slider.es", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/components/spacer/spacer.es", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/components/split/split.es", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/components/table/table.es", "../../../../wh/whtree/modules/system/js/dompack/browserfix/scroll.es", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/components/tabs/tabs.es", "../../../../wh/whtree/modules/system/js/dompack/src/index.es", "../../../../wh/whtree/modules/system/js/dompack/components/internal/selectlist.es", "../../../../wh/whtree/modules/system/js/dompack/components/autosuggest/index.es", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/components/textedit/textedit.es", "../../../../wh/whtree/modules/tollium/web/ui/components/tagedit/tagedit.es", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/components/tagedit/tagedit.es", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/components/textarea/textarea.es", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/components/toolbar/toolbar.es", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/components/index.es", "../../../../wh/whtree/modules/tollium/web/ui/js/comm/linkendpoint.es", "../../../../wh/whtree/node_modules/stackframe/stackframe.js", "../../../../wh/whtree/node_modules/error-stack-parser/error-stack-parser.js", "../../../../wh/whtree/node_modules/stack-generator/stack-generator.js", "../../../../wh/whtree/node_modules/stacktrace-gps/node_modules/source-map/lib/util.js", "../../../../wh/whtree/node_modules/stacktrace-gps/node_modules/source-map/lib/binary-search.js", "../../../../wh/whtree/node_modules/stacktrace-gps/node_modules/source-map/lib/array-set.js", "../../../../wh/whtree/node_modules/stacktrace-gps/node_modules/source-map/lib/base64.js", "../../../../wh/whtree/node_modules/stacktrace-gps/node_modules/source-map/lib/base64-vlq.js", "../../../../wh/whtree/node_modules/stacktrace-gps/node_modules/source-map/lib/quick-sort.js", "../../../../wh/whtree/node_modules/stacktrace-gps/node_modules/source-map/lib/source-map-consumer.js", "../../../../wh/whtree/node_modules/stacktrace-gps/stacktrace-gps.js", "../../../../wh/whtree/node_modules/stacktrace-js/stacktrace.js", "../../../../wh/whtree/modules/system/js/wh/errorreporting.es", "../../../../wh/whtree/modules/tollium/web/ui/js/comm/transportbase.es", "../../../../wh/whtree/modules/tollium/web/ui/js/comm/toddservice.rpc.json", "../../../../wh/whtree/modules/tollium/web/ui/js/comm/jsonrpctransport.es", "../../../../wh/whtree/modules/tollium/web/ui/js/comm/websocket.es", "../../../../wh/whtree/modules/tollium/web/ui/js/comm/transportmanager.es", "../../../../wh/whtree/modules/system/js/wh/connect.es", "../../../../wh/whtree/modules/tollium/web/ui/js/debugging/magicmenu.es", "../../../../wh/whtree/modules/tollium/web/ui/js/shell/whcheck.es", "../../../../wh/whtree/modules/tollium/web/ui/js/shell/mousehandling.es", "../../../../wh/whtree/modules/tollium/web/ui/js/application/docpanel.es", "../../../../wh/whtree/modules/tollium/web/ui/js/application.es", "../../../../wh/whtree/modules/tollium/web/ui/js/components/jsx.es", "../../../../wh/whtree/modules/tollium/web/ui/js/shell/applicationbar.es", "../../../../wh/whtree/modules/tollium/web/ui/js/apps/dashboard.es", "../../../../wh/whtree/modules/tollium/web/ui/js/apps/login.es", "../../../../wh/whtree/modules/tollium/web/ui/js/apps/oauth.es", "../../../../wh/whtree/modules/tollium/web/ui/js/loginrequests.es", "../../../../wh/whtree/modules/tollium/web/ui/js/shell/towl.es", "../../../../wh/whtree/modules/system/js/net/eventserver.es", "../../../../wh/whtree/modules/tollium/web/ui/js/shell.es", "../../../../wh/whtree/modules/publisher/js/internal/polyfills/modern.es", "../../../../wh/whtree/modules/tollium/webdesigns/webinterface/webinterface.es", "../../../../../:entrypoint.js?%5B%22%2Fopt%2Fwh%2Fwhtree%2Fmodules%2Fpublisher%2Fjs%2Finternal%2Fpolyfills%2Fmodern.es%22%2C%22%2Fopt%2Fwh%2Fwhtree%2Fmodules%2Ftollium%2Fwebdesigns%2Fwebinterface%2Fwebinterface%22%5D"],
"sourceRoot": "@mod-humpty/dumpty/had/a/great/fall/humpty/dumpty/fell/of/the/wall.js",
"sourcesContent": ["// Workaround babel error that 'Promise.defer = ' isn't converted to the Babel promise type\nlet PromiseType = Promise;\nif(!PromiseType.prototype.finally)\n{\n /** Finally function for promises (executes callback without parameters, waits on returned thenables, then fulfills with\n original result\n */\n PromiseType.prototype.finally = function(callback)\n {\n // From https://github.com/domenic/promises-unwrapping/issues/18\n var constructor = this.constructor;\n return this.then(\n function(value) { return constructor.resolve(callback()).then(function() { return value; }); },\n function(reason) { return constructor.resolve(callback()).then(function() { throw reason; }); });\n };\n}\n\n/** Create a promise together with resolve & reject functions\n @return\n @cell return.promise\n @cell return.resolve\n @cell return.reject\n*/\nexport function createDeferred()\n{\n var deferred =\n { promise: null\n , resolve: null\n , reject: null\n };\n\n deferred.promise = new Promise(function(resolve, reject) { deferred.resolve = resolve; deferred.reject = reject; });\n return deferred;\n}\n", "/* @recommendedimport: import * as storage from 'dompack/extra/storage';\n*/\n\nlet backupsession = {}, backuplocal = {};\nlet sessionfail, localfail;\n\n//isolate us when running previews, CI tests use same Chrome for both preview and tests so the previews start increasing visitorcounts behind our back\nconst isolated = \"whIsolateStorage\" in document.documentElement.dataset;\n\n/** @return True if our storage is fully isolated */\nexport function isIsolated()\n{\n return isolated;\n}\n\nexport function setSession(key, value)\n{\n try\n {\n if(value !== null)\n {\n backupsession[key] = value;\n if(!isolated)\n window.sessionStorage.setItem(key, JSON.stringify(value));\n }\n else\n {\n delete backupsession[key];\n if(!isolated)\n window.sessionStorage.removeItem(key);\n }\n\n if(sessionfail)\n {\n console.log(\"storage.setSession succeed after earlier fail\");\n sessionfail = false;\n }\n }\n catch(e)\n {\n if(!sessionfail)\n {\n console.log(\"storage.setSession failed\", e);\n sessionfail = true;\n }\n }\n}\n\nexport function getSession(key)\n{\n if(!isolated)\n {\n try\n {\n let retval = window.sessionStorage[key];\n try\n {\n return retval ? JSON.parse(retval) : null;\n }\n catch(e)\n {\n console.log(\"Failed to parse sessionStorage\",e,key);\n return null;\n }\n }\n catch(e)\n {\n if(!sessionfail)\n {\n console.log(\"getSessionStorage failed\", e);\n sessionfail = true;\n }\n }\n }\n return key in backupsession ? backupsession[key] : null;\n}\n\nexport function setLocal(key, value)\n{\n try\n {\n if(value !== null)\n {\n backuplocal[key] = value;\n if(!isolated)\n window.localStorage.setItem(key, JSON.stringify(value));\n }\n else\n {\n delete backuplocal[key];\n if(!isolated)\n window.localStorage.removeItem(key);\n }\n\n if(localfail)\n {\n console.log(\"storage.setLocal succeed after earlier fail\");\n localfail = false;\n }\n }\n catch(e)\n {\n if(!localfail)\n {\n console.log(\"storage.setLocal failed\", e);\n localfail = true;\n }\n }\n}\n\nexport function getLocal(key)\n{\n if(!isolated)\n {\n try\n {\n let retval = window.localStorage[key];\n try\n {\n return retval ? JSON.parse(retval) : null;\n }\n catch(e)\n {\n console.log(\"Failed to parse localStorage\",e,key);\n return null;\n }\n }\n catch(e)\n {\n if(!localfail)\n {\n console.log(\"getLocalStorage failed\", e);\n localfail = true;\n }\n }\n }\n return key in backuplocal ? backuplocal[key] : null;\n}\n", "/** This is currently more or less based on the mootools Cookie library */\n/* eslint no-useless-escape: off */\n\nimport { isIsolated } from './storage.es';\nlet isolatedcookies = {};\n\nfunction escapeRegExp(xx)\n{\n return xx.replace(/([-.*+?^${}()|[\\]\\/\\\\])/g, '\\\\$1');\n}\n\n//based on mootools cookie\nclass Cookie\n{\n constructor(key,options)\n {\n if(!options)\n options={};\n\n this.key = key;\n this.options = { path: 'path' in options ? options.path : '/'\n , domain: 'domain' in options ? options.domain : false\n , duration: 'duration' in options ? options.duration : false\n , secure: 'secure' in options ? options.secure : false\n , encode: 'encode' in options ? options.encode : true\n , httponly: 'httpOnly' in options ? options.httpOnly : 'httponly' in options ? options.httponly : false\n , samesite: 'samesite' in options ? options.samesite : ''\n };\n }\n write(value)\n {\n if(isIsolated())\n {\n isolatedcookies[\"c.\" + this.key] = String(value);\n return;\n }\n\n if (this.options.encode)\n value = encodeURIComponent(value);\n if (this.options.domain)\n value += '; domain=' + this.options.domain;\n if (this.options.path)\n value += '; path=' + this.options.path;\n if (this.options.duration)\n {\n var date = new Date();\n date.setTime(date.getTime() + this.options.duration * 24 * 60 * 60 * 1000);\n value += '; expires=' + date.toGMTString();\n }\n if (this.options.secure)\n value += '; secure';\n if (this.options.httponly)\n value += '; HttpOnly';\n if (this.options.samesite)\n value += '; SameSite='+this.options.samesite;\n\n document.cookie = this.key + '=' + value;\n return this;\n }\n read()\n {\n if(isIsolated())\n return isolatedcookies[\"c.\" + this.key] || null;\n\n var value = document.cookie.match('(?:^|;)\\\\s*' + escapeRegExp(this.key) + '=([^;]*)');\n return (value) ? decodeURIComponent(value[1]) : null;\n }\n remove()\n {\n if(isIsolated())\n {\n delete isolatedcookies[\"c.\" + this.key];\n return;\n }\n new Cookie(this.key, Object.assign({}, this.options, {duration: -1})).write('');\n }\n}\n\nexport function list()\n{\n if(isIsolated())\n return Object.entries(isolatedcookies).map(entry => ({ name: entry[0].substr(2), value: entry[1] }));\n\n return document.cookie.split(';').map(cookie =>\n {\n let parts = cookie.split('=');\n return { name: decodeURIComponent(parts[0].trim()), value:decodeURIComponent(parts[1]||'') };\n });\n}\n\nexport function write(key, value, options)\n{\n return new Cookie(key, options).write(value);\n}\n\nexport function read(key)\n{\n return new Cookie(key).read();\n}\n\nexport function remove(key, options)\n{\n new Cookie(key, options).remove();\n}\n", "export let debugflags = {};\nimport * as domcookie from '../extra/cookie.es';\n\n/** Extract a specific variable from the URL\n @param varname Variable name, eg dompack-debug\n*/\nexport function parseDebugURL(varname)\n{\n //FIXME proper regex escape for varname, but fortunately this isn't user input\n let urldebugvar = window.location.href.match(new RegExp('[?]' + varname + '=([^?]*)'));\n if(urldebugvar)\n {\n let debugstr = decodeURIComponent(urldebugvar[1]).split(',');\n if(debugstr.length)\n addDebugFlags(debugstr);\n }\n}\nexport function addDebugFlags(flags)\n{\n flags.forEach(flagname =>\n {\n if(flagname.startsWith('sig='))\n return;\n\n debugflags[flagname] = true;\n document.documentElement.classList.add(\"dompack--debug-\" + flagname);\n });\n\n if(debugflags.dompack)\n console.log('[dompack] debugging flags: ' + Object.keys(debugflags).join(', '));\n}\n\nexport function initDebug()\n{\n //no-op but there are still external callers which need fixing\n}\n\n//initialize debugging support (read debugflags etc)\nparseDebugURL('wh-debug');\n\nlet debugcookie = domcookie.read(\"wh-debug\");\nif(debugcookie)\n addDebugFlags(debugcookie.split('.'));\n", "let eventconstructor = null;\n\nif(typeof window !== 'undefined')\n{\n try //IE11 does not ship with CustomEvent\n {\n new window.CustomEvent(\"test\");\n eventconstructor = window.CustomEvent;\n }\n catch(e)\n {\n eventconstructor = function(event, params)\n {\n var evt;\n params = params || {\n bubbles: false,\n cancelable: false,\n detail: undefined\n };\n\n evt = document.createEvent(\"CustomEvent\");\n evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);\n return evt;\n };\n eventconstructor.prototype = window.Event.prototype;\n }\n}\n\nexport let CustomEvent = eventconstructor;\n\nexport function dispatchDomEvent(element, eventtype, options)\n{\n //see here https://developer.mozilla.org/en-US/docs/Web/Events whether an event is bubbles/cancelabel\n options = { bubbles: [\"input\",\"change\",\"click\",\"contextmenu\",\"dblclick\",\n \"reset\",\"submit\"].includes(eventtype)\n , cancelable: [\"animationstart\",\"animationcancel\",\"animationend\",\"animationiteration\"\n ,\"beforeunload\"\n ,\"click\",\"contextmenu\",\"dblclick\"\n ,\"reset\",\"submit\"\n ,\"transitionstart\",\"transitioncancel\",\"transitionend\",\"transitionrun\"].includes(eventtype)\n , ...options\n };\n\n if(!element.ownerDocument)\n return true; //the element has left the dom... so there's no more bubbling. just drop it\n\n let createtype = [\"load\",\"scroll\"].includes(eventtype) == \"load\" ? \"UIEvents\" : [\"focus\",\"blur\",\"focusin\",\"focusout\"].includes(eventtype) ? \"FocusEvent\" : eventtype == \"click\" ? \"MouseEvents\" : \"HTMLEvents\";\n\n var evt = element.ownerDocument.createEvent(createtype);\n evt.initEvent(eventtype, options.bubbles, options.cancelable);\n if(options.detail)\n evt.detail = options.detail;\n if(options.relatedTarget) //its a readonly prop, so redefine it\n Object.defineProperty(evt, 'relatedTarget', { value:options.relatedTarget, writable: false });\n\n if(eventtype == 'click' && window.IScroll)\n evt._constructed = true; //ensure IScroll doesn't blindly cancel our synthetic clicks\n\n return element.dispatchEvent(evt);\n}\n\n//fire the proper modified events (input and/or change) on the element after changing its value - DEPRECATED, you should fire the proper input and change events according to the situation\nexport function fireModifiedEvents(element, options)\n{\n fireHTMLEvent(element, 'input', options);\n fireHTMLEvent(element, 'change', options);\n}\n\n//manually fire 'onchange' events. needed for event simulation - DEPRECATED\nexport function fireHTMLEvent(element, type, options)\n{\n return dispatchDomEvent(element, type, options);\n}\n\n/** Fire a custom event\n @param node node to fire the event on\n @param event event type\n @param params\n @cell params.bubbles\n @cell params.cancelable\n @cell params.detail\n @cell params.defaulthandler Handler to execute if the default isn't prevented by a event listener\n @return true if the default wasn't prevented\n*/\nexport function dispatchCustomEvent(node, event, params)\n{\n if(!params)\n params={};\n ['bubbles','cancelable'].forEach(prop =>\n {\n if(!(prop in params))\n throw new Error(`Missing '${prop}' in dispatchCustomEvent parameter`);\n });\n\n let evt = new CustomEvent(event, { bubbles: params.bubbles\n , cancelable: params.cancelable\n , detail: params.detail\n });\n let defaultaction = true;\n try\n {\n if(!node.dispatchEvent(evt))\n defaultaction = false; //defaultPrevented is unreliable on IE11, so double check\n }\n finally\n {\n if(!evt.defaultPrevented && params.defaulthandler && defaultaction)\n {\n params.defaulthandler(evt);\n }\n }\n return defaultaction && !evt.defaultPrevented;\n}\n\n/** Change the value of a form element, and fire the correct events as if it were a user change\n @param element Element to change\n @param newvalue New value */\nexport function changeValue(element, newvalue)\n{\n if(element instanceof Array || element instanceof NodeList)\n {\n Array.from(element).forEach(node => changeValue(node, newvalue));\n return;\n }\n\n if(element.nodeName=='INPUT' && ['radio','checkbox'].includes(element.type))\n {\n if(!!element.checked == !!newvalue)\n return;\n element.checked=!!newvalue;\n }\n else\n {\n if(element.value == newvalue)\n return;\n\n element.value = newvalue;\n }\n dispatchDomEvent(element, 'input');\n dispatchDomEvent(element, 'change');\n}\n\nlet keydata;\nfunction initKeyMapping()\n{\n keydata =\n {\n // Mapping from keyIdentifier/key to key. If not found, translate U+XXXX to the unicode char and return that.\n\n // List of all current key mappings (and current inconsistencies\n // at https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values\n mapping:\n { \"Del\": \"Delete\" // IE/edge use 'Del' instead of 'Delete'\n , \"Esc\": \"Escape\" // IE/edge use 'Esc' instead of 'Escape'\n , \"OS\": \"Meta\" // Meta key is called OS on IE 9 and Firefox (tested v50)\n , \"Win\": \"Meta\" // Meta key is called Win on IE/Edge\n , \"Scroll\": \"ScrollLock\" // More IE/Edge, from tests\n , \"PrintScreen\": \"Print\" // More IE/Edge, from tests\n , \"MozHomeScreen\": \"GoHome\" // Prior to Firefox 37, the Home button generated a key code of \"Exit\". Starting in Firefox 37, the button generates the key code \"MozHomeScreen\".\n\n // Internet Explorer 9 and Firefox 36 and earlier return \"Left\", \"Right\", \"Up\", and \"Down\" for the arrow keys, instead of \"ArrowLeft\", \"ArrowRight\", \"ArrowUp\", and \"ArrowDown\".\n , \"Left\": \"ArrowLeft\"\n , \"Right\": \"ArrowRight\"\n , \"Up\": \"ArrowUp\"\n , \"Down\": \"ArrowDown\"\n // More IE and old Firefox stuff\n , \"Crsel\": \"CrSel\"\n , \"Exsel\": \"ExSel\"\n , \"Nonconvert\": \"NonConvert\"\n // Internet Explorer 9 and Firefox 36 and earlier report \"Apps\" instead of \"ContextMenu\" for the context menu key.\n , \"Apps\": \"ContextMenu\"\n // Internet Explorer 9 and Firefox 36 and earlier use \"MediaNextTrack\" and \"MediaPreviousTrack\" instead of \"MediaTrackNext\" and \"MediaTrackPrevious\".\n , \"MediaNextTrack\": \"MediaTrackNext\"\n , \"MediaPreviousTrack\": \"MediaTrackPrevious\"\n // In Internet Explorer 9, and prior to Firefox 49, \"AudioVolumeUp\", \"AudioVolumeDown\", and \"AudioVolumeMute\" were \"VolumeUp\", \"VolumeDown\", and \"VolumeMute\".\n // In Firefox 49 they were updated to match the latest specification. The old names are still used on Boot to Gecko.\n , \"VolumeUp\": \"AudioVolumeUp\"\n , \"VolumeDown\": \"AudioVolumeDown\"\n , \"VolumeMute\": \"AudioVolumeMute\"\n // Firefox added proper support for the \"TV\" key in Firefox 37; before that, this key generated the key code \"Live\".\n , \"Live\": \"TV\"\n // Internet Explorer 9 and Firefox 36 and earlier identify the zoom toggle button as \"Zoom\". Firefox 37 corrects this to \"ZoomToggle\".\n , \"Zoom\": \"ZoomToggle\"\n // Internet Explorer 9 and Firefox 36 and earlier use \"SelectMedia\" instead of \"LaunchMediaPlayer\". Firefox 37 through Firefox 48 use \"MediaSelect\". Firefox 49 has been updated to match the latest specification, and to return \"LaunchMediaPlayer\".\n , \"SelectMedia\": \"LaunchMediaPlayer\"\n , \"MediaSelect\": \"LaunchMediaPlayer\"\n // Google Chrome returns \"LaunchCalculator\" instead of \"LaunchApplication1\". See Chromium bug 612743 for more information.\n // Google Chrome returns \"LaunchMyComputer\" instead of \"LaunchApplication2\". See Chromium bug 612743 for more information.\n // (LaunchCalculator and LaunchMyComputer are valid too, so no translation)\n\n // While older browsers used words like \"Add\", \"Decimal\", \"Multiply\", and so forth modern browsers identify these using the actual character (\"+\", \".\", \"*\", and so forth).\n , \"Multiply\": \"*\"\n , \"Add\": \"+\"\n , \"Divide\": \"/\"\n , \"Subtract\": \"-\"\n , \"Decimal\": \".\" // (mozilla Key_Values doc says depends on region)\n , \"Separator\": \".\" // (mozilla Key_Values doc says depends on region)\n\n // keyIdenfier spec mapping of non-printable keys: https://www.w3.org/TR/2009/WD-DOM-Level-3-Events-20090908/#keyset-keyidentifiers\n // Used in safari\n , \"U+0008\": \"Backspace\"\n , \"U+0009\": \"Tab\"\n , \"U+000D\": \"Enter\"\n , \"U+0018\": \"Cancel\"\n , \"U+001B\": \"Escape\"\n , \"U+007F\": \"Delete\"\n , \"U+0300\": \"DeadGrave\"\n , \"U+0301\": \"DeadEacute\"\n , \"U+0302\": \"DeadCircumflex\"\n , \"U+0303\": \"DeadTilde\"\n , \"U+0304\": \"DeadMacron\"\n , \"U+0306\": \"DeadBreve\"\n , \"U+0307\": \"DeadAboveDot\"\n , \"U+0308\": \"DeadUmlaut\"\n , \"U+030A\": \"DeadAboveRing\"\n , \"U+030B\": \"DeadDoubleacute\"\n , \"U+030C\": \"DeadCaron\"\n , \"U+0327\": \"DeadCedilla\"\n , \"U+0328\": \"DeadOgonek\"\n , \"U+0345\": \"DeadIota\"\n , \"U+3099\": \"DeadVoicedSound\"\n , \"U+309A\": \"DeadSemivoicedSound\"\n\n // keyIdenfier spec mapping to characters: https://www.w3.org/TR/2009/WD-DOM-Level-3-Events-20090908/#keyset-keyidentifiers\n , \"Exclamation\": \"!\"\n , \"DoubleQuote\": \"\\\"\"\n , \"Hash\": \"#\"\n , \"Dollar\": \"$\"\n , \"Ampersand\": \"&\"\n , \"LeftParen\": \"(\"\n , \"RightParen\": \")\"\n , \"Asterisk\": \"*\"\n , \"Plus\": \"+\"\n , \"Percent\": \"%\"\n , \"Comma\": \",\"\n , \"HyphenMinus\": \"-\"\n , \"Period\": \".\"\n , \"Solidus\": \"/\"\n , \"Colon\": \":\"\n , \"Semicolon\": \";\"\n , \"LessThan\": \"<\"\n , \"Equals\": \"=\"\n , \"GreaterThan\": \">\"\n , \"QuestionMark\": \"?\"\n , \"At\": \"@\"\n , \"LeftSquareBracket\": \"[\"\n , \"Backslash\": \"\\\\\"\n , \"RightSquareBracket\": \"]\"\n , \"Circumflex\": \"^\"\n , \"Underscore\": \"_\"\n , \"Grave\": \"`\"\n , \"LeftCurlyBracket\": \"{\"\n , \"Pipe\": \"|\"\n , \"RightCurlyBracket\": \"}\"\n , \"Euro\": \"\u20AC\"\n , \"InvertedExclamation\": \"\u00A1\"\n\n // Safari fixes: viewed in local tests\n , \"U+0010\": \"ContextMenu\"\n }\n };\n}\n\n/** Returns normalized keyboard event properties, following the current W3C UI Events spec\n @param evt Keyboard event\n @return Normalized keyboard event data\n*/\nexport function normalizeKeyboardEventData(evt)\n{\n // event.key is supported from chrome:51, edge, ff: 29, ie: 9, not in safari\n // event.keyIdentifier in chrome 26-54, opera 15-41, safara: 5.1\n // safari doesn't provide either in keypress events, use U+evt.keyCode (uppercase 4-byte hex)\n\n let key = evt.key;\n if (key && key.charCodeAt(0) < 32) // IE11 under selenium gives back control chars at keypress\n key = \"\";\n\n key = key || evt.keyIdentifier || (evt.keyCode ? \"U+\" + (\"000\" + evt.keyCode.toString(16)).substr(-4).toUpperCase() : \"\");\n if (!key)\n key = \"Unidentified\";\n if (!keydata)\n initKeyMapping();\n if (keydata.mapping.hasOwnProperty(key))\n key = keydata.mapping[key];\n else if (key.startsWith(\"U+\")) // U+xxxx code\n key = String.fromCodePoint(parseInt(key.substr(2), 16));\n\n // IE11/edge numpad '.' with numlock returns 'Del' in .key in keypress event.\n if (evt.type === \"keypress\" && evt.char === \".\")\n key = \".\";\n else if (evt.key == \"\\u0000\" && evt.code == \"NumpadDecimal\") // seen in chrome 56.0.2924.76 on linux, numpad '.' without numlock returns key \"\\u0000\"\n key = \".\";\n\n return (\n { type: evt.type\n , target: evt.target\n , key: key\n , code: evt.code || \"Unidentified\"\n , ctrlKey: evt.ctrlKey\n , altKey: evt.altKey\n , location: evt.location\n , shiftKey: evt.shiftKey\n , metaKey: evt.metaKey\n , repeat: evt.repeat\n , isComposing: evt.isComposing\n });\n}\n\n/** Stop, fully, an event */\nexport function stop(event)\n{\n event.preventDefault();\n event.stopImmediatePropagation();\n}\n", "import * as domdebug from './debug.es';\nimport * as dompromise from './promise.es';\nimport * as domevents from './events.es';\n\nlet lockmgr = null;\nlet locallocks = [];\nlet ischild = false;\n\nclass LockManager\n{\n //this object is not for external consumption\n constructor()\n {\n this.locks = [];\n this.busycounter = 0;\n this.deferreduipromise = null;\n this.uiwatcher = null;\n this.modallocked = false;\n }\n anyModalLocks()\n {\n for(var lock of this.locks)\n if(lock.ismodal)\n return true;\n return false;\n }\n add(lock)\n {\n this.locks.push(lock);\n let returnvalue = this.busycounter++;\n\n if(lock.ismodal && !this.modallocked)\n {\n this.modallocked = true;\n\n if(domevents.dispatchCustomEvent(window, 'dompack:busymodal', { bubbles: true, cancelable: true, detail: { islock: true } }))\n document.documentElement.classList.add(\"dompack--busymodal\");\n }\n return returnvalue;\n }\n release(lock)\n {\n let pos = this.locks.indexOf(lock);\n if(pos==-1)\n {\n if(domdebug.debugflags.bus)\n {\n console.error(\"Duplicate release of busy lock #\" + lock.locknum);\n console.log(\"Lock allocated:\");\n console.log(lock.acquirestack);\n console.log(\"Lock first released:\");\n console.log(lock.releasestack);\n }\n throw new Error(\"Duplicate release of busy lock\");\n }\n\n this.locks.splice(pos,1);\n this.prepWatcher();\n }\n prepWatcher()\n {\n if(!this.uiwatcher && this.locks.length==0 && (this.deferreduipromise || this.modallocked))\n {\n this.uiwatcher = setTimeout(() => this.checkUIFree(),0);\n }\n }\n getNumLocks()\n {\n return this.locks.length;\n }\n checkUIFree()\n {\n this.uiwatcher = null;\n if(this.locks.length == 0)\n {\n if(this.deferreduipromise)\n {\n this.deferreduipromise.resolve(true);\n this.deferreduipromise = null;\n }\n if(this.modallocked && !this.anyModalLocks())\n {\n this.modallocked = false;\n if(domevents.dispatchCustomEvent(window, 'dompack:busymodal', { bubbles: true, cancelable: true, detail: { islock: false } }))\n document.documentElement.classList.remove(\"dompack--busymodal\");\n }\n }\n }\n waitUIFree()\n {\n if(!this.deferreduipromise)\n this.deferreduipromise = dompromise.createDeferred();\n this.prepWatcher();\n return this.deferreduipromise.promise;\n }\n logLocks()\n {\n this.locks.forEach( (lock,idx) => console.log('[bus] lock #' + lock.locknum, lock.acquirestack, lock) );\n console.log(\"[bus] total \" + this.locks.length + \" locks\");\n }\n getLockIds()\n {\n return this.locks.map(l => \"#\" + l.locknum).join(\", \");\n }\n}\n\nexport class Lock\n{\n constructor(options)\n {\n this.ismodal = options && options.ismodal;\n\n this.locknum = lockmgr.add(this);\n if(ischild)\n locallocks.push(this);\n\n if(domdebug.debugflags.bus)\n {\n this.acquirestack = (new Error).stack;\n console.trace('[bus] Busy lock #' + this.locknum + ' taken. ' + lockmgr.getNumLocks() + \" locks active now: \" + lockmgr.getLockIds());\n }\n }\n release()\n {\n if(domdebug.debugflags.bus)\n this.releasestack = (new Error).stack;\n\n lockmgr.release(this);\n if(ischild)\n {\n let lockpos = locallocks.indexOf(this);\n locallocks.splice(lockpos,1);\n }\n\n if(domdebug.debugflags.bus)\n {\n console.trace('[bus] Busy lock #' + this.locknum + ' released. ' + lockmgr.getNumLocks() + \" locks active now: \" + lockmgr.getLockIds());\n }\n }\n}\n\n/** Return a promise resolving as soon as the UI is free for at least one tick */\nexport function waitUIFree()\n{\n return lockmgr.waitUIFree();\n}\n\n/** flag userinterface as busy. tests then know not to interact with the UI until the busy flag is released\n @param options Options\n @cell options.ismodal Whether the lock is a modal lock\n*/\nexport function flagUIBusy(options)\n{\n return new Lock(options);\n}\n\nexport function getUIBusyCounter()\n{\n return lockmgr.busycounter;\n}\n\ntry //we're accessing a parent window, so we may hit security exceptions\n{\n if(window.parent && window.parent.$dompack$busylockmanager)\n {\n lockmgr = window.parent.$dompack$busylockmanager;\n ischild = true;\n //if we connected to a parent... deregister our locks, eg. if parent navigated our frame away\n window.addEventListener(\"unload\", () =>\n {\n if(domdebug.debugflags.bus)\n console.log(\"[bus] Frame unloading, \" + locallocks.length + \" locks pending.\", locallocks.map(l=>\"#\"+l.locknum).join(\", \"), locallocks);\n\n //switch to local instance as we'll be unable to auto-release\n let locallockmgr = new LockManager;\n locallocks.forEach(lock => { lockmgr.release(lock); locallockmgr.add(lock); });\n locallocks = [];\n lockmgr = locallockmgr;\n });\n }\n}\ncatch(e)\n{\n}\n\nif(!lockmgr)\n lockmgr = new LockManager;\n\nif(typeof window != 'undefined')\n window.$dompack$busylockmanager = lockmgr;\n", "\n/* Regex to identify dimensionless style sttributes. copied from old version of preact/src/constants.js (MIT)\n meant to capture:\n { boxFlex:1, boxFlexGroup:1, columnCount:1, fillOpacity:1, flex:1, flexGrow:1,\n flexPositive:1, flexShrink:1, flexNegative:1, fontWeight:1, lineClamp:1, lineHeight:1,\n opacity:1, order:1, orphans:1, strokeOpacity:1, widows:1, zIndex:1, zoom:1\n*/\nconst IS_NON_DIMENSIONAL = /acit|ex(?:s|g|n|p|$)|rph|ows|mnc|ntw|ine[ch]|zoo|^ord/i;\n\nfunction generateInsertList(nodes)\n{\n if(nodes.length==1)\n return typeof nodes[0]==='string' ? document.createTextNode(nodes[0]) : nodes[0];\n\n let frag = document.createDocumentFragment();\n nodes.forEach(node => frag.appendChild(typeof node === 'string' ? document.createTextNode(node) : node));\n return frag;\n}\n\nexport function matches(node, selector)\n{\n var tocall = node.matches || node.matchesSelector || node.msMatchesSelector || node.webkitMatchesSelector;\n //if none of the 'matches' was found, this might be a textnode or something. returning false is probably safest\n return tocall && tocall.apply(node, [selector]);\n}\nexport function closest(node, selector)\n{\n if(node.closest)\n return node.closest(selector);\n for(;node&&!matches(node,selector);node=node.parentElement)\n /*iterate*/;\n return node;\n}\n//implements contains. TODO we only really need this on IE11, which doesn't consider a text node a child, we can probably fall back to native elsewhere ?\nexport function contains(ancestor, child)\n{\n for(;child;child=child.parentNode)\n if(child===ancestor)\n return true;\n return false;\n}\n//insert a range of nodes before a node: https://dom.spec.whatwg.org/#dom-childnode-before\nexport function before(node,...nodes)\n{\n if(node.before)\n {\n node.before(...nodes);\n return;\n }\n if(node.parentNode)\n node.parentNode.insertBefore(generateInsertList(nodes), node);\n}\n//insert a range of nodes after a node: https://dom.spec.whatwg.org/#dom-childnode-after\nexport function after(node,...nodes)\n{\n if(node.after)\n {\n node.after(...nodes);\n return;\n }\n if(node.parentNode)\n node.parentNode.insertBefore(generateInsertList(nodes), node.nextSibling);\n}\n//replace node with a set of nodes : https://dom.spec.whatwg.org/#dom-childnode-replacewith\nexport function replaceWith(node,...nodes)\n{\n if(node.replaceWith)\n {\n node.replaceWith(...nodes);\n return;\n }\n if(node.parentNode)\n node.parentNode.replaceChild(generateInsertList(nodes), node);\n}\n//remove node with a set of nodes : https://dom.spec.whatwg.org/#dom-childnode-remove\nexport function remove(node)\n{\n if(node.parentNode)\n node.parentNode.removeChild(node);\n}\n//insert nodes at start: https://dom.spec.whatwg.org/#dom-parentnode-prepend\nexport function prepend(node, ...nodes)\n{\n node.insertBefore(generateInsertList(nodes), node.firstChild);\n}\n//insert nodes at end: https://dom.spec.whatwg.org/#dom-parentnode-append\nexport function append(node, ...nodes)\n{\n node.appendChild(generateInsertList(nodes));\n}\n\n//offer toggleClass ourselves as IE11's native version is broken - does not understand the last parameter\n/** Toggle a single class */\nexport function toggleClass(node, classname, settoggle)\n{\n if (arguments.length === 2)\n node.classList.toggle(classname);\n else if (settoggle)\n node.classList.add(classname);\n else\n node.classList.remove(classname);\n}\n\n/** Toggle classes in a node\n @param node Node which classes to toggle\n @param toggles Object, all keys will be added/removed based on the truthyness of their values\n*/\nexport function toggleClasses(node, toggles)\n{\n if (typeof(toggles) !== \"object\")\n throw new Error(\"Expected an object with keys as classnames\");\n Object.keys(toggles).forEach(key => node.classList[toggles[key] ? \"add\" : \"remove\"](key));\n}\n\n/* remove the contents of an existing node */\nexport function empty(node)\n{\n //node.innerHTML=''; // this does NOT work for IE11, it destroys all nodes instead of unlinking them\n while(node.lastChild)\n node.removeChild(node.lastChild);\n}\n\n/** get the relative bound difference between two elements, and return a writable copy */\nexport function getRelativeBounds(node, relativeto)\n{\n if(!relativeto)\n relativeto = node.ownerDocument.documentElement;\n\n var nodecoords = node.getBoundingClientRect();\n var relcoords = relativeto.getBoundingClientRect();\n return { top: nodecoords.top - relcoords.top\n , left: nodecoords.left - relcoords.left\n , right: nodecoords.right - relcoords.left\n , bottom: nodecoords.bottom - relcoords.top\n , width: nodecoords.width\n , height: nodecoords.height\n };\n}\n\nexport function isDomReady()\n{\n return document.readyState == \"interactive\" || document.readyState == \"complete\";\n}\n\n/* run the specified function 'on ready'. adds to DOMContentLoaded if dom is not ready yet. Exceptions from the ready handler will not be fatal to the rest of code execution */\nexport function onDomReady(callback)\n{\n if(isDomReady())\n {\n try\n {\n callback();\n }\n catch(e)\n {\n console.error(\"Exception executing a domready handler\");\n console.log(e,e.stack);\n\n if (window.onerror)\n {\n // Send to onerror to trigger exception reporting\n try\n {\n window.onerror(e.message, e.fileName || \"\", e.lineNumber || 1, e.columNumber || 1, e);\n }\n catch (e)\n {\n }\n }\n }\n }\n else\n document.addEventListener(\"DOMContentLoaded\", callback);\n}\n\n//parse JSON data, throw with more info on parse failure\nexport function getJSONAttribute(node, attributename)\n{\n try\n {\n return JSON.parse(node.getAttribute(attributename));\n }\n catch(e)\n {\n console.error(\"JSON parse failure on attribute '\" +attributename+ \"' of node\", node);\n throw e;\n }\n}\n\n/** Get the base URI of the current document. IE11 doesn't implement document.baseURI\n @param doc Document to query. Defaults to window.document\n*/\nexport function getBaseURI(doc)\n{\n if(!doc)\n doc=window.document;\n if(doc.baseURI)\n return doc.baseURI;\n\n let base = doc.querySelector('base');\n if(base && base.href)\n return base.href;\n return doc.URL;\n}\n\n//queryselector quick wrapper\nexport function qS(node_or_selector, selector)\n{\n if(typeof node_or_selector == 'string')\n return document.querySelector(node_or_selector);\n else\n return node_or_selector.querySelector(selector);\n}\n\n//queryselectorall quick wrapper\nexport function qSA(node_or_selector, selector)\n{\n if(typeof node_or_selector == 'string')\n return Array.from(document.querySelectorAll(node_or_selector));\n else\n return Array.from(node_or_selector.querySelectorAll(selector));\n}\n\n\n/** Sets multiple styles on a node, automatically adding 'px' to numbers when appropriate\n (can be used as replacement for Mootools .setStyles)\n*/\nexport function setStyles(node, value)\n{\n if (!value || typeof value === 'string')\n node.style.cssText = value || '';\n else if (typeof value === 'object')\n {\n for (let i in value)\n {\n // for numbers, add 'px' if the constant isn't dimensionless (eg zIndex)\n node.style[i] = typeof value[i] === 'number' && IS_NON_DIMENSIONAL.test(i) === false\n ? value[i] + 'px'\n : value[i];\n }\n }\n}\n", "import { append, setStyles } from './tree.es';\n\nfunction flattenArray(list)\n{\n return list.reduce((acc, elt) => acc.concat(Array.isArray(elt) ? flattenArray(elt) : elt), []);\n}\n\nfunction setClassName(node, value)\n{\n if (!value || typeof value === 'string')\n node.className = value || '';\n else if (Array.isArray(value))\n node.className = value.filter(elt => elt && typeof elt === 'string').join(\" \");\n else\n {\n let str = \"\";\n Object.keys(value).forEach((key, idx) => { if (value[key]) str += (idx ? \" \" : \"\") + key; });\n node.className = str;\n }\n}\n\n/** Matches non-first uppercase characters\n (when the second char is uppercases, the first char is passed too)\n*/\nconst MATCH_UPCASE = /([A-Z])/g;\nconst MATCH_DASH_AND_CHAR = /-([a-zA-Z])/g;\n\n/** Convert a camelCased identifier to a dashed string\n*/\nexport function toDashed(value)\n{\n if (value)\n return (value.substring(0, 1) + value.substring(1).replace(MATCH_UPCASE, \"-$1\")).toLowerCase();\n}\n\n/** Convert a dashed string to a camelCase identifier\n*/\nexport function toCamel(value)\n{\n return value.replace(MATCH_DASH_AND_CHAR, (item, match_1) => match_1.toUpperCase());\n}\n\nfunction attrHasBooleanValue(propname)\n{\n return ['disabled','checked','selected','readonly','multiple','ismap'].includes(propname);\n}\n\nfunction createElement(elementname, attributes, toattrs)\n{\n var node = document.createElement(elementname);\n if(attributes)\n {\n Object.keys(attributes).forEach(attrname =>\n {\n if(attrname == 'events')\n throw new Error(\"Use 'on' instead of 'events' in dompack.create\");\n if(attrname == 'styles')\n throw new Error(\"Use 'style' instead of 'styles' in dompack.create\");\n if(attrname == 'children')\n {\n // allow null 'children' property for jsxcreate, property delete is detrimental to performance.\n if (attributes[attrname])\n throw new Error(\"Use 'childNodes' instead of 'children' in dompack.create\");\n return;\n }\n\n let value = attributes[attrname];\n\n if (attrname == 'on') //create event listeners\n return void Object.keys(value).forEach(eventname => node.addEventListener(eventname, value[eventname], false));\n else if (attrname.startsWith(\"on\"))\n return void node.addEventListener(toDashed(attrname.substring(2)), value, false);\n\n if (attrname == \"className\" || attrname == \"class\")\n {\n if(node.className) // already modified the class?\n throw new Error(\"Specify either 'className' or 'class' to dompack.create, but not both\");\n setClassName(node, value);\n return;\n }\n\n if(attrname == 'style')\n return void setStyles(node, value);\n\n if(attrname == 'dataset') //explicitly assign\n return void Object.assign(node[attrname], value);\n\n if(attrname == 'childNodes') //append as children\n return void append(node, ...attributes.childNodes.filter(child => child != null && child !== true && child !== false));\n\n if(toattrs && attrHasBooleanValue(attrname))\n {\n if(value)\n node.setAttribute(attrname,\"\");\n else\n node.removeAttribute(attrname);\n return;\n }\n\n if (toattrs && !attrname.startsWith(\"prop\"))\n {\n if (value != null) // matches not null and not undefined\n {\n if (value && typeof value == \"object\")\n throw new Error(\"Cannot store non-null objects in attributes, use a property starting with 'prop'\");\n node.setAttribute(attrname, attributes[attrname]);\n }\n }\n else\n node[attrname] = attributes[attrname];\n });\n }\n return node;\n}\n\n/* create elements. sets properties (not attributes!) immediately.\n everything inside 'on' is added as an addEventListener without capture\n everything inside 'childNodes' is appended to the node. nulls are ignored inside childNodes\n\n Examples:\n domtools.create(\"input\", { type:\"file\", className: \"myupload\", style: { display: \"none\" }));\n\n*/\nexport function create(elementname, attributes)\n{\n return createElement(elementname, attributes, false);\n}\n\n/** Function to create for jsx, create elements directly (instead of virtual dom nodes).\n\n import * as dompack from 'dompack';\n\n /* @jsx dompack.jsxcreate *\\/\n your code\n*/\nexport function jsxcreate(element, attributes, ...childNodes)\n{\n // Ensure attributes\n attributes = attributes || {};\n // Flatten childnodes arrays, convert numbers to strings. Also support children property (React uses that)\n let parts = (attributes.childNodes || []).concat(attributes.children || []).concat(childNodes);\n if (attributes.children)\n attributes.children = null;\n parts = flattenArray(parts);\n parts = parts.map((elt) => typeof elt === \"number\" ? String(elt) : elt);\n // Create the element\n attributes.childNodes = parts;\n if (typeof element === \"function\")\n return element(attributes, null, null);\n return createElement(element, attributes, true);\n}\n\n", "import * as domtree from './tree.es';\nimport * as domevents from './events.es';\n\nlet components = [];\nconst map = new WeakMap();\n\n\n//is a node completely in the dom? if we can find a sibling anywhere, it must be closed\nfunction isNodeCompletelyInDom(node)\n{\n for(;node;node=node.parentNode)\n if(node.nextSibling)\n return true;\n return false;\n}\nfunction processRegistration(item, reg, domready)\n{\n if(!domready && !isNodeCompletelyInDom(item))\n return; //not safe to register\n\n if (!map.has(item))\n map.set(item, [ reg.num ]);\n else\n {\n let list = map.get(item);\n if (list.includes(reg.num))\n return;\n list.push(reg.num);\n }\n reg.handler(item, reg.index++); //note: if an exception is reported from Object.handler,\n}\nfunction applyRegistration(reg, startnode)\n{\n let domready = domtree.isDomReady();\n if(reg.afterdomready && !domready)\n return;\n\n let items = Array.from( (startnode || document).querySelectorAll(reg.selector));\n if(startnode && domtree.matches(startnode,reg.selector))\n items.unshift(startnode);\n\n items.forEach(item =>\n {\n try\n {\n processRegistration(item, reg, domready);\n }\n catch(e)\n {\n console.error(\"Exception handling registration of\",item,\"for rule\",reg.selector);\n console.log(\"Registration\",reg);\n console.log(e,e.stack);\n if (window.onerror)\n {\n // Send to onerror to trigger exception reporting\n try\n {\n window.onerror(e.message, e.fileName || \"\", e.lineNumber || 1, e.columNumber || 1, e);\n }\n catch (e)\n {\n }\n }\n }\n });\n}\n\n/** getBoundingClientRect, but as a plain copyable object.. Debugging and other code often needs this\n @param node Node to query\n @param srcret Offset rectangle\n @return top,bottom,left,right,width,height like getBCR, but spreadable/assignable/copyable etc*/\nexport function getRect(node, srcrect)\n{\n const bcr = node.getBoundingClientRect();\n let rect = { top: bcr.top\n , bottom: bcr.bottom\n , left: bcr.left\n , right: bcr.right\n , width: bcr.width\n , height: bcr.height\n };\n\n if(srcrect)\n {\n rect.top = rect.top - srcrect.top;\n rect.bottom = rect.bottom - srcrect.top;\n rect.left = rect.left - srcrect.left;\n rect.right = rect.right - srcrect.left;\n }\n return rect;\n}\n\n/* A focus implementation that allows the node to intercept focused, allowing eg\n radio/checkbox replacements to redirect focus but also explicitly preventing\n focus of a disabled element\n Returns true when the focus operation was successfull or handled by an event handler.\n @param node Node to focus\n @param options.preventScroll Prevent scroll to focused element\n*/\nexport function focus(node, options)\n{\n if(!domevents.dispatchCustomEvent(node, 'dompack:takefocus', { bubbles: true, cancelable: true, detail: {options} }))\n return true;\n\n if(node.disabled)\n return false;\n\n // IE likes to throw errors when setting focus\n try\n {\n node.focus(options);\n }\n catch(e)\n {\n return false;\n }\n return true;\n}\n\n/** Deprecated, invoke scrollIntoView directly on the nodes */\nexport function scrollIntoView(node, options)\n{\n node.scrollIntoView(options);\n return true;\n}\n\n/** @short Register a component for auto-initialization.\n @param selector Selector the component must match\n @param handler Handler\n @param options Any unrecognized options are passed to the handler\n\n The handler will be invoked with two parameters\n - the node to register\n - the index of the node (a unique counter for this selector - first is 0) */\n\nexport function register(selector, handler, options)\n{\n let newreg = { selector: selector\n , handler: handler\n , index: 0\n , num: components.length\n , afterdomready: !options || options.afterdomready\n };\n if(components.length==0 && !domtree.isDomReady()) //first component... we'll need a ready handler\n domtree.onDomReady(() => registerMissed());\n\n components.push(newreg);\n applyRegistration(newreg, null);\n}\n\n// register any components we missed on previous scans\nexport function registerMissed(startnode)\n{\n let todo = components.slice(0);\n todo.forEach(item => applyRegistration(item, startnode));\n}\n", "/* @importstatement:\n import * as dompack from 'dompack';\n\n This function is our public api. Any direct inclusions from src/.... not mentioned here are not a stable API\n*/\n\nexport { createDeferred } from './src/promise.es';\nexport { flagUIBusy } from './src/busy.es';\nexport { dispatchCustomEvent, dispatchDomEvent, fireModifiedEvents, changeValue, normalizeKeyboardEventData\n , stop\n } from './src/events.es';\nexport { qS, qSA, contains, closest, matches\n , empty, isDomReady, onDomReady, getJSONAttribute\n , before, after, replaceWith, remove, prepend, append\n , toggleClass, toggleClasses\n , setStyles\n , getBaseURI, getRelativeBounds } from './src/tree.es';\nexport { create, jsxcreate } from './src/create.es';\nexport { focus, register, registerMissed, scrollIntoView, getRect } from './src/components.es';\nexport { debugflags, parseDebugURL, addDebugFlags, initDebug } from './src/debug.es';\n", "/**\nimport * as whintegration from '@mod-system/js/wh/integration';\n*/\n\nimport * as dompack from 'dompack';\n\nexport let config = {};\n\nfunction generateForm(action, values, method)\n{\n var form = dompack.create(\"form\", { action: action, method: method || \"POST\", charset: \"utf-8\" });\n if(values instanceof Array)\n {\n values.forEach(function(item)\n {\n form.appendChild(dompack.create(\"input\", { type: \"hidden\", name: item.name, value: item.value }));\n });\n }\n else Object.keys(values, key =>\n {\n form.appendChild(dompack.create(\"input\", { type: \"hidden\", name: key, value: values[key] }));\n });\n return form;\n}\n\nexport function submitForm(action, values, method)\n{\n var form = generateForm(action, values, method);\n document.body.appendChild(form);\n form.submit();\n}\n\nexport function executeSubmitInstruction(instr, options)\n{\n if(!instr)\n throw Error(\"Unknown instruction received\");\n\n options = Object.assign({ ismodal: true }, options);\n //Are there any cirumstances where you would want to reelase this lock?\n dompack.flagUIBusy({ ismodal: options.ismodal });\n\n if (options.iframe)\n {\n switch (instr.type)\n {\n case \"redirect\":\n {\n options.iframe.src = instr.url;\n } break;\n\n case \"form\":\n {\n // FIXME: Clear iframe if document is not cross-domain accessible\n var idoc = options.iframe.document || options.iframe.contentDocument || options.iframe.contentWindow.document;\n\n var form = generateForm(instr.form.action, instr.form.vars, instr.method);\n var adopted_form = idoc.adoptNode(form);\n idoc.body.appendChild(adopted_form);\n adopted_form.submit();\n } break;\n\n default:\n {\n throw Error(\"Unknown submit instruction '\" + instr.type + \"' for iframe received\");\n }\n }\n return;\n }\n\n switch (instr.type)\n {\n case \"redirect\":\n {\n location.href=instr.url;\n } break;\n\n case \"form\":\n {\n submitForm(instr.form.action, instr.form.vars, instr.form.method);\n } break;\n\n case \"refresh\":\n case \"reload\":\n {\n window.location.reload();\n } break;\n\n case \"postmessage\":\n {\n if (!instr.target || instr.target === \"parent\")\n parent.postMessage(instr.message, \"*\");\n else if (instr.target === \"opener\")\n {\n opener.postMessage(instr.message, \"*\");\n window.close();\n }\n else\n throw Error(\"Unknown postmessage target '\" + instr.target + \"' received\");\n } break;\n\n case \"close\":\n {\n window.close();\n } break;\n\n default:\n {\n throw new Error(\"Unknown submit instruction '\" + instr.type + \"' received\");\n }\n }\n}\n\nif(typeof window !== 'undefined') //check we're in a browser window, ie not serverside or some form of worker\n{\n let whconfigel = typeof document != \"undefined\" ? document.querySelector('script#wh-config') : null;\n if(whconfigel)\n config = JSON.parse(whconfigel.textContent);\n\n // Make sure we have obj/site as some sort of object, to prevent crashes on naive 'if ($wh.config.obj.x)' tests'\n if(!config.obj)\n config.obj={};\n if(!config.site)\n config.site={};\n\n let errhandler = config[\"system:errorhandler\"];\n if(errhandler)\n console.error(errhandler.statuscode + \" \" + errhandler.statusmessage);\n}\n", "/** @import: import KeyboardHandler from \"dompack/extra/keyboard\";\n\n*/\n\n/* All keynames should be mixed-cased, as done here: http://www.w3.org/TR/DOM-Level-3-Events-key/\n\n Modifiers should be in the ordering Alt+Control+Meta+Shift+key (ie alphabetical)\n\n To simply prevent keys from propagating up (ie to keep Enter inside its textarea)\n new KeyboardHandler(this.textarea, {}, { dontpropagate: ['Enter']});\n*/\n\nimport { normalizeKeyboardEventData } from '../src/events.es';\nimport { debugflags } from '../src/debug.es';\n\nvar propnames = { \"shiftKey\": \"Shift\"\n , \"ctrlKey\": navigator.platform == \"MacIntel\" ? \"Control\" : [ \"Accel\", \"Control\" ]\n , \"metaKey\": navigator.platform == \"MacIntel\" ? [ \"Accel\", \"Meta\" ] : \"Meta\"\n , \"altKey\": \"Alt\"\n };\n\nfunction getFinalKey(event) //get the naem for the 'final' key, eg the 'D' in 'alt+control+d'\n{\n if(event.code.startsWith('Key') && event.code.length==4)\n return event.code.substr(3,1).toUpperCase();\n if(event.code.startsWith('Digit') && event.code.length==6)\n return event.code.substr(5,1).toUpperCase();\n return event.key.length === 1 ? event.key.toUpperCase() : event.key;\n}\n\nfunction getKeyNames(event)\n{\n let names = [[]];\n\n/*\n // Firefix under selenium on linux always says 'Unidentified' as key. Backup for some keys.\n if (basekey == \"Unidentified\")\n basekey = selenium_backup[event.keyCode];\n*/\n // Create the modifiers in the names array (omit the basekey, so we can sort on modifier first)\n Object.keys(propnames).forEach(function(propname)\n {\n if (event[propname])\n {\n // The key is pressed. Add the modifier name to all current names.\n var modifier = propnames[propname];\n if (!Array.isArray(modifier))\n names.forEach(function(arr) { arr.push(modifier); });\n else\n {\n // Multiple modifiers map to this key, duplicate all result sequences for every modifier\n var newkeys = [];\n modifier.forEach(function(singlemodifier)\n {\n names.forEach(function(arr)\n {\n newkeys.push(arr.concat([ singlemodifier ]));\n });\n });\n names = newkeys;\n }\n }\n });\n\n names = names.map(function(arr)\n {\n // Sort the modifier names\n arr = arr.sort();\n arr.push(getFinalKey(event));\n return arr.join(\"+\");\n });\n\n return names;\n}\n\nfunction validateKeyName(key)\n{\n var modifiers = key.split(\"+\");\n modifiers.pop();\n\n // Check for allowed modifiers\n modifiers.forEach(function(mod)\n {\n if (![ \"Accel\", \"Alt\", \"Control\", \"Meta\", \"Shift\"].includes(mod))\n throw new Error(\"Illegal modifier name '\" + mod + \"' in key '\" + key + \"'\");\n });\n\n var original_order = modifiers.join('+');\n modifiers.sort();\n if (modifiers.join('+') != original_order)\n throw new Error(\"Illegal key name \" + key + \", modifiers must be sorted alphabetically\");\n}\n\n/** node: The node to attach to\n keymap: Keymap\n options.stopmapped - preventDefault and stopPropagation on any key we have in our map\n options.dontpropagate - string array of keys not to propagate out of this object\n options.onkeypress - when set, call for all keypresses. signature: function(event, key). Should always return true and preventDefault (and/or stop) the event to cancel its handling\n options.listenoptions - addEventListener options (eg {capture:true})\n*/\nexport default class KeyboardHandler\n{\n constructor(node, keymap, options)\n {\n this.node = node;\n this.keymap = {};\n this.stopmapped = options&&options.stopmapped;\n this.dontpropagate = options && options.dontpropagate ? [...options.dontpropagate].map(name => name.toUpperCase()) : [];\n this.onkeypress = options&&options.onkeypress;\n this.captureunsafekeys = options&&options.captureunsafekeys;\n this._listenoptions = (options && options.listenoptions) || {};\n\n Object.keys(keymap).forEach(keyname =>\n {\n if (debugflags.key)\n validateKeyName(keyname);\n this.keymap[keyname.toUpperCase()] = keymap[keyname];\n });\n\n this._onkeydown = (event) => this._onKeyDown(event);\n this._onkeypress = (event) => this._onKeyPress(event);\n node.addEventListener('keydown', this._onkeydown, this._listenoptions);\n node.addEventListener('keypress', this._onkeypress, this._listenoptions);\n }\n\n destroy()\n {\n this.node.removeEventListener('keydown', this._onkeydown, this._listenoptions);\n this.node.removeEventListener('keypress', this._onkeypress, this._listenoptions);\n }\n\n /** Returns thether the current pressed special key should be ignored for the current target node\n Used to detect input/textarea/rte's\n @param target Current target node for keyboard event\n @param key Parsed key (as returned by GetKeyNames)\n @return Whether the key must be ignored by KeyboardHandler, default browser behaviour should be triggered.\n */\n _mustIgnoreKey(target, key, keynames)\n {\n var tag = target.nodeName.toLowerCase();\n if (tag == \"select\")\n {\n if ([\"ArrowUp\", \"ArrowDown\", \"Home\", \"End\", \"PageUp\", \"PageDown\"].indexOf(key) != -1)\n return true;\n }\n else if (tag == \"input\" || tag == \"textarea\" || target.isContentEditable)\n {\n // These keys we ignore, regardless of the modifier\n if ([ \"ArrowLeft\", \"ArrowRight\", \"ArrowUp\", \"ArrowDown\"\n , \"PageUp\", \"PageDown\"\n , \"Home\", \"End\"\n , \"Insert\", \"Delete\", \"Backspace\"\n ].indexOf(key) != -1)\n return true;\n\n var is_special_combo = false;\n\n // only input doesn't want exact combo 'Enter', the rest does\n if (tag != \"input\" && keynames.indexOf(\"Enter\") != -1)\n is_special_combo = true;\n\n // Only contenteditable wants \"Shift+Enter\"\n if (target.isContentEditable && keynames.indexOf(\"Shift+Enter\") != -1)\n is_special_combo = true;\n\n // These exact combo's are wanted by all inputs\n [ \"Accel+A\", \"Accel+V\", \"Accel+C\", \"Accel+X\" ].forEach(function(name)\n {\n is_special_combo = is_special_combo || keynames.indexOf(name) != -1;\n });\n return is_special_combo;\n }\n return false;\n }\n\n addKey(keybinding, handler)\n {\n if(debugflags.key)\n {\n validateKeyName(keybinding);\n console.log(\"[key] KeyDown handler registered for \" + keybinding);\n }\n this.keymap[keybinding.toUpperCase()] = handler;\n }\n removeKey(keybinding)\n {\n delete this.keymap[keybinding.toUpperCase()];\n }\n _onKeyDown(event)\n {\n let keydata = normalizeKeyboardEventData(event);\n\n // Get all possible names for this key\n let keynames = getKeyNames(keydata);\n if (!keydata.key || !keynames.length)\n {\n if(debugflags.key)\n console.log(\"[key] KeyDown handler for \", this.node, \" did not recognize key from event\",event);\n return true;\n }\n\n if(debugflags.key)\n console.log(\"[key] KeyDown handler for \", this.node, \" got key \", keydata.key, \" with target \", event.target, \" keynames:\",keynames);\n\n /* Some keys we ignore, unless we're explicitly bound to a node, so we don't inadvertly break eg a node inside\n a listview we're handling or otherwise break a user's expectation. Set the option 'captureunsafekeys' if you explicitly\n want to be able to capture any key */\n\n if (!this.captureunsafekeys && this._mustIgnoreKey(event.target, keydata.key, keynames))\n {\n if(debugflags.key)\n console.log(\"[key] KeyDown event will not be intercepted, it's an unsafe key to intercept\");\n return true;\n }\n\n if (this.dontpropagate)\n {\n keynames.forEach(keyname =>\n {\n if (this.dontpropagate.includes(keyname))\n {\n if(debugflags.key)\n console.log(\"[key] KeyDown event will not bubbleup because of our dontpropagate option (but may still trigger a default action)\");\n event.stopPropagation();\n }\n });\n }\n\n for (var i = 0; i < keynames.length; ++i)\n {\n let mapping = this.keymap[keynames[i].toUpperCase()];\n if(!mapping)\n continue;\n\n if (this.stopmapped)\n {\n if(debugflags.key)\n console.log(\"[key] KeyDown event will not bubbleup or trigger default, because we're configured to block any mapped key\");\n event.stopPropagation();\n event.preventDefault();\n }\n\n let ishandled = mapping.apply(this.node,[event]);\n if(ishandled && !event.defaultPrevented)\n {\n console.warn(`The key handler for '${keynames[i]}' should preventDefault (or dompack.stop) the event to block fruther propagation`);\n event.stopPropagation();\n event.preventDefault();\n if(debugflags.key)\n console.log(\"[key] KeyDown event will not bubbleup or trigger default, because the keyhandler indicated the key was handled\");\n }\n\n if(!event.defaultPrevented && debugflags.key)\n console.log(\"[key] KeyDown event was not blocked by its explicitly configured handler\");\n }\n return true;\n }\n _onKeyPress(event)\n {\n let keydata = normalizeKeyboardEventData(event);\n\n if (this.onkeypress)\n {\n if (!this.onkeypress.apply(this.node, [ event, keydata.key ]))\n {\n if(!event.defaultPrevented)\n console.warn(\"The onkeypress handler should preventDefault (or dompack.stop) the event to block fruther propagation\");\n event.stopPropagation();\n event.preventDefault();\n }\n }\n }\n}\n\nexport function getEventKeyNames(event)\n{\n let keydata = normalizeKeyboardEventData(event);\n return getKeyNames(keydata);\n}\n\nKeyboardHandler.getEventKeyNames = function(event)\n{\n let keydata = normalizeKeyboardEventData(event);\n return getKeyNames(keydata);\n};\n\n/** Is the native 'copy' modifier for this platform pressed? */\nKeyboardHandler.hasNativeEventCopyKey = function(event)\n{\n return event && (navigator.platform == \"MacIntel\" ? event.altKey : event.ctrlKey);\n};\n\n/** Is the native 'multiselect' modifier for this platform pressed? */\nKeyboardHandler.hasNativeEventMultiSelectKey = function(event)\n{\n return event && (navigator.platform == \"MacIntel\" ? event.metaKey : event.ctrlKey);\n};\n\nKeyboardHandler.getDragModeOverride = function(event)\n{\n const modifiers =\n (event.altKey?\"Alt+\":\"\") +\n (event.ctrlKey?\"Control+\":\"\") +\n (event.metaKey?\"Meta+\":\"\") +\n (event.shiftKey?\"Shift+\":\"\") +\n (navigator.platform === \"MacIntel\" ? \"Mac\" : \"Other\");\n\n let override = \"\";\n switch (modifiers)\n {\n case \"Shift+Other\":\n case \"Meta+Other\": override = \"move\"; break;\n case \"Control+Other\":\n case \"Alt+Mac\": override = \"copy\"; break;\n case \"Control+Shift+Other\":\n case \"Alt+Other\":\n case \"Control+Mac\": override = \"link\"; break;\n }\n\n return override;\n};\n", "// Copyright Joyent, Inc. and other Node contributors.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a\n// copy of this software and associated documentation files (the\n// \"Software\"), to deal in the Software without restriction, including\n// without limitation the rights to use, copy, modify, merge, publish,\n// distribute, sublicense, and/or sell copies of the Software, and to permit\n// persons to whom the Software is furnished to do so, subject to the\n// following conditions:\n//\n// The above copyright notice and this permission notice shall be included\n// in all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN\n// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\n// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\n// USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n'use strict';\n\nvar R = typeof Reflect === 'object' ? Reflect : null\nvar ReflectApply = R && typeof R.apply === 'function'\n ? R.apply\n : function ReflectApply(target, receiver, args) {\n return Function.prototype.apply.call(target, receiver, args);\n }\n\nvar ReflectOwnKeys\nif (R && typeof R.ownKeys === 'function') {\n ReflectOwnKeys = R.ownKeys\n} else if (Object.getOwnPropertySymbols) {\n ReflectOwnKeys = function ReflectOwnKeys(target) {\n return Object.getOwnPropertyNames(target)\n .concat(Object.getOwnPropertySymbols(target));\n };\n} else {\n ReflectOwnKeys = function ReflectOwnKeys(target) {\n return Object.getOwnPropertyNames(target);\n };\n}\n\nfunction ProcessEmitWarning(warning) {\n if (console && console.warn) console.warn(warning);\n}\n\nvar NumberIsNaN = Number.isNaN || function NumberIsNaN(value) {\n return value !== value;\n}\n\nfunction EventEmitter() {\n EventEmitter.init.call(this);\n}\nmodule.exports = EventEmitter;\n\n// Backwards-compat with node 0.10.x\nEventEmitter.EventEmitter = EventEmitter;\n\nEventEmitter.prototype._events = undefined;\nEventEmitter.prototype._eventsCount = 0;\nEventEmitter.prototype._maxListeners = undefined;\n\n// By default EventEmitters will print a warning if more than 10 listeners are\n// added to it. This is a useful default which helps finding memory leaks.\nvar defaultMaxListeners = 10;\n\nfunction checkListener(listener) {\n if (typeof listener !== 'function') {\n throw new TypeError('The \"listener\" argument must be of type Function. Received type ' + typeof listener);\n }\n}\n\nObject.defineProperty(EventEmitter, 'defaultMaxListeners', {\n enumerable: true,\n get: function() {\n return defaultMaxListeners;\n },\n set: function(arg) {\n if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) {\n throw new RangeError('The value of \"defaultMaxListeners\" is out of range. It must be a non-negative number. Received ' + arg + '.');\n }\n defaultMaxListeners = arg;\n }\n});\n\nEventEmitter.init = function() {\n\n if (this._events === undefined ||\n this._events === Object.getPrototypeOf(this)._events) {\n this._events = Object.create(null);\n this._eventsCount = 0;\n }\n\n this._maxListeners = this._maxListeners || undefined;\n};\n\n// Obviously not all Emitters should be limited to 10. This function allows\n// that to be increased. Set to zero for unlimited.\nEventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {\n if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) {\n throw new RangeError('The value of \"n\" is out of range. It must be a non-negative number. Received ' + n + '.');\n }\n this._maxListeners = n;\n return this;\n};\n\nfunction _getMaxListeners(that) {\n if (that._maxListeners === undefined)\n return EventEmitter.defaultMaxListeners;\n return that._maxListeners;\n}\n\nEventEmitter.prototype.getMaxListeners = function getMaxListeners() {\n return _getMaxListeners(this);\n};\n\nEventEmitter.prototype.emit = function emit(type) {\n var args = [];\n for (var i = 1; i < arguments.length; i++) args.push(arguments[i]);\n var doError = (type === 'error');\n\n var events = this._events;\n if (events !== undefined)\n doError = (doError && events.error === undefined);\n else if (!doError)\n return false;\n\n // If there is no 'error' event listener then throw.\n if (doError) {\n var er;\n if (args.length > 0)\n er = args[0];\n if (er instanceof Error) {\n // Note: The comments on the `throw` lines are intentional, they show\n // up in Node's output if this results in an unhandled exception.\n throw er; // Unhandled 'error' event\n }\n // At least give some kind of context to the user\n var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : ''));\n err.context = er;\n throw err; // Unhandled 'error' event\n }\n\n var handler = events[type];\n\n if (handler === undefined)\n return false;\n\n if (typeof handler === 'function') {\n ReflectApply(handler, this, args);\n } else {\n var len = handler.length;\n var listeners = arrayClone(handler, len);\n for (var i = 0; i < len; ++i)\n ReflectApply(listeners[i], this, args);\n }\n\n return true;\n};\n\nfunction _addListener(target, type, listener, prepend) {\n var m;\n var events;\n var existing;\n\n checkListener(listener);\n\n events = target._events;\n if (events === undefined) {\n events = target._events = Object.create(null);\n target._eventsCount = 0;\n } else {\n // To avoid recursion in the case that type === \"newListener\"! Before\n // adding it to the listeners, first emit \"newListener\".\n if (events.newListener !== undefined) {\n target.emit('newListener', type,\n listener.listener ? listener.listener : listener);\n\n // Re-assign `events` because a newListener handler could have caused the\n // this._events to be assigned to a new object\n events = target._events;\n }\n existing = events[type];\n }\n\n if (existing === undefined) {\n // Optimize the case of one listener. Don't need the extra array object.\n existing = events[type] = listener;\n ++target._eventsCount;\n } else {\n if (typeof existing === 'function') {\n // Adding the second element, need to change to array.\n existing = events[type] =\n prepend ? [listener, existing] : [existing, listener];\n // If we've already got an array, just append.\n } else if (prepend) {\n existing.unshift(listener);\n } else {\n existing.push(listener);\n }\n\n // Check for listener leak\n m = _getMaxListeners(target);\n if (m > 0 && existing.length > m && !existing.warned) {\n existing.warned = true;\n // No error code for this since it is a Warning\n // eslint-disable-next-line no-restricted-syntax\n var w = new Error('Possible EventEmitter memory leak detected. ' +\n existing.length + ' ' + String(type) + ' listeners ' +\n 'added. Use emitter.setMaxListeners() to ' +\n 'increase limit');\n w.name = 'MaxListenersExceededWarning';\n w.emitter = target;\n w.type = type;\n w.count = existing.length;\n ProcessEmitWarning(w);\n }\n }\n\n return target;\n}\n\nEventEmitter.prototype.addListener = function addListener(type, listener) {\n return _addListener(this, type, listener, false);\n};\n\nEventEmitter.prototype.on = EventEmitter.prototype.addListener;\n\nEventEmitter.prototype.prependListener =\n function prependListener(type, listener) {\n return _addListener(this, type, listener, true);\n };\n\nfunction onceWrapper() {\n if (!this.fired) {\n this.target.removeListener(this.type, this.wrapFn);\n this.fired = true;\n if (arguments.length === 0)\n return this.listener.call(this.target);\n return this.listener.apply(this.target, arguments);\n }\n}\n\nfunction _onceWrap(target, type, listener) {\n var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener };\n var wrapped = onceWrapper.bind(state);\n wrapped.listener = listener;\n state.wrapFn = wrapped;\n return wrapped;\n}\n\nEventEmitter.prototype.once = function once(type, listener) {\n checkListener(listener);\n this.on(type, _onceWrap(this, type, listener));\n return this;\n};\n\nEventEmitter.prototype.prependOnceListener =\n function prependOnceListener(type, listener) {\n checkListener(listener);\n this.prependListener(type, _onceWrap(this, type, listener));\n return this;\n };\n\n// Emits a 'removeListener' event if and only if the listener was removed.\nEventEmitter.prototype.removeListener =\n function removeListener(type, listener) {\n var list, events, position, i, originalListener;\n\n checkListener(listener);\n\n events = this._events;\n if (events === undefined)\n return this;\n\n list = events[type];\n if (list === undefined)\n return this;\n\n if (list === listener || list.listener === listener) {\n if (--this._eventsCount === 0)\n this._events = Object.create(null);\n else {\n delete events[type];\n if (events.removeListener)\n this.emit('removeListener', type, list.listener || listener);\n }\n } else if (typeof list !== 'function') {\n position = -1;\n\n for (i = list.length - 1; i >= 0; i--) {\n if (list[i] === listener || list[i].listener === listener) {\n originalListener = list[i].listener;\n position = i;\n break;\n }\n }\n\n if (position < 0)\n return this;\n\n if (position === 0)\n list.shift();\n else {\n spliceOne(list, position);\n }\n\n if (list.length === 1)\n events[type] = list[0];\n\n if (events.removeListener !== undefined)\n this.emit('removeListener', type, originalListener || listener);\n }\n\n return this;\n };\n\nEventEmitter.prototype.off = EventEmitter.prototype.removeListener;\n\nEventEmitter.prototype.removeAllListeners =\n function removeAllListeners(type) {\n var listeners, events, i;\n\n events = this._events;\n if (events === undefined)\n return this;\n\n // not listening for removeListener, no need to emit\n if (events.removeListener === undefined) {\n if (arguments.length === 0) {\n this._events = Object.create(null);\n this._eventsCount = 0;\n } else if (events[type] !== undefined) {\n if (--this._eventsCount === 0)\n this._events = Object.create(null);\n else\n delete events[type];\n }\n return this;\n }\n\n // emit removeListener for all listeners on all events\n if (arguments.length === 0) {\n var keys = Object.keys(events);\n var key;\n for (i = 0; i < keys.length; ++i) {\n key = keys[i];\n if (key === 'removeListener') continue;\n this.removeAllListeners(key);\n }\n this.removeAllListeners('removeListener');\n this._events = Object.create(null);\n this._eventsCount = 0;\n return this;\n }\n\n listeners = events[type];\n\n if (typeof listeners === 'function') {\n this.removeListener(type, listeners);\n } else if (listeners !== undefined) {\n // LIFO order\n for (i = listeners.length - 1; i >= 0; i--) {\n this.removeListener(type, listeners[i]);\n }\n }\n\n return this;\n };\n\nfunction _listeners(target, type, unwrap) {\n var events = target._events;\n\n if (events === undefined)\n return [];\n\n var evlistener = events[type];\n if (evlistener === undefined)\n return [];\n\n if (typeof evlistener === 'function')\n return unwrap ? [evlistener.listener || evlistener] : [evlistener];\n\n return unwrap ?\n unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);\n}\n\nEventEmitter.prototype.listeners = function listeners(type) {\n return _listeners(this, type, true);\n};\n\nEventEmitter.prototype.rawListeners = function rawListeners(type) {\n return _listeners(this, type, false);\n};\n\nEventEmitter.listenerCount = function(emitter, type) {\n if (typeof emitter.listenerCount === 'function') {\n return emitter.listenerCount(type);\n } else {\n return listenerCount.call(emitter, type);\n }\n};\n\nEventEmitter.prototype.listenerCount = listenerCount;\nfunction listenerCount(type) {\n var events = this._events;\n\n if (events !== undefined) {\n var evlistener = events[type];\n\n if (typeof evlistener === 'function') {\n return 1;\n } else if (evlistener !== undefined) {\n return evlistener.length;\n }\n }\n\n return 0;\n}\n\nEventEmitter.prototype.eventNames = function eventNames() {\n return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : [];\n};\n\nfunction arrayClone(arr, n) {\n var copy = new Array(n);\n for (var i = 0; i < n; ++i)\n copy[i] = arr[i];\n return copy;\n}\n\nfunction spliceOne(list, index) {\n for (; index + 1 < list.length; index++)\n list[index] = list[index + 1];\n list.pop();\n}\n\nfunction unwrapListeners(arr) {\n var ret = new Array(arr.length);\n for (var i = 0; i < ret.length; ++i) {\n ret[i] = arr[i].listener || arr[i];\n }\n return ret;\n}\n", "import * as dompack from 'dompack';\nconst EventEmitter = require('events');\n\nclass InternetRequester extends EventEmitter\n{\n constructor(options)\n {\n super();\n if(!options)\n options={};\n //Host url of event server\n this.options = { url: options.url || ''\n , log: Boolean(options.log || dompack.debugflags.rpc)\n , withcredentials: 'withCredentials' in options && options.withcredentials\n };\n\n // XMLHttpRequest\n this.conn = null;\n\n // used for estimating the server date\n this.__date_server = null;\n this.__date_client = null;\n this.__date_diff = null;\n }\n\n destroy()\n {\n this.stopCurrentRequest();\n this.conn = null;\n }\n\n stopCurrentRequest()\n {\n if(this.conn)\n {\n this.conn.onreadystatechange = null;\n this.conn.onloadend = null;\n\n this.conn.abort();\n }\n if (this.jsoncheckinterval)\n {\n clearTimeout(this.jsoncheckinterval);\n this.jsoncheckinterval = null;\n }\n }\n\n ensureConnection()\n {\n if (!this.conn)\n this.conn = new XMLHttpRequest();\n }\n\n startXMLHTTPRequest(method, url, body, options)\n {\n this.ensureConnection();\n\n var async = !options || !options.synchronous;\n\n // Because aborting the connection may result in a readystatechange event (yes, we're looking at you, Titanium's\n // TiNetworkHTTPClient...), we have to reset the have_response flag _after_ aborting the connection, so the response for\n // the previous request isn't used for the new request\n\n this.laststateevent = null; //make sure we don't accidentally cancel the previous request\n this.conn.abort();\n this.have_response = false;\n\n this.conn.open(method.toUpperCase(), url, async);\n if(options && options.headers)\n Object.keys(options.headers).forEach(key => { this.conn.setRequestHeader(key,options.headers[key]); });\n\n if(this.options.withcredentials)\n this.conn.withCredentials = true;\n\n this.conn.onreadystatechange = this.onStateChange.bind(this);\n // Required for Firefox 12 (+firebug?), without it statechange to 4 doesn't seem to be fired sometimes\n this.conn.onloadend = this.onStateChange.bind(this);\n this.conn.onabort = this.onAbort.bind(this);\n\n this.emit(\"requeststart\", { target: this });\n this.conn.send(body);\n\n if (!async)\n this.onStateChange();\n }\n\n onAbort(event)\n {\n if(this.laststateevent)\n this.laststateevent.isaborted = true;\n }\n\n onStateChange (event)\n {\n if (this.conn.readyState != 4 || this.have_response)\n return;\n\n this.have_response = true;\n\n var datestr = this.conn.getResponseHeader(\"date\");\n if (datestr != \"\")\n {\n var parseddate = Date.parse(datestr);\n this.__date_server = parseddate;\n this.__date_client = new Date();\n this.__date_diff = this.__date_server - this.__date_client;\n }\n\n var evt = { target: this\n , success: this.conn.status == 200\n , internalerror: this.conn.status == 500\n , message: this.conn.status\n\n , responsetext: this.conn.responseText\n , responsejson: null\n };\n\n //FIXME only decode JSON data if the mimetype specified it was JSON, and then log any errors\n try\n {\n evt.responsejson = JSON.parse(evt.responsetext);\n }\n catch(e)\n {\n }\n\n this.laststateevent = evt;\n this.emit(\"requestend\", evt);\n }\n}\n\nmodule.exports = InternetRequester;\n", "/** @require: var JSONRPC = require('@mod-system/js/net/jsonrpc')\n*/\nconst InternetRequester = require('./requester');\nimport * as whintegration from '@mod-system/js/wh/integration';\nimport * as dompack from 'dompack';\n\nvar rpcscriptid = Math.floor(Math.random()* 1000);\n\nclass JSONRPC extends InternetRequester\n{\n /** @short RPC status codes (defined as getter-only properties as long as we don't have static const properties) */\n static get HTTP_ERROR() { return -1; } // Error connecting to the RPC server\n static get JSON_ERROR() { return -2; } // The returned value could not be decoded into a JSON object\n static get PROTOCOL_ERROR() { return -3; } // The return object did not contain an id, or the id did not match the request id\n static get RPC_ERROR() { return -4; } // The RPC returned an error\n static get OFFLINE_ERROR() { return -5; } // The application is not online (only returned if the onlineonly option was set)\n static get TIMEOUT_ERROR() { return -6; } // The request could not be sent or was not answered before within the timeout set in the options\n static get SERVER_ERROR() { return -7; } // The server encountered an internal error\n\n constructor(options)\n {\n super(options);\n if(!options)\n options={};\n\n this.lastid = 0;\n this.requestqueue = [];\n this.cachecounter = 0;\n this.activerequest = null;\n this.haveresponse = false;\n //timeout after which we trigger a 'wait' action, eg a spinner\n this.options.waittimeout = 'waittimeout' in options ? options.waittimeout : 500;\n this.options.appendfunctionname = 'appendfunctionname' in options ? options.appendfunctionname : false;\n this.waitcallback = null;\n this.waittimeoutid = null;\n this.waitingnow = false;\n\n this.on(\"requestend\", this.onResponse.bind(this));\n }\n\n destroy()\n {\n super.destroy();\n this.requestqueue = [];\n this.activerequest = null;\n\n if (this.waittimeoutid)\n {\n clearTimeout(this.waittimeoutid);\n this.waittimeoutid = null;\n }\n }\n\n promiseRequest(method, params, options)\n {\n let deferred = dompack.createDeferred();\n let req = this.request(method, params, deferred.resolve, (errorcode, errormsg, rpcid) => { deferred.reject(new Error(errormsg)); }, options);\n deferred.promise.__jsonrpcinfo = { deferred, req };\n return deferred.promise;\n }\n async(method, ...params)\n {\n return this.promiseRequest(method, params);\n }\n\n _doAsyncAbort(promise, result, rejection)\n {\n if(!promise.__jsonrpcinfo)\n throw new Error(\"The promise is not an async JSONRPC request\");\n if(!rejection)\n promise.__jsonrpcinfo.deferred.resolve(result);\n else\n promise.__jsonrpcinfo.deferred.reject(rejection);\n promise.__jsonrpcinfo.req.cancel();\n }\n\n rpcResolve(promise, result)\n {\n this._doAsyncAbort(promise, result);\n }\n rpcReject(promise, rejection)\n {\n this._doAsyncAbort(promise, null, rejection);\n }\n\n\n/**\n * @short Queue an RPC request\n * @param method The RPC method to call\n * @param params Params for the RPC method\n * @param callback The callback which is called, with:\n * param status A JSONRPC. value\n * param result The result object as sent by the RPC, or an error message string sent by the RPC, or an error\n * message\n * param id The request id\n * @param options Options\n * @param options.url The URL to connect to\n * @param options.timeout Timeout in ms after which the request will fail (callback is called with ERROR_TIMEOUT error)\n * @param options.waittimeout Timeout in ms after which the request will set waiting status to TRUE (via the waitCallback)\n * Set negative to not trigger waiting status.\n * @return The request id\n */\n request(method, params, onsuccess, onfailure, options)\n {\n if(!params || typeof params != \"object\" || params.length === undefined)\n throw new Error(\"The parameters passed to request must be an Array\");\n\n var id = ++this.lastid;\n\n var url;\n if(options && options.url)\n url = options.url + (options.appendfunctionname ? (options.url.match(/\\/$/) ? '' : '/') + method : '');\n else if(this.options.url)\n url = this.options.url + (this.options.appendfunctionname ? (this.options.url.match(/\\/$/) ? '' : '/') + method : '');\n else\n url = location.href; //we do not support appendfunctionname for self-posts\n\n var timeout = Math.max((options && typeof options.timeout == \"number\") ? options.timeout : 0, 0);\n var waittimeout = (options && typeof options.waittimeout == \"number\") ? options.waittimeout : this.options.waittimeout;\n var synchronous = options && options.synchronous || false;\n var errortrace = options && options.errortrace || null;\n\n if (this.options.log)\n console.log(\"JSONRPC request\", method, params, options, 'timeout:', timeout, 'waitTimeout:', waittimeout);\n\n var request = new Request(this, id, method, params, url, timeout, waittimeout, onsuccess, onfailure, synchronous, errortrace);\n if (this.options.log || !whintegration.config || !whintegration.config.islive)\n request.stack = new Error().stack;\n\n this.requestqueue.push(request);\n if (this.options.log)\n console.log(\"JSONRPC request is on queue\");\n this.processNextRequest();\n return request;\n }\n\n handleError(onfailure, errorcode, errormsg, rpcid)\n {\n if(onfailure)\n setTimeout( () => onfailure(errorcode, errormsg, rpcid), 0);\n\n setTimeout( () => this.emit([ \"error\", { target: this, errorcode: errorcode, errormessage: errormsg, rpcid: rpcid } ]), 0);\n }\n\n //is a json request pending?\n isRequestPending()\n {\n return this.activerequest !== null || this.requestqueue.length;\n }\n\n //ADDME is it possible for the 'next' response to already be .delay/setTimeout() scheduled, racing against our cancel ?\n __cancelRequest(id)\n {\n if(typeof id != 'number')\n return;\n\n if (this.activerequest == id)\n {\n this.stopCurrentRequest();\n this.activerequest = null;\n\n var request = this.requestqueue.shift();\n if (request.timeout && typeof request.timeout != \"boolean\")\n clearTimeout(request.timeout);\n\n this.processNextRequest();\n }\n else\n {\n for (var i = 0; i < this.requestqueue.length; ++i)\n if (this.requestqueue[i].id == id)\n {\n this.requestqueue.splice(i, 1);\n break;\n }\n }\n }\n\n processNextRequest()\n {\n if (this.activerequest)\n {\n if(this.options.log)\n console.log(\"JSONRPC request #\" + this.activerequest + \" pending, not scheduling a new one yet\");\n this.handleWaitTimeouts();\n return;\n }\n\n var request = null;\n while (!request)\n {\n request = this.requestqueue[0];\n if (!request)\n {\n if(this.options.log)\n console.log(\"JSONRPC request - processNextRequest, queue is empty\");\n return;\n }\n if (request.timeout && typeof request.timeout == \"boolean\")\n {\n this.requestqueue = this.requestqueue.filter(el => el != request);\n request = this.requestqueue[0];\n }\n }\n\n this.activerequest = request.id;\n\n if (request.timeout)\n request.timeout = setTimeout( () => this.onTimeout(request), request.timeout);\n\n if(this.options.log)\n console.log(\"JSONRPC request #\" + request.id + \" offering for XMLHTTP\");\n this.startXMLHTTPRequest(\n \"post\",\n request.url,\n JSON.stringify(request.request),\n { headers: { \"Content-Type\": \"application/json; charset=utf-8\" }\n , synchronous: request.synchronous\n });\n this.handleWaitTimeouts();\n }\n\n onResponse(event)\n {\n this.activerequest = null;\n\n var request = this.requestqueue[0];\n if (!request)\n return;\n\n this.requestqueue = this.requestqueue.slice(1);\n\n if (request.timeout)\n {\n if (typeof request.timeout == \"boolean\")\n {\n this.processNextRequest();\n return;\n }\n clearTimeout(request.timeout);\n }\n\n var status = -1;\n var result = null;\n\n if (!event.success)\n {\n status = JSONRPC.HTTP_ERROR;\n result = \"HTTP Error: \" + event.message;\n\n if (event.internalerror)\n {\n let json = null;\n try\n {\n json = event.responsejson;\n var trace;\n if(json && json.error && json.error.data)\n {\n trace = json.error.data.trace || json.error.data.errors || json.error.data.list || [];\n\n console.group();\n var line = \"RPC #\" + rpcscriptid +\":\"+ request.id + \" failed: \" + json.error.message;\n console.warn(line);\n if (request.errortrace)\n request.errortrace.push(line);\n trace.forEach(rec =>\n {\n if (rec.filename || rec.line)\n {\n var line = rec.filename + '#' + rec.line + '#' + rec.col + (rec.func ? ' (' + rec.func + ')' : '');\n console.warn(line);\n if (request.errortrace)\n request.errortrace.push(line);\n }\n });\n console.groupEnd();\n }\n status = JSONRPC.SERVER_ERROR;\n result = json.error && `${json.error.message} from ${request.url}` || \"Unknown error\";\n }\n catch (e)\n {\n }\n }\n }\n else\n {\n let json = event.responsejson;\n\n if (!json)\n {\n status = JSONRPC.JSON_ERROR;\n result = \"Invalid JSON response\";\n }\n else if (json.id === null || json.id != request.id)\n {\n status = JSONRPC.PROTOCOL_ERROR;\n result = \"Protocol error: invalid id\";\n }\n else if (json.error !== null)\n {\n status = JSONRPC.RPC_ERROR;\n result = json.error;\n if(this.options.log)\n console.log('RPC error:', result.message ? result.message : '*no message*');\n }\n else if (\"result\" in json)\n {\n status = 0;\n result = json.result;\n }\n else\n {\n status = JSONRPC.PROTOCOL_ERROR;\n result = \"Could not interpret response\";\n }\n }\n\n this.processNextRequest();\n\n if (this.options.log)\n {\n console.log(\"JSONRPC request\", request.request.method, 'status:', status, 'time:', (new Date).getTime()- request.scheduled, 'ms, result:');\n console.log(result);\n }\n\n /*\n console.log({ serverdate: this.__date_server\n , clientdate: this.__date_client\n , diff: this.__date_diff\n });\n */\n setTimeout( () => request.__completedCall(status, result, event),0 );\n }\n\n onTimeout(request)\n {\n request.timeout = true;\n if (this.activerequest == request.id)\n {\n this.activerequest = null;\n this.stopCurrentRequest();\n this.processNextRequest();\n }\n this.handleError(request.onfailure, JSONRPC.TIMEOUT_ERROR, \"Timeout while waiting for response\", request.id);\n }\n\n onWaitTimeout()\n {\n this.waittimeoutid = null;\n this.handleWaitTimeouts();\n }\n\n handleWaitTimeouts()\n {\n if (this.waittimeoutid)\n {\n clearTimeout(this.waittimeoutid);\n this.waittimeoutid = null;\n }\n\n if (!this.waitCallback)\n return;\n\n var waiting = false;\n var nextTimeout = -1;\n\n var now = (new Date).getTime();\n for (var i = 0; i < this.requestqueue.length; ++i)\n {\n var req = this.requestqueue[i];\n if (req.waitTimeout >= 0)\n {\n var waitLength = now - req.scheduled;\n\n if (waitLength >= req.waitTimeout)\n waiting = true;\n else\n {\n var toGo = req.waitTimeout - waitLength;\n if (nextTimeout < 0 || nextTimeout > toGo)\n nextTimeout = toGo;\n }\n }\n }\n\n if (this.waitingNow != waiting)\n {\n this.waitingNow = waiting;\n setTimeout( () => this.waitCallback(waiting), 0);\n }\n\n if (nextTimeout >= 0)\n this.waittimeoutid = setTimeout( () => this.onWaitTimeout(), nextTimeout);\n }\n\n getEstimatedServerTime()\n {\n return new Date().getTime() + this.__date_diff;\n }\n\n /** @short estimate the server's datetime based on the known descrepancy between the date of an reponse from the server and the time on the client\n */\n getEstimatedServerDate()\n {\n return new Date(this.getEstimatedServerTime());\n }\n}\n\nclass Request //extends PreloadableAsset\n{\n constructor(parent, id, method, params, url, timeout, waittimeout, onsuccess, onfailure, synchronous, errortrace)\n {\n// super();\n\n this.cancelled = false;\n this.stack = null;\n\n if (parent.options.log)\n console.log('req',this);\n this.parent=parent;\n this.id = id;\n this.request = { id: id\n , method: method\n , params: params || []\n };\n this.url = url;\n this.onsuccess = onsuccess;\n this.onfailure = onfailure;\n this.timeout = timeout;\n this.scheduled = new Date-0;\n this.waittimeout = waittimeout;\n this.synchronous = synchronous;\n this.errortrace = errortrace;\n\n //this.startPreload();\n }\n onStartPreload()\n {\n\n }\n cancel()\n {\n //we need to prevent a race when our parent invokes cancel(), but we actually had our __completedCall already queued up. if we still fire onsuccess/onfailure, our parent might think we completed the _next_ request our parent submitted\n this.cancelled=true;\n this.parent.__cancelRequest(this.id);\n }\n\n __completedCall(status,result,event)\n {\n if(event.isaborted)\n this.cancelled=true;\n\n if(status == 0)\n {\n if(this.onsuccess && !this.cancelled)\n this.onsuccess(result);\n //this.donePreload(true);\n }\n else\n {\n if(!this.cancelled)\n {\n if(this.stack)\n {\n console.log(\"Stack at calling point:\");\n console.log(this.stack);\n }\n this.parent.handleError(this.onfailure, status, result, this.id);\n }\n //this.donePreload(false);\n }\n }\n}\n\nmodule.exports=JSONRPC;\n", "//ADDME move cookie state to sessionstorage, we don't need to transmit _c cookies on each request\n\nimport * as dompack from 'dompack';\nimport * as domcookie from 'dompack/extra/cookie';\nimport * as whintegration from '@mod-system/js/wh/integration';\nimport Keyboard from 'dompack/extra/keyboard';\n\nimport JSONRPC from '@mod-system/js/net/jsonrpc';\n\nvar defaultauth = null;\n\nfunction getBackVar(backurl)\n{\n backurl = backurl.split('/').slice(3).join('/'); //strip origin, make relative to current server\n return backurl ? '?b=' + encodeURIComponent(backurl) : '';\n}\n\nfunction getURLOrigin(url)\n{\n return url.split('/').slice(0,3).join('/');\n}\n\nclass WRDAuthenticationProvider\n{\n constructor(options)\n {\n if(!options)\n options={};\n\n this.cookiename = 'cookiename' in options ? options.cookiename : \"webharelogin\";\n\n this.refresh();\n }\n\n refresh()\n {\n this.isloggedin = false;\n this.userinfo = null;\n this.logouturl = \"\";\n this.loginservice = new JSONRPC( { url: '/wh_services/wrd/auth' });\n\n var jsstate = domcookie.read(this.cookiename + '_j');\n var currentstate = domcookie.read(this.cookiename + '_c');\n\n if(dompack.debugflags.aut)\n {\n console.log(\"[aut] \" + this.cookiename + \"_j=\" + jsstate);\n console.log(\"[aut] \" + this.cookiename + \"_c=\" + currentstate);\n }\n if(!jsstate)\n return;\n\n if(!currentstate || currentstate.substr(0, jsstate.length) != jsstate)\n {\n location.replace('/.wrd/auth/restoresession.shtml' + getBackVar(location.href));\n return;\n }\n else\n {\n if(dompack.debugflags.aut)\n console.log(\"[aut] looks like we're still logged in\");\n\n this.isloggedin = true;\n if(currentstate.length > 1)\n try\n {\n this.userinfo = JSON.parse(currentstate.substr(jsstate.length));\n }\n catch(e)\n {\n }\n }\n }\n\n //Get the current session id - use this if you need to discard settings\n getCurrentSessionId()\n {\n return domcookie.read(this.cookiename + '_j') || '';\n }\n\n logout()\n {\n let backurl = location.href;\n if(this.logouturl)\n {\n let logouturl = new URL(this.logouturl, backurl).toString();\n if(getURLOrigin(backurl) != getURLOrigin(logouturl))\n throw new Error(\"A logout URL is not allowed to change the origin\"); //we won't be an open redirect. and getBackVar will clear the origin anyway\n\n backurl = logouturl;\n }\n\n let redirectto = '/.wrd/auth/logout.shtml' + getBackVar(backurl);\n location.replace(redirectto);\n }\n\n setupLoginForm(form)\n {\n if(!form)\n throw new Error(\"No such form\");\n\n new Keyboard(form, { \"Enter\": evt => this._handleLoginForm(form, evt) });\n form.addEventListener(\"submit\", evt => this._handleLoginForm(form, evt));\n form.addEventListener(\"click\", evt => this._handleLoginClick(form, evt));\n }\n _handleLoginClick(form, event)\n {\n if(event.target.closest('.wh-wrdauth__loginbutton'))\n return this._handleLoginForm(form, event); //will stop the event too\n }\n _handleLoginForm(form, event)\n {\n dompack.stop(event);\n\n var loginfield = form.querySelector('*[name=\"login\"]');\n var passwordfield = form.querySelector('*[name=\"password\"]');\n var persistentfield = form.querySelector('*[name=\"persistent\"]');\n\n if(!loginfield)\n throw new Error(\"No field named 'login' found\");\n if(!passwordfield)\n throw new Error(\"No field named 'password' found\");\n\n var persistentlogin = persistentfield && persistentfield.checked;\n this._tryLogin(form, loginfield.value, passwordfield.value, { persistent: persistentlogin });\n }\n login(login, password, options)\n {\n options = {...options};\n return new Promise( (resolve, reject) =>\n {\n var url = new URL(location.href);\n\n var opts =\n { logincontrol: url.searchParams.get(\"wrdauth_logincontrol\") || \"\"\n };\n\n return this.loginservice.request('Login'\n , [ location.href\n , login\n , password\n , Boolean(options.persistent)\n , opts\n ]\n , function(response)\n { //success handler\n resolve(response);\n }\n , function(error)\n {\n reject(error);//FIXME translate to exception\n }\n );\n });\n }\n\n loginSecondFactor(loginproof, type, data, options)\n {\n return new Promise( (resolve, reject) =>\n {\n var url = new URL(location.href);\n\n var opts =\n { logincontrol: url.searchParams.get(\"wrdauth_logincontrol\") || \"\"\n };\n\n return this.loginservice.request('LoginSecondFactor'\n , [ location.href\n , loginproof\n , type\n , { ...data}\n , opts\n ]\n , function(response)\n { //success handler\n resolve(response);\n }\n , function(error)\n {\n reject(error);//FIXME translate to exception\n }\n );\n });\n }\n\n /** Get the afterlogin submitinstruction from the wrdauth_logincontrol webvariable\n @cell(string) opts.logincontrol Override wrdauth_logincontrol variable from the url\n @return Submit instruction. The defult instruction is { \"type\": \"reload\" }.\n */\n getAfterLoginSubmitInstruction(opts = {})\n {\n const url = new URL(location.href);\n const logincontrol = opts.logincontrol || url.searchParams.get(\"wrdauth_logincontrol\") || \"\";\n\n return new Promise( (resolve, reject) =>\n {\n this.loginservice.request('getAfterLoginSubmitInstruction',\n [ location.href, logincontrol ],\n function(response)\n { //success handler\n resolve(response);\n }\n , function(error)\n {\n reject(error);//FIXME translate to exception\n }\n );\n });\n }\n\n //ADDME do we have direct callers or can we _tryLogin this?\n //FIXME be more wh-form like, at least BEM the 'submitting' class\n _tryLogin(form, login, password, options)\n {\n let loginlock = dompack.flagUIBusy();\n if(form)\n form.classList.add(\"submitting\");\n\n this.login(login, password, options).then( result => this.onLoginSuccess(loginlock, form, result) )\n .catch( error => this._onLoginFailure(loginlock, form, options, error));\n }\n onLoginSuccess(loginlock, form, response)\n {\n if(form)\n form.classList.remove(\"submitting\");\n\n let completion = () => this._completeLoginSuccess(loginlock, response, form);\n dompack.dispatchCustomEvent(form || document.documentElement, 'wh:wrdauth-onlogin',\n { bubbles: true\n , cancelable: true\n , detail: { callback: completion, userinfo: response.userinfo }\n , defaulthandler: completion\n });\n }\n _completeLoginSuccess(loginlock, response, form)\n {\n loginlock.release();\n if(response.success)\n {\n if (response.submitinstruction)\n {\n whintegration.executeSubmitInstruction(response.submitinstruction);\n return;\n }\n\n //The user has succesfully logged in\n location.reload(true);\n return;\n }\n\n this._failLogin(/* FIXME? Locale.get('wh-common.authentication.loginfail') || */'The specified login data is incorrect.', response, form);\n }\n _onLoginFailure(loginlock, form, options, code, msg)\n {\n if(form)\n form.classList.remove(\"submitting\");\n loginlock.release();\n\n this._failLogin(/* FIXME? Locale.get('wh-common.authentication.loginerror') || */'An error has occurred.', { code: code }, form);\n }\n _failLogin(message, response, form)\n {\n let evtdetail = { message: message\n , code: response.code\n , data: response.data\n };\n\n let cancelled = !dompack.dispatchCustomEvent(form || document.documentElement, \"wh:wrdauth-loginfailed\", { bubbles: true, cancelable: true, detail: evtdetail });\n if(!cancelled)\n {\n /*\n if($wh.Popup && $wh.Popup.Dialog)\n new $wh.Popup.Dialog( { text: failevent.message, buttons: [{ result: 'ok', title: \"Ok\" }] });\n else*/\n alert(message);\n }\n }\n isLoggedIn()\n {\n return this.isloggedin;\n }\n getUserInfo()\n {\n return this.userinfo;\n }\n setLogoutURL(url)\n {\n this.logouturl = url;\n }\n\n startLogin(type, sp_tag, options)\n {\n options = options || {};\n var defer = dompack.createDeferred();\n\n this.loginservice.request('StartLogin'\n , [ type, sp_tag, location.href, options ]\n , defer.resolve\n , defer.reject //FIXME translate to exception\n );\n\n return defer.promise;\n }\n startSAMLLogin(sp_tag, options)\n {\n return this.startLogin('saml', sp_tag, options);\n }\n\n //Setup the page with loginstate. automatically invoked on the default auth provider\n setupPage()\n {\n document.documentElement.classList.toggle(\"wh-wrdauth-loggedin\", this.isLoggedIn()); //legacy! will be removed\n document.documentElement.classList.toggle(\"wh-wrdauth--isloggedin\", this.isLoggedIn());\n }\n}\n\nWRDAuthenticationProvider.getDefaultAuth = function()\n{\n return defaultauth;\n};\n\nif(window.$wh && window.$wh.WRDAuthenticationProvider)\n{\n console.log(\"Both designfiles wrd.auth and @mod-wrd/js/auth are loaded. @mod-wrd/js/auth will not activate\");\n}\nelse if(whintegration.config[\"wrd:auth\"])\n{\n defaultauth = new WRDAuthenticationProvider(whintegration.config[\"wrd:auth\"]);\n defaultauth.setupPage();\n\n dompack.register('.wh-wrdauth__logout, .whplugin-wrdauth-logout', node =>\n {\n node.whplugin_processed = true;\n node.addEventListener(\"click\", event =>\n {\n event.stopPropagation();\n event.preventDefault();\n defaultauth.logout();\n });\n });\n dompack.register('.wh-wrdauth__loginform, .whplugin-wrdauth-loginform', node =>\n {\n node.whplugin_processed = true;\n defaultauth.setupLoginForm(node);\n });\n\n if(defaultauth.userinfo)\n {\n dompack.register(\"*[data-wrdauth-text]\", node =>\n {\n var elname = node.dataset.wrdauthText;\n if(elname in defaultauth.userinfo)\n node.textContent = defaultauth.userinfo[elname];\n });\n dompack.register(\"*[data-wrdauth-value]\", node =>\n {\n var elname = node.dataset.wrdauthValue;\n if(elname in defaultauth.userinfo)\n node.value = defaultauth.userinfo[elname];\n });\n }\n}\n\nmodule.exports = WRDAuthenticationProvider;\n", "/** @import: import * as domfocus from 'dompack/browserfix/focus';\n*/\n\nexport function getActiveElement(doc)\n{\n try\n {\n //activeElement can reportedly throw on IE9 and _definately_ on IE11\n return doc.activeElement;\n }\n catch(e)\n {\n return null;\n }\n}\n\nexport function getToplevelWindow()\n{\n let toplevelwindow = window;\n while(toplevelwindow.frameElement)\n toplevelwindow = toplevelwindow.parent;\n return toplevelwindow;\n}\n/** Find the currently focused element\n @param limitwin If set, only return compontents in the specified document (prevents editable iframes from returning subframes) */\nexport function getCurrentlyFocusedElement(limitdoc)\n{\n try\n {\n var focused = getActiveElement(getToplevelWindow().document);\n while(true)\n {\n if (focused.tagName == \"IFRAME\" && (!limitdoc || focused.ownerDocument != limitdoc))\n focused = getActiveElement(focused.contentDocument);\n else\n break;\n }\n if(focused && limitdoc && focused.ownerDocument != limitdoc)\n return null;\n return focused;\n }\n catch(e)\n {\n return null;\n }\n}\n\nfunction isHTMLElement(node)\n{\n return node.nodeType == 1 && typeof node.className == \"string\";\n}\n\nfunction getIframeFocusableNodes(body, currentnode, recurseframes)\n{\n //ADDME force body into list?\n var subnodes = [];\n try\n {\n const body = (currentnode.contentDocument || currentnode.contentWindow.document).body;\n if (body.isContentEditable)\n return subnodes;\n\n subnodes = getFocusableComponents(body, recurseframes);\n }\n catch (e)\n {\n console.log(\"failed to descend into iframe\",e);\n }\n\n return subnodes;\n}\n\n// whether the node is reachable for focus by keyboard navigation\n// (because tabIndex == -1 will be seen a non(keyboard)focusable by this function)\nexport function canFocusTo(node) //returns if a -visible- node is focusable (this function does not check for visibility itself)\n{\n try\n {\n if(node.nodeType != 1)\n return false;\n if(node.contentEditable === \"true\")\n return true;\n\n // http://dev.w3.org/html5/spec-preview/editing.html#focusable\n if(node.tabIndex == -1) //explicitly disabled\n return false;\n\n return (parseInt(node.getAttribute('tabIndex')) >= 0) //we cannot read the property tabIndex directly, as IE <= 8 will return '0' even if the tabIndex is missing\n || ([\"A\",\"LINK\"].includes(node.nodeName) && node.href)\n || (!node.disabled && ([\"BUTTON\",\"SELECT\",\"TEXTAREA\",\"COMMAND\"].includes(node.nodeName)\n || (node.nodeName==\"INPUT\" && node.type != \"hidden\")));\n }\n catch(e)\n {\n return false; //the code above may fail eg on IE11 if it's a Flash object that'ss still loading\n }\n}\n\n\nexport function getFocusableComponents(startnode, recurseframes)\n{\n var focusable=[];\n for(var currentnode=startnode.firstChild;currentnode;currentnode=currentnode.nextSibling) //can't use element.getChildren, startnode may be document\n {\n if(!isHTMLElement(currentnode))\n {\n //if(currentnode.getStyle) console.log(\"getFocusableComponents skipping\",currentnode, $(currentnode).getStyle(\"display\"), currentnode.getStyle(\"visibility\"))\n continue;\n }\n\n // Get current style (avoid mootools due to cross-frame issues)\n var currentstyle = getComputedStyle(currentnode);\n if (!currentstyle || currentstyle.display == \"none\" || currentstyle.visibility == \"hidden\")\n {\n //if(currentnode.getStyle) console.log(\"getFocusableComponents skipping\",currentnode, $(currentnode).getStyle(\"display\"), currentnode.getStyle(\"visibility\"))\n continue;\n }\n\n if(recurseframes && currentnode.nodeName == \"IFRAME\") //might contain more things to focus\n {\n const subnodes = getIframeFocusableNodes(currentnode, currentnode, recurseframes);\n if(subnodes.length)\n focusable=focusable.concat(subnodes);\n }\n else if(canFocusTo(currentnode))\n {\n focusable.push(currentnode);\n }\n\n if (currentnode.isContentEditable)\n continue;\n\n const subnodes = getFocusableComponents(currentnode, recurseframes);\n if(subnodes.length)\n focusable = focusable.concat(subnodes);\n }\n return focusable;\n}\n\nexport function getAllFocusableComponents()\n{\n return getFocusableComponents(getToplevelWindow().document, true);\n}\n", "// WARNING: This file is loaded by both webpack (babel) and nodejs code and\n// should avoid babel-only features not yet supported by nodejs\n\n// import * as texttype from 'dompack/types/text';\n\nexport function encodeTextNode(str)\n{\n return str.split('&').join('&')\n .split('<').join('<')\n .split('>').join('>');\n}\n\nexport function encodeValue(str)\n{\n return str.split('&').join('&')\n .split('<').join('<')\n .split('>').join('>')\n .split('\"').join('"')\n .split(\"'\").join(''');\n}\n\nexport function decodeValue(str)\n{\n return str.replace(/ /g, \"\\n\")\n .replace(/(\\d+);/g, (match, dec) => String.fromCharCode(dec))\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\")\n .replace(/&/g, \"&\");\n}\n\nexport function encodeJSCompatibleJSON(s)\n{\n return JSON.stringify(s).replace(/\\u2028/g, '\\\\u2028').replace(/\\u2029/g, '\\\\u2029');\n}\n", "import * as encoding from \"dompack/types/text.es\";\nimport * as domdebug from \"dompack/src/debug.es\";\nimport * as wh from \"@mod-system/js/wh/integration.es\";\n\n/*\nSupported debug flags:\n gtd Debug get(Rich)Tid\n*/\n\nlet allTids = {};\nlet curLang = \"\";\n\nfunction encodeHTML(input)\n{\n return input.split('&').join('&')\n .split('<').join('<')\n .split('>').join('>')\n .split('\\n').join(' ');\n}\n\nfunction executeCompiledTidText(text, params, rich)\n{\n if(typeof text == \"object\" && !Array.isArray(text))\n text = text?.[\"\"];\n if (text == null)\n return text;\n if(typeof text == \"string\")\n return rich ? encodeHTML(text) : text;\n\n let output = '';\n for(let tok of text)\n {\n if(typeof tok == \"string\")\n {\n output += rich ? encodeHTML(tok) : tok;\n }\n else if (typeof tok == \"number\")\n {\n if (tok >= 1)\n {\n let get_param = params?.[tok-1];\n if(get_param)\n {\n output += rich ? encodeHTML(get_param) : get_param;\n }\n }\n }\n else if(tok.t == \"tag\")\n {\n let sub = executeCompiledTidText(tok.subs, params, rich);\n output += rich ? `<${tok.tag}>${sub}${tok.tag}>` : sub;\n }\n else if(tok.t == \"ifparam\")\n {\n let get_param = params?.[tok.p-1] || '';\n output += executeCompiledTidText(get_param.toUpperCase() == tok.value.toUpperCase() ? tok.subs : tok.subselse, params, rich);\n }\n else if(tok.t == \"a\")\n {\n let sub = executeCompiledTidText(tok.subs, params, rich);\n if(rich)\n {\n let link = tok.link;\n if(tok.linkparam > 0 && tok.linkparam <= params.length)\n link = params[tok.linkparam - 1];\n if(link)\n output += `${sub}`;\n else\n output += sub;\n }\n else\n {\n output += sub;\n }\n }\n }\n return output;\n}\n\nfunction resolveTid(tid, params, options)\n{\n if(curLang=='debug')\n {\n return '{' + tid + (params.length ? '|' + params.join('|') : '') + '}';\n }\n\n // Make sure we have 4 string params\n for (let i = 0; i < 4; ++i)\n if (params.length == i)\n params.push(\"\");\n else if (typeof params[i] == \"number\")\n params[i] = \"\" + params[i];\n else if (!params[i])\n params[i] = \"\";\n params = params.slice(0, 4);\n\n // Initialize text with the 'cannot find text' message\n let text = domdebug.debugflags.sut ? \".\" + tid.split(\".\").pop() : \"(cannot find text:\" + tid + \")\";\n\n // Check if the module is defined\n let module = tid.substr(0, tid.indexOf(\":\"));\n if (!module || !(module in allTids))\n {\n if (!wh.config.islive || domdebug.debugflags.gtd)\n console.warn(\"No language texts found for module '\" + module + \"'\");\n return /*cannot find*/ text;\n }\n\n let language = options?.overridelanguage || getTidLanguage();\n if (!(language in allTids[module]))\n {\n if (!wh.config.islive || domdebug.debugflags.gtd)\n console.warn(\"No language texts found for language '\" + language + \"'\");\n return /*cannot find*/ text;\n }\n\n try\n {\n if (domdebug.debugflags.gtd)\n {\n console.group(`Resolving tid '${tid}'`);\n console.info(tid, params, options, language);\n }\n\n // Dig into the module gid structure\n let context = allTids[module][language];\n tid = tid.substr(module.length + 1);\n if (!tid.split(\".\").every(part =>\n {\n let found = part in context;\n if (found)\n context = context[part];\n else if (domdebug.debugflags.gtd)\n console.warn(\"Subpart '\"+ part + \"' not found\");\n\n return found; // If not found, break 'every' loop\n }))\n {\n return /*cannot find*/ text;\n }\n\n const executed = executeCompiledTidText(context, params, options?.html);\n if (executed == null)\n {\n if (domdebug.debugflags.gtd)\n console.warn(`Tid '${module}:${tid}'' is a group node`);\n return /*cannot find*/ text;\n }\n if (domdebug.debugflags.gtd)\n console.info(\"getTid\", `${module}:${tid}`, params, executed);\n\n return executed;\n }\n finally\n {\n if (domdebug.debugflags.gtd)\n console.groupEnd();\n }\n}\n\nfunction getTid(tid, p1, p2, p3, p4)\n{\n return resolveTid(tid, Array.prototype.slice.call(arguments, 1));\n}\n\nfunction getHTMLTid(tid, p1, p2, p3, p4)\n{\n return resolveTid(tid, Array.prototype.slice.call(arguments, 1), { html: true });\n}\n\nfunction getTidLanguage()\n{\n if (curLang)\n return curLang;\n\n // Read the document's language, if there is a DOM context\n if (typeof document != \"undefined\")\n curLang = (document.documentElement.lang||'').substr(0,2);\n\n return curLang;\n}\n\nfunction setTidLanguage(lang)\n{\n curLang = lang;\n}\n\nfunction tidMerge(readContext, writeContext)\n{\n for (let key of Object.keys(readContext))\n {\n if (typeof readContext[key] != \"object\" || Array.isArray(readContext[key])) //a leaf, safe to copy\n {\n writeContext[key] = readContext[key];\n }\n else\n {\n if (!(key in writeContext))\n writeContext[key] = {};\n tidMerge(readContext[key], writeContext[key]);\n }\n }\n}\n\nfunction registerTexts(module, language, tids)\n{\n if (!(module in allTids))\n {\n allTids[module] = {};\n }\n if (!(language in allTids[module]))\n {\n allTids[module][language] = tids;\n return;\n }\n tidMerge(tids, allTids[module][language]);\n}\n\n// Fill nodes with a data-texttid attribute with the translated text\nfunction convertElementTids(scope = document.body)\n{\n // Only available in a DOM context and if the DOM is ready\n if (typeof document == \"undefined\" || !scope)\n return;\n Array.from(scope.querySelectorAll(\"*[data-texttid]\")).forEach(function(node)\n {\n node.textContent = getTid(node.getAttribute(\"data-texttid\"));\n });\n}\n\n// If this script is run within a DOM context, convert data-texttid attributes automatically\nif (typeof document != \"undefined\")\n document.addEventListener(\"DOMContentLoaded\", event => convertElementTids());\n\n\n// Define 'tidLanguage' as a property on the main export (so you can use getTid.tidLanguage)\nObject.defineProperty(getTid, \"tidLanguage\", { get: getTidLanguage, set: setTidLanguage });\n// Define 'html' as a method on the main export (so you can use getTid.html)\ngetTid.html = getHTMLTid;\n\n// Export getTid as the default function, explicitly export getTid, getHTMLTid and registerTexts as well\nexport { getTid as default\n , getTid\n , getTidLanguage\n , getHTMLTid\n , convertElementTids\n , registerTexts\n };\n", "// Auto-generated language file from /opt/wh/whtree/modules/tollium/web/ui/common.lang.json\nvar registerTexts = require(\"@mod-tollium/js/gettid\").registerTexts;\nregisterTexts(\"tollium\",\"en\",{\"common\":{\"actions\":{\"about\":\"About\",\"about_app\":[\"About \",1],\"add\":\"Add\",\"addasportlet\":\"Add as Portlet\",\"addtostartmenu\":\"Show in Favorites\",\"apply\":\"Apply\",\"browse\":\"Browse\",\"cancel\":\"Cancel\",\"change\":\"Change\",\"clear\":\"Clear\",\"close\":\"Close\",\"columns\":\"Select Columns\",\"connect\":\"Connect\",\"copy\":\"Copy\",\"copyto\":\"Copy To\",\"cut\":\"Cut\",\"delete\":\"Delete\",\"disable\":\"Disable\",\"disconnect\":\"Disconnect\",\"down\":\"Down\",\"download\":\"Download\",\"duplicate\":\"Duplicate\",\"edit\":\"Edit\",\"enable\":\"Enable\",\"exit\":\"Exit\",\"export\":\"Export\",\"finish\":\"Finish\",\"help\":\"Help\",\"import\":\"Import\",\"inspect\":\"Inspect\",\"invertselection\":\"Invert Selection\",\"link\":\"Link to\",\"logoff\":\"Logoff\",\"menu\":\"Menu\",\"moveto\":\"Move To\",\"new\":\"New\",\"newinstance\":\"New Instance\",\"next\":\"Next\",\"no\":\"No\",\"ok\":\"OK\",\"open\":\"Open\",\"ordering\":\"Set ordering\",\"paste\":\"Paste\",\"preferences\":\"Preferences\",\"previous\":\"Previous\",\"print\":\"Print\",\"properties\":\"Properties\",\"redo\":\"Redo\",\"refresh\":\"Refresh\",\"rename\":\"Rename\",\"replace\":\"Replace\",\"reset\":\"Reset\",\"save\":\"Save\",\"saveas\":\"Save As\",\"search\":\"Search\",\"select\":\"Select\",\"selectall\":\"Select All\",\"settings\":\"Settings\",\"startmenuhint\":\"Open the Start Menu\",\"synchronize\":\"Synchronize\",\"tobottom\":\"To bottom\",\"today\":\"Today\",\"totop\":\"To top\",\"undo\":\"Undo\",\"up\":\"Up\",\"upload\":\"Upload\",\"view\":\"View\",\"yes\":\"Yes\",\"yestoall\":\"Yes to All\"},\"errors\":{\"databasereadonly\":\"The database is currently read-only and changes cannot be made. Please try again later.\",\"field_required\":[\"Field '\",1,\"' is a required field\"],\"image_toosmall\":[\"Field '\",1,\"' must contain an image of at least \",2,\" - it is \",3,\".\"],\"invalid_date\":[\"Field '\",1,\"' contains an invalid date\"],\"invalid_float\":[\"Field '\",1,\"' must contain a valid value\"],\"invalid_hostname\":[\"Field '\",1,\"' must contain a hostname (eg 'myserver.example.net')\"],\"invalid_integer\":[\"Field '\",1,\"' must contain a valid numeric value\"],\"invalid_money\":[\"Field '\",1,\"' must contain a valid value\"],\"invalid_time\":[\"Field '\",1,\"' contains an invalid time\"],\"invalid_year\":[\"Field '\",1,\"' contains an invalid year; you need to enter at least 4 digits (prepend with zeroes for years before the year 1000, eg. 0954)\"],\"passwordmismatch\":\"The entered passwords do not match\",\"tagedit_unprocessed_text\":[\"Field '\",1,\"' contains a tag that has not yet been committed\"],\"too_long\":[\"Field '\",1,\"' may not contain more than \",2,\" characters\"],\"too_many_decimals\":[\"Field '\",1,\"' may not contain \",{\"p\":2,\"subs\":[\"any decimals\"],\"subselse\":[\"more than \",2,\" \",{\"p\":2,\"subs\":[\"decimal\"],\"subselse\":[\"decimals\"],\"t\":\"ifparam\",\"value\":\"1\"}],\"t\":\"ifparam\",\"value\":\"0\"}],\"too_short\":[\"Field '\",1,\"' must contain \",2,\" or more characters\"],\"upload_allowsuspiciousfile\":\"Do you want to continue this upload?\",\"upload_blockedsuspiciousfile\":\"Contact the system operator if you still need to upload this file.\",\"upload_cannotcheck\":\"We are currently unable to verify this file.\",\"upload_suspicious\":[\"There appears to be an issue with this file (\",1,\") and it cannot be uploaded.\"],\"value_out_of_range\":[\"Field '\",1,\"' contains an invalid value; you need to enter a value \",{\"p\":2,\"subs\":[\"of at most \",3],\"subselse\":[{\"p\":3,\"subs\":[\"of at least \",2],\"subselse\":[\"between \",2,\" and \",3],\"t\":\"ifparam\",\"value\":\"\"}],\"t\":\"ifparam\",\"value\":\"\"}]},\"examples\":{\"email\":\"email@example.com\",\"emailcomma\":\"email@example.com, another@example.net, ...\",\"http-url\":\"http://www.example.com/folder/\",\"https-url\":\"https://www.example.com/folder/\",\"urls\":[\"https://www.example.com/folder/\",\"\\n\",\"http://www.anotherexample.com/\"]},\"labels\":{\"bksp\":\"Backspace\",\"creationdate\":\"Creation date\",\"del\":\"Del\",\"deletiondate\":\"Deletion date\",\"description\":\"Description\",\"down\":\"Down\",\"email\":\"Email\",\"end\":\"End\",\"enter\":\"Enter\",\"esc\":\"Escape\",\"home\":\"Home\",\"id\":\"ID\",\"keywords\":\"Keywords\",\"left\":\"Left\",\"modificationdate\":\"Modification date\",\"name\":\"Name\",\"no\":\"No\",\"none\":\"None\",\"ordering\":\"Ordering\",\"pgdn\":\"Page Down\",\"pgup\":\"Page Up\",\"right\":\"Right\",\"status\":\"Status\",\"subject\":\"Subject\",\"tab\":\"Tab\",\"tag\":\"Tag\",\"title\":\"Title\",\"type\":\"Type\",\"up\":\"Up\",\"yes\":\"Yes\"},\"languages\":{\"aa\":\"Afar\",\"ab\":\"Abkhaz\",\"ae\":\"Avestan\",\"af\":\"Afrikaans\",\"ak\":\"Akan\",\"am\":\"Amharic\",\"an\":\"Aragonese\",\"ar\":\"Arabic\",\"as\":\"Assamese\",\"av\":\"Avaric\",\"ay\":\"Aymara\",\"az\":\"Azerbaijani\",\"ba\":\"Bashkir\",\"be\":\"Belarusian\",\"bg\":\"Bulgarian\",\"bh\":\"Bihari\",\"bi\":\"Bislama\",\"bm\":\"Bambara\",\"bn\":\"Bengali\",\"bo\":\"Tibetan\",\"br\":\"Breton\",\"bs\":\"Bosnian\",\"ca\":\"Catalan\",\"ce\":\"Chechen\",\"ch\":\"Chamorro\",\"co\":\"Corsican\",\"cr\":\"Cree\",\"cs\":\"Czech\",\"cu\":\"Old Church Slavonic\",\"cv\":\"Chuvash\",\"cy\":\"Welsh\",\"da\":\"Danish\",\"de\":\"German\",\"dv\":\"Divehi\",\"dz\":\"Dzongkha\",\"ee\":\"Ewe\",\"el\":\"Greek\",\"en\":\"English\",\"eo\":\"Esperanto\",\"es\":\"Spanish\",\"et\":\"Estonian\",\"eu\":\"Basque\",\"fa\":\"Persian\",\"ff\":\"Fula\",\"fi\":\"Finnish\",\"fj\":\"Fijian\",\"fo\":\"Faroese\",\"fr\":\"French\",\"fy\":\"Western Frisian\",\"ga\":\"Irish\",\"gd\":\"Gaelic\",\"gl\":\"Galician\",\"gn\":\"Guaran\u00ED\",\"gu\":\"Gujarati\",\"gv\":\"Manx\",\"ha\":\"Hausa\",\"he\":\"Hebrew\",\"hi\":\"Hindi\",\"ho\":\"Hiri Motu\",\"hr\":\"Croatian\",\"ht\":\"Haitian\",\"hu\":\"Hungarian\",\"hy\":\"Armenian\",\"hz\":\"Herero\",\"ia\":\"Interlingua\",\"id\":\"Indonesian\",\"ie\":\"Interlingue\",\"ig\":\"Igbo\",\"ii\":\"Nuosu\",\"ik\":\"Inupiaq\",\"io\":\"Ido\",\"is\":\"Icelandic\",\"it\":\"Italian\",\"iu\":\"Inuktitut\",\"ja\":\"Japanese\",\"jv\":\"Javanese\",\"ka\":\"Georgian\",\"kg\":\"Kongo\",\"ki\":\"Kikuyu\",\"kj\":\"Kwanyama\",\"kk\":\"Kazakh\",\"kl\":\"Kalaallisut\",\"km\":\"Khmer\",\"kn\":\"Kannada\",\"ko\":\"Korean\",\"kr\":\"Kanuri\",\"ks\":\"Kashmiri\",\"ku\":\"Kurdish\",\"kv\":\"Komi\",\"kw\":\"Cornish\",\"ky\":\"Kyrgyz\",\"la\":\"Latin\",\"lb\":\"Luxembourgish\",\"lg\":\"Ganda\",\"li\":\"Limburgish\",\"ln\":\"Lingala\",\"lo\":\"Lao\",\"lt\":\"Lithuanian\",\"lu\":\"Luba-Katanga\",\"lv\":\"Latvian\",\"mg\":\"Malagasy\",\"mh\":\"Marshallese\",\"mi\":\"Maori\",\"mk\":\"Macedonian\",\"ml\":\"Malayalam\",\"mn\":\"Mongolian\",\"mo\":\"Moldovan\",\"mr\":\"Marathi\",\"ms\":\"Malay\",\"mt\":\"Maltese\",\"my\":\"Burmese\",\"na\":\"Nauru\",\"nb\":\"Norwegian Bokmal\",\"nd\":\"North Ndebele\",\"ne\":\"Nepali\",\"ng\":\"Ndonga\",\"nl\":\"Dutch\",\"nn\":\"Norwegian Nynorsk\",\"no\":\"Norwegian\",\"nr\":\"South Ndebele\",\"nv\":\"Navajo\",\"ny\":\"Chichewa\",\"oc\":\"Occitan\",\"oj\":\"Ojibwe\",\"om\":\"Oromo\",\"or\":\"Oriya\",\"os\":\"Ossetian\",\"pa\":\"Panjabi\",\"pi\":\"Pali\",\"pl\":\"Polish\",\"ps\":\"Pashto, Pushto\",\"pt\":\"Portuguese\",\"qu\":\"Quechua\",\"rm\":\"Romansh\",\"rn\":\"Kirundi\",\"ro\":\"Romanian\",\"ru\":\"Russian\",\"rw\":\"Kinyarwanda\",\"sa\":\"Sanskrit\",\"sc\":\"Sardinian\",\"sd\":\"Sindhi\",\"se\":\"Northern Sami\",\"sg\":\"Sango\",\"sh\":\"Serbo-Croatian\",\"si\":\"Sinhala\",\"sk\":\"Slovak\",\"sl\":\"Slovene\",\"sm\":\"Samoan\",\"sn\":\"Shona\",\"so\":\"Somali\",\"sq\":\"Albanian\",\"sr\":\"Serbian\",\"ss\":\"Swati\",\"st\":\"Southern Sotho\",\"su\":\"Sundanese\",\"sv\":\"Swedish\",\"sw\":\"Swahili\",\"ta\":\"Tamil\",\"te\":\"Telugu\",\"tg\":\"Tajik\",\"th\":\"Thai\",\"ti\":\"Tigrinya\",\"tk\":\"Turkmen\",\"tl\":\"Tagalog\",\"tn\":\"Tswana\",\"to\":\"Tonga\",\"tr\":\"Turkish\",\"ts\":\"Tsonga\",\"tt\":\"Tatar\",\"tw\":\"Twi\",\"ty\":\"Tahitian\",\"ug\":\"Uyghur\",\"uk\":\"Ukrainian\",\"ur\":\"Urdu\",\"uz\":\"Uzbek\",\"ve\":\"Venda\",\"vi\":\"Vietnamese\",\"vo\":\"Volap\u00FCk\",\"wa\":\"Walloon\",\"wo\":\"Wolof\",\"xh\":\"Xhosa\",\"yi\":\"Yiddish\",\"yo\":\"Yoruba\",\"za\":\"Zhuang\",\"zh\":\"Chinese\",\"zu\":\"Zulu\"},\"menus\":{\"edit\":\"Edit\",\"file\":\"File\",\"help\":\"Help\",\"new\":\"New\",\"tools\":\"Tools\",\"view\":\"View\"},\"messages\":{\"dontaskagain\":\"Remember my choice\",\"dontshowagain\":\"Do not show me this message again\",\"searchnoresults\":[\"No results were found when searching for '\",1,\"'.\"]},\"richstyles\":{\"blocktext\":\"Block Text\",\"bodytext\":\"Body Text\",\"bodytext2\":\"Body Text 2\",\"bodytext3\":\"Body Text 3\",\"bodytextfirstindent\":\"Body Text First Indent\",\"bodytextfirstindent2\":\"Body Text First Indent 2\",\"bodytextindent\":\"Body Text Indent\",\"bodytextindent2\":\"Body Text Indent 2\",\"bodytextindent3\":\"Body Text Indent 3\",\"caption\":\"Caption\",\"closing\":\"Closing\",\"commentreference\":\"Comment Reference\",\"commenttext\":\"Comment Text\",\"date\":\"Date\",\"defaultparagraphfont\":\"Default Paragraph Font\",\"documentmap\":\"Document Map\",\"emphasis\":\"Emphasis\",\"endnotereference\":\"Endnote Reference\",\"endnotetext\":\"Endnote Text\",\"envelopeaddress\":\"Envelope Address\",\"envelopereturn\":\"Envelope Return\",\"followedhyperlink\":\"Followed Hyperlink\",\"footer\":\"Footer\",\"footnotereference\":\"Footnote Reference\",\"footnotetext\":\"Footnote Text\",\"header\":\"Header\",\"heading1\":\"Heading 1\",\"heading2\":\"Heading 2\",\"heading3\":\"Heading 3\",\"heading4\":\"Heading 4\",\"heading5\":\"Heading 5\",\"heading6\":\"Heading 6\",\"heading7\":\"Heading 7\",\"heading8\":\"Heading 8\",\"heading9\":\"Heading 9\",\"hyperlink\":\"Hyperlink\",\"index1\":\"Index 1\",\"index2\":\"Index 2\",\"index3\":\"Index 3\",\"index4\":\"Index 4\",\"index5\":\"Index 5\",\"index6\":\"Index 6\",\"index7\":\"Index 7\",\"index8\":\"Index 8\",\"index9\":\"Index 9\",\"indexheading\":\"Index Heading\",\"linenumber\":\"Line Number\",\"list\":\"List\",\"list2\":\"List 2\",\"list3\":\"List 3\",\"list4\":\"List 4\",\"list5\":\"List 5\",\"listbullet\":\"List Bullet\",\"listbullet2\":\"List Bullet 2\",\"listbullet3\":\"List Bullet 3\",\"listbullet4\":\"List Bullet 4\",\"listbullet5\":\"List Bullet 5\",\"listcontinue\":\"List Continue\",\"listcontinue2\":\"List Continue 2\",\"listcontinue3\":\"List Continue 3\",\"listcontinue4\":\"List Continue 4\",\"listcontinue5\":\"List Continue 5\",\"listnumber\":\"List Number\",\"listnumber2\":\"List Number 2\",\"listnumber3\":\"List Number 3\",\"listnumber4\":\"List Number 4\",\"listnumber5\":\"List Number 5\",\"macrotext\":\"Macro Text\",\"messageheader\":\"Message Header\",\"normal\":\"Normal\",\"normalindent\":\"Normal Indent\",\"noteheadiong\":\"Note Heading\",\"ordered\":\"Ordered list\",\"pagenumber\":\"Page Number\",\"plaintext\":\"Plain Text\",\"salutation\":\"Salutation\",\"signature\":\"Signature\",\"strong\":\"Strong\",\"subtitle\":\"Subtitle\",\"tableofauthorities\":\"Table of Authorities\",\"tableoffigures\":\"Table of Figures\",\"title\":\"Title\",\"toaheading\":\"TOA Heading\",\"toc1\":\"TOC 1\",\"toc2\":\"TOC 2\",\"toc3\":\"TOC 3\",\"toc4\":\"TOC 4\",\"toc5\":\"TOC 5\",\"toc6\":\"TOC 6\",\"toc7\":\"TOC 7\",\"toc8\":\"TOC 8\",\"toc9\":\"TOC 9\",\"unordered\":\"Unordered list\"},\"units\":{\"centuries\":\"centuries\",\"century\":\"century\",\"day\":\"day\",\"days\":\"days\",\"hour\":\"hour\",\"hours\":\"hours\",\"millisecond\":\"millisecond\",\"milliseconds\":\"milliseconds\",\"minute\":\"minute\",\"minutes\":\"minutes\",\"month\":\"month\",\"months\":\"months\",\"second\":\"second\",\"seconds\":\"seconds\",\"week\":\"week\",\"weeks\":\"weeks\",\"year\":\"year\",\"years\":\"years\"}},\"shell\":{\"checks\":{\"errors\":[{\"p\":1,\"subs\":[2],\"subselse\":[\"'\",2,\"' and \",1,\" more \",{\"p\":1,\"subs\":[\"error\"],\"subselse\":[\"errors\"],\"t\":\"ifparam\",\"value\":\"1\"}],\"t\":\"ifparam\",\"value\":\"0\"}],\"unresolvedissues\":\"There are unresolved issues!\"},\"controller\":{\"neededrights\":[\"The following rights are needed: \",1],\"rightsproblem\":[\"You don't have access to this application (logged in as '\",1,\"')\"],\"sessionexpired\":\"Your login session has expired. Please log in again to restart this application.\",\"untrustedparams\":\"You are not allowed to open this application with additional URL parameters.\"},\"dashboard\":{\"apptitle\":\"WebHare\",\"connect-success\":\"Received new WebHare connect-helper settings\",\"connect-title\":\"WebHare connect\",\"logout\":\"Log out\",\"mountwebhare\":\"Mount server over WebDav\",\"noapps\":[\"No match for '\",1,\"'\"],\"resetimagecache\":\"Reset image cache\",\"showallapps\":\"Show all apps\"},\"editfavorites\":\"Manage\",\"errors\":{\"appstartfailed\":\"Unable to start the application. The server may be unreachable or you may need to login again\",\"appstartfailed-development\":[\"Unable to start the application. The server may be unreachable or you may need to login again\",\"\\n\",\"\\n\",\"You may also need to softreset WebHare (wh softreset) or inspect the log files for issues (wh watchlog)\"],\"debug\":\"Debug\",\"encounterederror\":\"The application encountered an error and has terminated.\",\"errordialogtitle\":\"Application\",\"errortitle\":\"An error took place, please contact the system operator.\",\"restart\":\"Restart\"},\"feedback\":{\"button-general\":\"General\",\"button-specific\":\"Specific\",\"message\":\"Do you want to submit feedback about a specific interface element or general feedback?\",\"title\":\"Feedback\"},\"frontendclose\":\"Application sessions have expired\",\"frontendclose_description\":\"The connection between your browser and WebHare was broken. The impacted applications have been closed.\",\"loadingapp\":\"Starting application...\",\"login\":{\"apptitle\":\"WebHare\",\"cancel\":\"Cancel\",\"closedlogin\":\"It's currently not possible to login. Please try again later.\",\"disabledlogin\":\"Login failed: this user account is disabled\",\"enterauthenticatorcode\":\"Please enter your one-time code\",\"enterbackupcode\":\"Too many incorrect codes entered, please enter a backup code\",\"enterusernameandpassword\":\"Please enter your username and password.\",\"forgotpassword\":\"Forgot password\",\"genericerror\":\"An error occurred. Please try again, if the error keeps occurring please contact your system administrator.\",\"infotitle\":\"Important information\",\"invaliddata\":\"Your login session has expired, please login again\",\"invalidlogin\":\"Invalid username and/or password.\",\"loginbutton\":\"Login\",\"loginidentityservices\":\"Login using identity services\",\"loginsecondfactor\":\"Second factor login\",\"logintitle\":\"Login\",\"loginwithwebhareaccount\":\"Login with a WebHare account\",\"musteditauthsettings\":\"Your authentication settings must be updated before proceeding.\",\"notloggedin\":\"You no longer seem to be logged in. You may need to reload this webpage to relogin.\",\"nowebhareaccount\":\"No corresponding WebHare account found. Please contact your system administrator.\",\"password\":\"Password\",\"savelogin\":\"Remember me\",\"secondfactorauthentication\":\"Second factor login\",\"totpattemptsleft\":[\"Please enter your one-time code (you have \",1,\" attempts left)\"],\"totpcode\":\"One-time login code\",\"totpinvalidcode\":\"This code is not valid\",\"totplocked\":\"You have entered too many invalid codes, you will have to enter a backup code to login in\",\"totpreusedcode\":\"This code has already been used to log in, please wait until the next code before trying again\",\"unexpectedprotocolversion\":[\"It looks like your browser is running an outdated version of this webpage. Please try Shift-F5 (or Shift-Cmd-R on a Mac or Alt-Cmd-R in Safari) to reload this page.\",\"\\n\",\"\\n\",\"If that doesn't work please try restarting your browser.\"],\"username\":\"Username\"},\"logout\":{\"surelogout\":\"Are you sure you want to logout?\",\"title\":\"Log out\"},\"messagebox\":{\"defaulttitle\":\"Message\"},\"nowebhareconnect\":\"WebHare connect unavailable\",\"nowebhareconnect_description\":[\"You have enabled WebHare connect for this server, but the service could not be contacted on \",1],\"oauth\":{\"apptitle\":\"OAuth authorization\",\"clientid\":\"Client\",\"errortitle\":\"An error has occurred\",\"explanation\":\"The following server wants to get access to this server:\",\"messages\":{\"invalid_redirect\":\"An invalid redirect URL has been specified (must lie within the client URL.)\",\"missing_client\":\"No client has been specified.\",\"missing_redirect\":\"No redirect URL has been specified.\",\"missing_scopes\":\"No scopes have been specified.\",\"unknownclient\":[\"Client '\",1,\"' has not been registered in Webhare Connect.\"],\"unknownerror\":\"An unknown error has occurred. Please try again, and if this error keeps occurring please contact the system administrator.\",\"webhareconnecterror\":\"Could not connect to Webhare Connect. Please contact your system administrator if this problem persists.\"},\"oauthtitle\":\"Authorization\",\"question\":\"The requesting server will have access to everything your account has access to on this server. Are you sure you want to grant this access?\",\"scopes\":\"Scopes\"},\"offline\":\"Connection lost\",\"offline_description\":\"The connection to the server has been lost. Please wait for it to recover.\",\"openedas\":[\"Opened as '\",1,\"'\"],\"personalmenu\":\"Favorites\",\"restartapp\":\"Restart application\",\"towl\":{\"gonativedescription\":\"Click here to show notifications on your desktop.\",\"gonativetitle\":\"Desktop notifications\",\"notificationtitle\":\"WebHare notification\"},\"upload\":{\"messages\":{\"errortitle\":\"An error occurred\",\"unknownerror\":\"Something went wrong during uploading. Please try again.\"},\"progress\":{\"calculating\":\"Calculating...\",\"progress\":\"Progress\",\"size\":\"Size\",\"speed\":\"Speed\",\"title\":\"Uploading\"}},\"webhareupdated\":\"WebHare updated\",\"webhareupdated_description\":\"The webpage restarted because the WebHare server received an update\"}});\nregisterTexts(\"tollium\",\"nl\",{\"common\":{\"actions\":{\"about\":\"Over\",\"about_app\":[\"Over \",1],\"add\":\"Toevoegen\",\"addasportlet\":\"Toon als portlet\",\"addtostartmenu\":\"Toon in favorieten\",\"apply\":\"Toepassen\",\"browse\":\"Bladeren\",\"cancel\":\"Annuleren\",\"change\":\"Aanpassen\",\"clear\":\"Leegmaken\",\"close\":\"Sluiten\",\"columns\":\"Kolommen instellen\",\"connect\":\"Verbinden\",\"copy\":\"Kopi\u00EBren\",\"copyto\":\"Kopi\u00EBren naar\",\"cut\":\"Knippen\",\"delete\":\"Verwijderen\",\"disable\":\"Uitschakelen\",\"disconnect\":\"Verbreken\",\"down\":\"Omlaag\",\"download\":\"Downloaden\",\"duplicate\":\"Dupliceren\",\"edit\":\"Bewerken\",\"enable\":\"Inschakelen\",\"exit\":\"Afsluiten\",\"export\":\"Exporteren\",\"finish\":\"Voltooien\",\"help\":\"Help\",\"import\":\"Importeren\",\"inspect\":\"Inspecteren\",\"invertselection\":\"Selectie omkeren\",\"link\":\"Koppel aan\",\"logoff\":\"Uitloggen\",\"menu\":\"Menu\",\"moveto\":\"Verplaatsen naar\",\"new\":\"Nieuw\",\"newinstance\":\"Nieuwe instantie\",\"next\":\"Volgende\",\"no\":\"Nee\",\"ok\":\"OK\",\"open\":\"Openen\",\"ordering\":\"Volgorde instellen\",\"paste\":\"Plakken\",\"preferences\":\"Voorkeuren\",\"previous\":\"Vorige\",\"print\":\"Afdrukken\",\"properties\":\"Eigenschappen\",\"redo\":\"Opnieuw\",\"refresh\":\"Verversen\",\"rename\":\"Hernoemen\",\"replace\":\"Vervangen\",\"reset\":\"Herstellen\",\"save\":\"Opslaan\",\"saveas\":\"Opslaan als\",\"search\":\"Zoeken\",\"select\":\"Selecteren\",\"selectall\":\"Alles selecteren\",\"settings\":\"Instellingen\",\"startmenuhint\":\"Open het Start menu\",\"synchronize\":\"Synchroniseren\",\"tobottom\":\"Onderaan\",\"today\":\"Vandaag\",\"totop\":\"Bovenaan\",\"undo\":\"Ongedaan maken\",\"up\":\"Omhoog\",\"upload\":\"Uploaden\",\"view\":\"Bekijken\",\"yes\":\"Ja\",\"yestoall\":\"Ja op alles\"},\"errors\":{\"databasereadonly\":\"De database is in alleen-lezen mode en daardoor kunnen er nu geen wijzigingen worden gemaakt. Probeer het later nog eens.\",\"field_required\":[\"Het veld '\",1,\"' dient ingevuld te worden\"],\"image_toosmall\":[\"Het veld '\",1,\"' dient een afbeelding te bevatten van minimaal \",2,\" - is nu \",3,\".\"],\"invalid_date\":[\"Het veld '\",1,\"' bevat een ongeldige datum\"],\"invalid_float\":[\"Het veld '\",1,\"' dient een geldige waarde te bevatten\"],\"invalid_hostname\":[\"Het veld '\",1,\"' dient een hostname te bevatten (bv 'myserver.example.net')\"],\"invalid_integer\":[\"Het veld '\",1,\"' dient een geldige numerieke waarde te bevatten\"],\"invalid_money\":[\"Het veld '\",1,\"' dient een geldige waarde te bevatten\"],\"invalid_time\":[\"Het veld '\",1,\"' bevat een ongeldig tijdstip\"],\"invalid_year\":[\"Het veld '\",1,\"' bevat een ongeldig jaar; u dient ten minste 4 cijfers in te vullen (voeg nullen aan het begin van het getal toe voor jaren voor het jaar 1000, bijvoorbeeld 0954)\"],\"passwordmismatch\":\"De ingevoerde wachtwoorden komen niet overeen\",\"tagedit_unprocessed_text\":[\"Het veld '\",1,\"' bevat een tag die nog moet worden opgeslagen\"],\"too_long\":[\"Het veld '\",1,\"' mag niet meer dan \",2,\" karakters bevatten\"],\"too_many_decimals\":[\"Het veld '\",1,\"' mag \",{\"p\":2,\"subs\":[\"geen decimalen\"],\"subselse\":[\"niet meer dan \",2,\" \",{\"p\":2,\"subs\":[\"decimaal\"],\"subselse\":[\"decimalen\"],\"t\":\"ifparam\",\"value\":\"1\"}],\"t\":\"ifparam\",\"value\":\"0\"},\" bevatten\"],\"too_short\":[\"Het veld '\",1,\"' moet \",2,\" of meer karakters bevatten\"],\"upload_allowsuspiciousfile\":\"Wil u deze upload toch toestaan?\",\"upload_blockedsuspiciousfile\":\"Neem contact op met uw systeembeheerder als u dit bestand toch moet uploaden.\",\"upload_cannotcheck\":\"Dit bestand kan momenteel niet gecontroleerd worden.\",\"upload_suspicious\":[\"Er lijkt een probleem met dit bestand te zijn (\",1,\") en het kan daardoor niet geupload worden\"],\"value_out_of_range\":[\"Het veld '\",1,\"' bevat een ongeldige waarde; u dient een waarde \",{\"p\":2,\"subs\":[\"van maximaal \",3],\"subselse\":[{\"p\":3,\"subs\":[\"van ten minste \",2],\"subselse\":[\"tussen \",2,\" en \",3],\"t\":\"ifparam\",\"value\":\"\"}],\"t\":\"ifparam\",\"value\":\"\"},\" in te vullen\"]},\"examples\":{\"email\":\"email@voorbeeld.nl\",\"emailcomma\":\"email@example.nl, anders@example.nl, ...\",\"http-url\":\"http://www.example.nl/map/\",\"https-url\":\"https://www.example.nl/map/\",\"urls\":[\"https://www.example.nl/map/\",\"\\n\",\"http://www.anotherexample.com/\"]},\"labels\":{\"bksp\":\"Backspace\",\"creationdate\":\"Aanmaakdatum\",\"del\":\"Del\",\"deletiondate\":\"Verwijderdatum\",\"description\":\"Omschrijving\",\"down\":\"Omlaag\",\"email\":\"E-mailadres\",\"end\":\"End\",\"enter\":\"Enter\",\"esc\":\"Escape\",\"home\":\"Home\",\"id\":\"ID\",\"keywords\":\"Sleutelwoorden\",\"left\":\"Links\",\"modificationdate\":\"Wijzigingsdatum\",\"name\":\"Naam\",\"no\":\"Nee\",\"none\":\"Geen\",\"ordering\":\"Ordening\",\"pgdn\":\"Page Down\",\"pgup\":\"Page Up\",\"right\":\"Rechts\",\"status\":\"Status\",\"subject\":\"Onderwerp\",\"tab\":\"Tab\",\"tag\":\"Tag\",\"title\":\"Titel\",\"type\":\"Type\",\"up\":\"Omhoog\",\"yes\":\"Ja\"},\"languages\":{\"aa\":\"Afar\",\"ab\":\"Abchazisch\",\"ae\":\"Avestisch\",\"af\":\"Afrikaans\",\"ak\":\"Akan\",\"am\":\"Amhaars\",\"an\":\"Aragonees\",\"ar\":\"Arabisch\",\"as\":\"Assamees\",\"av\":\"Avaars\",\"ay\":\"Aymara\",\"az\":\"Azerbeidzjaans\",\"ba\":\"Basjkiers\",\"be\":\"Wit-Russisch\",\"bg\":\"Bulgaars\",\"bh\":\"Bihari\",\"bi\":\"Bislama\",\"bm\":\"Bambara\",\"bn\":\"Bengaals\",\"bo\":\"Tibetaans\",\"br\":\"Bretons\",\"bs\":\"Bosnisch\",\"ca\":\"Catalaans\",\"ce\":\"Tsjetsjeens\",\"ch\":\"Chamorro\",\"co\":\"Corsicaans\",\"cr\":\"Cree\",\"cs\":\"Tsjechisch\",\"cu\":\"Kerkslavisch\",\"cv\":\"Tsjoevasjisch\",\"cy\":\"Welsh\",\"da\":\"Deens\",\"de\":\"Duits\",\"dv\":\"Divehi\",\"dz\":\"Dzongkha\",\"ee\":\"Ewe\",\"el\":\"Grieks\",\"en\":\"Engels\",\"eo\":\"Esperanto\",\"es\":\"Spaans\",\"et\":\"Estisch\",\"eu\":\"Baskisch\",\"fa\":\"Perzisch\",\"ff\":\"Fula\",\"fi\":\"Fins\",\"fj\":\"Fijisch\",\"fo\":\"Faer\u00F6ers\",\"fr\":\"Frans\",\"fy\":\"Fries\",\"ga\":\"Iers-Gaelisch\",\"gd\":\"Schots-Gaelisch\",\"gl\":\"Galicisch\",\"gn\":\"Guaran\u00ED\",\"gu\":\"Gujarati\",\"gv\":\"Manx-Gaelisch\",\"ha\":\"Hausa\",\"he\":\"Hebreeuws\",\"hi\":\"Hindi\",\"ho\":\"Hiri Motu\",\"hr\":\"Kroatisch\",\"ht\":\"Krey\u00F2l\",\"hu\":\"Hongaars\",\"hy\":\"Armeens\",\"hz\":\"Herero\",\"ia\":\"Interlingua\",\"id\":\"Indonesisch\",\"ie\":\"Interlingue\",\"ig\":\"Igbo\",\"ii\":\"Yi\",\"ik\":\"Inupiak\",\"io\":\"Ido\",\"is\":\"IJslands\",\"it\":\"Italiaans\",\"iu\":\"Inuktitut\",\"ja\":\"Japans\",\"jv\":\"Javaans\",\"ka\":\"Georgisch\",\"kg\":\"Kikongo\",\"ki\":\"Gikuyu\",\"kj\":\"Kwanyama\",\"kk\":\"Kazachs\",\"kl\":\"Groenlands\",\"km\":\"Khmer\",\"kn\":\"Kannada\",\"ko\":\"Koreaans\",\"kr\":\"Kanuri\",\"ks\":\"Kasjmiri\",\"ku\":\"Koerdisch\",\"kv\":\"Zurjeens\",\"kw\":\"Cornisch\",\"ky\":\"Kirgizisch\",\"la\":\"Latijn\",\"lb\":\"Luxemburgs\",\"lg\":\"Luganda\",\"li\":\"Limburgs\",\"ln\":\"Lingala\",\"lo\":\"Laotiaans\",\"lt\":\"Litouws\",\"lu\":\"Luba-Katanga\",\"lv\":\"Lets\",\"mg\":\"Plateaumalagasi\",\"mh\":\"Marshallees\",\"mi\":\"Maori\",\"mk\":\"Macedonisch\",\"ml\":\"Malayalam\",\"mn\":\"Mongools\",\"mo\":\"Moldavisch\",\"mr\":\"Marathi\",\"ms\":\"Maleis\",\"mt\":\"Maltees\",\"my\":\"Birmaans\",\"na\":\"Nauruaans\",\"nb\":\"Norwegian Bokmal\",\"nd\":\"Noord-Ndebele\",\"ne\":\"Nepalees\",\"ng\":\"Ndonga\",\"nl\":\"Nederlands\",\"nn\":\"Nynorsk\",\"no\":\"Noors\",\"nr\":\"Zuid-Ndebele\",\"nv\":\"Navajo\",\"ny\":\"Nyanja\",\"oc\":\"Occitaans\",\"oj\":\"Ojibweg\",\"om\":\"Afaan Oromo\",\"or\":\"Oriya\",\"os\":\"Ossetisch\",\"pa\":\"Punjabi\",\"pi\":\"Pali\",\"pl\":\"Pools\",\"ps\":\"Pasjtoe\",\"pt\":\"Portugees\",\"qu\":\"Quechua\",\"rm\":\"Reto-Romaans\",\"rn\":\"Kirundi\",\"ro\":\"Roemeens\",\"ru\":\"Russisch\",\"rw\":\"Kinyarwanda\",\"sa\":\"Sanskriet\",\"sc\":\"Sardijns\",\"sd\":\"Sindhi\",\"se\":\"Noord-Samisch\",\"sg\":\"Sangho\",\"sh\":\"Servo-Kroatisch\",\"si\":\"Singalees\",\"sk\":\"Slowaaks\",\"sl\":\"Sloveens\",\"sm\":\"Samoaans\",\"sn\":\"Shona\",\"so\":\"Somalisch\",\"sq\":\"Albanees\",\"sr\":\"Servisch\",\"ss\":\"Swazi\",\"st\":\"Zuid-Sotho\",\"su\":\"Soendanees\",\"sv\":\"Zweeds\",\"sw\":\"Swahili\",\"ta\":\"Tamil\",\"te\":\"Telugu\",\"tg\":\"Tadzjieks\",\"th\":\"Thai\",\"ti\":\"Tigrinya\",\"tk\":\"Turkmeens\",\"tl\":\"Tagalog\",\"tn\":\"Tswana\",\"to\":\"Tongaans\",\"tr\":\"Turks\",\"ts\":\"Tsonga\",\"tt\":\"Tataars\",\"tw\":\"Twi\",\"ty\":\"Tahitiaans\",\"ug\":\"Oeigoers\",\"uk\":\"Oekra\u00EFens\",\"ur\":\"Urdu\",\"uz\":\"Oezbeeks\",\"ve\":\"Venda\",\"vi\":\"Vietnamees\",\"vo\":\"Volap\u00FCk\",\"wa\":\"Waals\",\"wo\":\"Wolof\",\"xh\":\"Xhosa\",\"yi\":\"Jiddisch\",\"yo\":\"Yoruba\",\"za\":\"Zhuang\",\"zh\":\"Chinees\",\"zu\":\"Zoeloe\"},\"menus\":{\"edit\":\"Bewerken\",\"file\":\"Bestand\",\"help\":\"Help\",\"new\":\"Nieuw\",\"tools\":\"Extra\",\"view\":\"Beeld\"},\"messages\":{\"dontaskagain\":\"Onthoud mijn keuze\",\"dontshowagain\":\"Laat deze melding niet meer zien\",\"searchnoresults\":[\"Geen resultaten gevonden bij het zoeken naar '\",1,\"'.\"]},\"richstyles\":{\"blocktext\":\"Block Text\",\"bodytext\":\"Body Text\",\"bodytext2\":\"Body Text 2\",\"bodytext3\":\"Body Text 3\",\"bodytextfirstindent\":\"Body Text First Indent\",\"bodytextfirstindent2\":\"Body Text First Indent 2\",\"bodytextindent\":\"Body Text Indent\",\"bodytextindent2\":\"Body Text Indent 2\",\"bodytextindent3\":\"Body Text Indent 3\",\"caption\":\"Caption\",\"closing\":\"Closing\",\"commentreference\":\"Comment Reference\",\"commenttext\":\"Comment Text\",\"date\":\"Date\",\"defaultparagraphfont\":\"Default Paragraph Font\",\"documentmap\":\"Document Map\",\"emphasis\":\"Emphasis\",\"endnotereference\":\"Endnote Reference\",\"endnotetext\":\"Endnote Text\",\"envelopeaddress\":\"Envelope Address\",\"envelopereturn\":\"Envelope Return\",\"followedhyperlink\":\"Followed Hyperlink\",\"footer\":\"Footer\",\"footnotereference\":\"Footnote Reference\",\"footnotetext\":\"Footnote Text\",\"header\":\"Header\",\"heading1\":\"Kop 1\",\"heading2\":\"Kop 2\",\"heading3\":\"Kop 3\",\"heading4\":\"Kop 4\",\"heading5\":\"Kop 5\",\"heading6\":\"Kop 6\",\"heading7\":\"Kop 7\",\"heading8\":\"Kop 8\",\"heading9\":\"Kop 9\",\"hyperlink\":\"Hyperlink\",\"index1\":\"Index 1\",\"index2\":\"Index 2\",\"index3\":\"Index 3\",\"index4\":\"Index 4\",\"index5\":\"Index 5\",\"index6\":\"Index 6\",\"index7\":\"Index 7\",\"index8\":\"Index 8\",\"index9\":\"Index 9\",\"indexheading\":\"Index Heading\",\"linenumber\":\"Line Number\",\"list\":\"List\",\"list2\":\"List 2\",\"list3\":\"List 3\",\"list4\":\"List 4\",\"list5\":\"List 5\",\"listbullet\":\"List Bullet\",\"listbullet2\":\"List Bullet 2\",\"listbullet3\":\"List Bullet 3\",\"listbullet4\":\"List Bullet 4\",\"listbullet5\":\"List Bullet 5\",\"listcontinue\":\"List Continue\",\"listcontinue2\":\"List Continue 2\",\"listcontinue3\":\"List Continue 3\",\"listcontinue4\":\"List Continue 4\",\"listcontinue5\":\"List Continue 5\",\"listnumber\":\"List Number\",\"listnumber2\":\"List Number 2\",\"listnumber3\":\"List Number 3\",\"listnumber4\":\"List Number 4\",\"listnumber5\":\"List Number 5\",\"macrotext\":\"Macro Text\",\"messageheader\":\"Message Header\",\"normal\":\"Normaal\",\"normalindent\":\"Normal Indent\",\"noteheadiong\":\"Note Heading\",\"ordered\":\"Lijst genummerd\",\"pagenumber\":\"Page Number\",\"plaintext\":\"Plain Text\",\"salutation\":\"Salutation\",\"signature\":\"Signature\",\"strong\":\"Strong\",\"subtitle\":\"Subtitle\",\"tableofauthorities\":\"Table of Authorities\",\"tableoffigures\":\"Table of Figures\",\"title\":\"Title\",\"toaheading\":\"TOA Heading\",\"toc1\":\"TOC 1\",\"toc2\":\"TOC 2\",\"toc3\":\"TOC 3\",\"toc4\":\"TOC 4\",\"toc5\":\"TOC 5\",\"toc6\":\"TOC 6\",\"toc7\":\"TOC 7\",\"toc8\":\"TOC 8\",\"toc9\":\"TOC 9\",\"unordered\":\"Lijst opsom.teken\"},\"units\":{\"centuries\":\"eeuwen\",\"century\":\"eeuw\",\"day\":\"dag\",\"days\":\"dagen\",\"hour\":\"uur\",\"hours\":\"uren\",\"millisecond\":\"milliseconde\",\"milliseconds\":\"milliseconden\",\"minute\":\"minuut\",\"minutes\":\"minuten\",\"month\":\"maand\",\"months\":\"maanden\",\"second\":\"seconde\",\"seconds\":\"seconden\",\"week\":\"week\",\"weeks\":\"weken\",\"year\":\"jaar\",\"years\":\"jaar\"}},\"shell\":{\"checks\":{\"errors\":[{\"p\":1,\"subs\":[2],\"subselse\":[\"'\",2,\"' en nog \",{\"p\":1,\"subs\":[\"1 fout\"],\"subselse\":[1,\" fouten\"],\"t\":\"ifparam\",\"value\":\"1\"}],\"t\":\"ifparam\",\"value\":\"0\"}],\"unresolvedissues\":\"Er zijn nog onopgeloste fouten!\"},\"controller\":{\"neededrights\":[\"De volgende rechten zijn benodigd: \",1],\"rightsproblem\":[\"U heeft geen toegang tot deze applicatie (ingelogd als '\",1,\"')\"],\"sessionexpired\":\"Uw inlogsessie is verlopen. U moet opnieuw inloggen in om deze applicatie te herstarten.\",\"untrustedparams\":\"U mag deze applicatie niet openen met URL parameters.\"},\"dashboard\":{\"apptitle\":\"WebHare\",\"connect-success\":\"Nieuwe WebHare connect-helper instellingen ontvangen\",\"connect-title\":\"WebHare connect\",\"logout\":\"Uitloggen\",\"mountwebhare\":\"Server via WebDav mounten\",\"noapps\":[\"Niets gevonden voor '\",1,\"'\"],\"resetimagecache\":\"Afbeeldingscache leegmaken\",\"showallapps\":\"Toon alle applicaties\"},\"editfavorites\":\"Beheren\",\"errors\":{\"appstartfailed\":\"Het is niet gelukt de applicatie op te starten. Mogelijk is de server onbereikbaar of dient u opnieuw in te loggen\",\"appstartfailed-development\":[\"Het is niet gelukt de applicatie op te starten. Mogelijk is de server onbereikbaar of dient u opnieuw in te loggen\",\"\\n\",\"\\n\",\"Mogelijk moet u ook WebHare softresetten (wh softreset) of de logbestanden controleren op mogelijke problemen (wh watchlog)\"],\"debug\":\"Debuggen\",\"encounterederror\":\"De applicatie is tegen een fout aangelopen en is afgesloten.\",\"errordialogtitle\":\"Applicatie\",\"errortitle\":\"Er heeft een fout plaatsgevonden, neem a.u.b contact op met de systeembeheerder.\",\"restart\":\"Herstarten\"},\"feedback\":{\"button-general\":\"Algemeen\",\"button-specific\":\"Specifiek\",\"message\":\"Wilt u feedback versturen voor een specifiek interface-element of algemene feedback?\",\"title\":\"Feedback\"},\"frontendclose\":\"Applicatiesessies zijn verlopen\",\"frontendclose_description\":\"De verbinding tussen uw browser en WebHare is verbroken. De betreffende applicaties in WebHare zijn gesloten.\",\"loadingapp\":\"Applicatie starten...\",\"login\":{\"apptitle\":\"WebHare\",\"cancel\":\"Annuleren\",\"closedlogin\":\"Het is momenteel niet mogelijk in te loggen. Probeer het later nog eens.\",\"disabledlogin\":\"Inloggen mislukt: dit account is niet actief\",\"enterauthenticatorcode\":\"Voer uw inlogcode in\",\"enterbackupcode\":\"Te veel incorrecte codes ingevoerd, voer een backup code in\",\"enterusernameandpassword\":\"Vult u a.u.b. uw gebruikersnaam en wachtwoord in.\",\"forgotpassword\":\"Wachtwoord vergeten\",\"genericerror\":\"Er is een fout opgetreden, probeer het a.u.b. opnieuw. Als de fout blijft optreden, neem dan contact op met uw systeembeheerder.\",\"infotitle\":\"Belangrijke informatie\",\"invaliddata\":\"Uw inlogsessie is verlopen, log a.u.b. opnieuw in\",\"invalidlogin\":\"U heeft een ongeldige gebruikersnaam en/of wachtwoord ingevoerd. Probeert u het a.u.b. opnieuw.\",\"loginbutton\":\"Inloggen\",\"loginidentityservices\":\"Inloggen met identiteitsservices\",\"loginsecondfactor\":\"Tweede factor inlog\",\"logintitle\":\"Inloggen\",\"loginwithwebhareaccount\":\"Inloggen met een WebHare account\",\"musteditauthsettings\":\"Uw authenticatie-instellingen moeten worden aangepast voordat u verder gaat.\",\"notloggedin\":\"U lijkt niet meer ingelogd te zijn. Mogelijk moet u deze pagina opnieuw inladen om weer in te loggen.\",\"nowebhareaccount\":\"Er is geen gekoppeld WebHare account gevonden. Neem contact op met uw systeembeheerder.\",\"password\":\"Wachtwoord\",\"savelogin\":\"Onthoud mij\",\"secondfactorauthentication\":\"Tweede factor inlog\",\"totpattemptsleft\":[\"Voer uw inlogcode in (u heeft nog \",1,\" pogingen over)\"],\"totpcode\":\"Eenmalige toegangscode\",\"totpinvalidcode\":\"Dit is geen geldige code\",\"totplocked\":\"Te veel ongeldige codes ingevoerd, u moet nu een backup code invoeren om in te loggen\",\"totpreusedcode\":\"Deze code is al gebruikt om in te loggen, wacht tot de volgende code beschikbaar is voordat u het opnieuw probeert.\",\"unexpectedprotocolversion\":[\"Het lijkt erop dat uw browser een verouderde versie van deze webpagina heeft ingeladen. Probeer Shift-F5 (of Shift-Cmd-R op een Mac of Alt-Cmd-R in Safari) om deze pagina opnieuw in te laden.\",\"\\n\",\"\\n\",\"Mocht dit niet werken probeer dan uw browser opnieuw te starten.\"],\"username\":\"Gebruikersnaam\"},\"logout\":{\"surelogout\":\"Weet u zeker dat u wilt uitloggen?\",\"title\":\"Uitloggen\"},\"messagebox\":{\"defaulttitle\":\"Bericht\"},\"nowebhareconnect\":\"WebHare connect niet beschikbaar\",\"nowebhareconnect_description\":[\"U heeft WebHare connect ingeschakeld voor deze server, maar de service kon niet bereikt worden op \",1],\"oauth\":{\"apptitle\":\"OAuth autorisatie\",\"clientid\":\"Client\",\"errortitle\":\"Er is een fout opgetreden\",\"explanation\":\"De volgende server wil toegang tot deze server verkrijgen:\",\"messages\":{\"invalid_redirect\":\"Er is een ongeldige redirect-URL opgegeven (deze moet binnen de client-URL vallen.)\",\"missing_client\":\"Er is geen client opgegeven.\",\"missing_redirect\":\"Er is geen redirect-URL opgegeven.\",\"missing_scopes\":\"Er zijn geen scopes opgegeven.\",\"unknownclient\":[\"Client '\",1,\"' is niet in Webhare Connect geregistreerd.\"],\"unknownerror\":\"Er is een onbekende fout opgetreden. Probeer het a.u.b. opnieuw. Als deze foutmelding dan weer terugkomt, neem dan a.u.b contact op met de systembeheerder.\",\"webhareconnecterror\":\"Kon niet met Webhare Connect verbinden. Neem a.u.b. contact op met systeembeheerder als dit probleem blijft voorkomen.\"},\"oauthtitle\":\"Autorisatie\",\"question\":\"De vragende server zal rechten hebben op alles wat uw account op deze server mag. Weet u zeker dat u deze toegang wil verlenen?\",\"scopes\":\"Scopes\"},\"offline\":\"Verbinding verbroken\",\"offline_description\":\"De verbinding met de server is verbroken. Wacht a.u.b. tot deze weer hersteld is.\",\"openedas\":[\"Geopend als '\",1,\"'\"],\"personalmenu\":\"Favorieten\",\"restartapp\":\"Applicatie herstarten\",\"towl\":{\"gonativedescription\":\"Klik hier om de notificaties weer te geven op uw desktop.\",\"gonativetitle\":\"Desktop notificaties\",\"notificationtitle\":\"WebHare-melding\"},\"upload\":{\"messages\":{\"errortitle\":\"Fout bij uploaden\",\"unknownerror\":\"Er is een fout opgetreden tijdens het uploaden. Probeer het alstublieft opnieuw.\"},\"progress\":{\"calculating\":\"Berekenen...\",\"progress\":\"Voortgang\",\"size\":\"Grootte\",\"speed\":\"Snelheid\",\"title\":\"Uploaden\"}},\"webhareupdated\":\"WebHare bijgewerkt\",\"webhareupdated_description\":\"De webpagina is opnieuw gestart omdat de WebHare server een update ontvangen heeft\"}});\n// Adding dependency: /opt/wh/whtree/modules/tollium/language/default.xml\n// Adding dependency: /opt/wh/whtree/modules/tollium/language/nl.xml\n// Adding dependency: /opt/wh/whtree/modules/tollium/language/default.xml\n", "import * as dompack from 'dompack';\n\nrequire(\"../common.lang.json\");\n\n/****************************************************************************************************************************\n * *\n * SUPPORT FUNCTIONS *\n * *\n ****************************************************************************************************************************/\n\n/* Log messages only when the given target is enabled. A user can specify targets in the URL by using the hash bookmark (e.g.\n ?$tolliumdebug=true#dimensions,communication will show messages with targets 'dimensions' and 'communication'). Currently\n the following targets are used:\n - dimensions (calculating component sizes)\n - rpc (show the individual components/messages being rfcd)\n - communication (communication between the web application and the server)\n - actionenabler (how the enabled state of actions is determined using enableons)\n - messages (the messages, updates and components being processed by the screens)\n - ui (generic UI stuff, eg focus handling)\n - all (show all messages)\n*/\n\nlet enabledlogtypes = [];\n\nvar $todd = {};\n\nexport const gridlineTopMargin = 2; // pixels to add to the top of a grid line\nexport const gridlineBottomMargin = 3; // pixels to add to the bottom of a grid line\nexport const gridlineTotalMargin = gridlineTopMargin + gridlineBottomMargin;\nexport const gridlineHeight = 28; //grid vertical size (28 pixels) including margins\nexport const gridlineInnerHeight = gridlineHeight - gridlineTotalMargin;\nexport const gridlineSnapMax = 8; //never add more than this amount of pixels to snap. an attempt to prevent inlineblocks from wildly generating empty space. this is mostly manually tuning and maybe we shouldn't do it\n\n//workaround not having fully switched to export yet:\n$todd.gridlineTopMargin = gridlineTopMargin;\n$todd.gridlineBottomMargin = gridlineBottomMargin;\n$todd.gridlineTotalMargin = gridlineTotalMargin;\n$todd.gridlineHeight = gridlineHeight;\n$todd.gridlineInnerHeight = gridlineInnerHeight;\n$todd.gridlineSnapMax = gridlineSnapMax;\n\n$todd.settings =\n{ tab_stacked_vpadding_inactive: 1 // border-bottom: 1px (only for inactive!)\n, textedit_defaultwidth: 150\n, list_column_padding: 8 // 2x4 padding\n, list_column_minwidth: 24 // minimum width for an icon (16) + 2x4 padding\n, gridline_topmargin: gridlineTopMargin\n, gridline_bottommargin: gridlineBottomMargin // pixels to add to the top of a grid line\n, grid_vsize: gridlineHeight //grid vertical size (28 pixels) including margins\n, tabspace_vsize: 32 //vertical size inside the tab-space layout\n\n//size of spacers in a sync with apps.scss. SYNC-SPACERS/SYNC-SPACERS-DEBUG\n, spacer_top: 10\n, spacer_bottom: 10\n, spacer_left: 10\n, spacer_right: 10\n//margin between line components. SYNC-SPACERWIDTH\n, spacerwidth: 4\n//size of spacers in a sync with apps.scss. SYNC-BORDERS\n, border_top: 1\n, border_bottom: 1\n, border_left: 1\n, border_right: 1\n\n, listview_padleft: 8\n, listview_padright: 8\n, listview_checkboxholder_width: 20\n, listview_expanderholder_width: 12 //\n, listview_iconholder_width: 20 // iconholder (image 16px + margin 4px)\n\n, fullscreen_maxx: 0.9 //maximum fraction of x width to use for fullscreen windows\n, fullscreen_maxy: 1.0 //maximum fraction of y height to use for fullscreen windows\n\n, buttonheight_intoolbar: 72\n, buttonheight_intabsspace: 27\n\n};\n\n\n$todd.applications = [];\n$todd.applicationstack = [];\n$todd.resourcebase = \"\";\n$todd.customactions = {};\n$todd.dummyimage = null;\n\n$todd.intolerant = window.location.href.indexOf('intolerant=1') != -1;\n$todd.fastunload= window.location.href.indexOf('fastunload=1') != -1;\n\n$todd.getActiveApplication = function()\n{\n return $todd.applicationstack.slice(-1)[0];\n};\n\n/****************************************************************************************************************************\n * Text functions\n */\n\n\n// Replaces \"{param_[n]}\" with p[n] in str for n in [1, 4]\n$todd.FormatString = function(str, p1, p2, p3, p4)\n{\n return str.substitute({ param_1: p1, param_2: p2, param_3: p3, param_4: p4 });\n};\n\n\n/****************************************************************************************************************************\n * Layout\n */\n\n$todd.textsize = { cache: {}\n , node: null\n , styles: { \"font-size\": \"\"\n , \"font-style\": \"\"\n , \"font-weight\": \"\"\n , \"text-decoration\": \"\"\n }\n };\n\n$todd.UpToGridsize = function (size, gridsize)\n{\n if(!gridsize || gridsize<=1)\n return size;\n\n var remainder = size % gridsize;\n if(remainder !== 0)\n size += gridsize - remainder;\n return size;\n};\n\n\n\n$todd.ResetCachedTextSizes = function()\n{\n $todd.textsize.cache = {};\n};\n\n$todd.GetCalculateTextStyles = function()\n{\n return Object.keys($todd.textsize.styles);\n};\n\n$todd.CalculateSize=function(node)\n{\n if (!$todd.calcsizenode)\n {\n $todd.calcsizenode = dompack.create(\"div\", { style: { \"backgroundColor\": \"#ffffff\"\n , color: \"#000000\"\n , position: \"absolute\"\n , visibility: \"hidden\" // Comment this out for debugging\n , width: '1px' //Encourage content collapsing (shrink-wrap)\n }});\n dompack.qS('#todd-measurements').appendChild($todd.calcsizenode);\n }\n $todd.calcsizenode.appendChild(node);\n var size = node.getBoundingClientRect();\n node.remove();\n return { x: Math.ceil(size.width), y: Math.ceil(size.height) };\n};\n\n// text: string with text to calculate size for\n// width: maximum width in pixels for wrapping text, or 0 for no wrapping\n// styles: getStyle-compatible object with font/text settings\n$todd.CalculateTextSize = function(text, width, styles, ishtml)\n{\n if (!$todd.textsize.node)\n {\n $todd.textsize.node = dompack.create(\"div\", { style: { \"backgroundColor\": \"#ffffff\"\n , color: \"#000000\"\n , position: \"absolute\"\n , visibility: \"hidden\" // Comment this out for debugging\n }\n });\n dompack.qS('#todd-measurements').appendChild($todd.textsize.node);\n }\n\n if (typeof (text) != \"string\")\n text = \"\";\n if (typeof (width) != \"number\")\n width = 0;\n\n // Apply only the sanctioned styles\n var applystyles = $todd.textsize.styles;\n if (typeof (styles) == \"object\")\n {\n // merge modifies the first argument, so clone it first\n applystyles = { ...applystyles\n };\n // take the subset of styles we seem to care about\n $todd.GetCalculateTextStyles().forEach(subsetstyle =>\n {\n if(subsetstyle in styles)\n applystyles[subsetstyle] = styles[subsetstyle];\n });\n }\n\n // Check if we have calculated this before\n var key = encodeURIComponent(text) + \"\\t\" + width + \"\\t\" + JSON.stringify(applystyles) + \"\\t\" + (ishtml?1:0);\n var size = $todd.textsize.cache[key];\n if (size)\n return size;\n\n // Set node width if specified\n if (width)\n {\n $todd.textsize.node.style.width = width + 'px';\n $todd.textsize.node.style.whiteSpace = \"normal\";\n }\n else\n {\n $todd.textsize.node.style.width = \"auto\";\n $todd.textsize.node.style.whiteSpace = \"nowrap\";\n }\n\n dompack.setStyles($todd.textsize.node, applystyles);\n\n // Calculate and cache text size\n $todd.textsize.node[ishtml ? \"innerHTML\" : \"textContent\"] = text;\n var rect = $todd.textsize.node.getBoundingClientRect();\n // Rounding up here to avoid returning rounded-down values which would result in elements too small to contain the given text\n // (getBoundingClientRect should return frational values, and not return rounded values)\n size = { x: Math.ceil(rect.width)\n , y: Math.ceil(rect.height)\n };\n $todd.textsize.cache[key] = size;\n return size;\n};\n\n$todd.ReadSize = function(sizeval)\n{\n if(!sizeval)\n return null;\n if(sizeval.substr(sizeval.length-2)=='gr')\n return { type: 5, size: parseInt(sizeval, 10) };\n if(sizeval.substr(sizeval.length-2)=='px')\n return { type: 2, size: parseInt(sizeval, 10) };\n if(sizeval.substr(sizeval.length-2)=='pr')\n return { type: 1, size: parseInt(sizeval, 10) };\n if(sizeval.substr(sizeval.length-1)=='x')\n return { type: 3, size: parseInt(sizeval, 10) };\n if(sizeval=='sp')\n return { type: 4, size: 1 };\n return null;\n};\n$todd.IsAbsoluteParsedSize = function(size)\n{\n return size && size.type != 1;\n};\n\n// Return the set width/height, or the xml width/height, for a component's size object\n$todd.ReadSetWidth = function(sizeobj)\n{\n return $todd.ReadSetSize(sizeobj, true);\n};\n$todd.ReadSetHeight = function(sizeobj)\n{\n return $todd.ReadSetSize(sizeobj, false);\n};\n$todd.ReadSetSize = function(sizeobj, horizontal)\n{\n var size = sizeobj.new_set;\n if (size === null)\n {\n var xml = $todd.ReadSize(sizeobj.xml_set);\n size = $todd.IsFixedSize(sizeobj.xml_set) ? $todd.CalcAbsSize(xml, horizontal) : 0;\n }\n return size;\n};\n$todd.CalcAbsWidth = function(size)\n{\n return $todd.CalcAbsSize(size, true);\n};\n//Calculate the absolute height for a block element (where 2gr = 56)\n$todd.CalcAbsHeight = function(size)\n{\n return $todd.CalcAbsSize(size, false);\n};\n//Calculate the absolute height for an inline element (where 2gr = 51)\n$todd.CalcAbsInlineHeight = function(size)\n{\n return $todd.CalcAbsSize(size, false, true);\n};\n$todd.CalcAbsSize = function(size, horizontal, inline)\n{\n if (!size)\n return 0;\n\n if (typeof(size) == \"number\")\n return size;\n\n if (typeof(size) == \"string\") // XML size specification\n {\n if(size.substr(size.length-2) == 'px')\n return parseInt(size, 10);\n if(size.substr(size.length-2) == 'gr')\n {\n if(horizontal)\n {\n console.error(\"'gr' units not supported horizontally\");\n if($todd.intolerant)\n throw new Error(\"'gr' units not supported horizontally\");\n }\n\n return parseInt(size, 10) * $todd.gridlineHeight - (inline ? $todd.gridlineTotalMargin : 0);\n }\n if(size.substr(size.length-1) == 'x')\n {\n if (horizontal)\n return parseInt(size, 10) * $todd.desktop.x_width;\n // Round to grid size\n return $todd.UpToGridsize(parseInt(size, 10) * $todd.desktop.x_height);\n }\n if(size == 'sp')\n return $todd.settings.spacerwidth;\n return parseInt(size, 10);\n }\n\n if (typeof(size) == \"object\") // Internal size record (as returned by ReadSize)\n {\n if (size.type == 2)\n return size.size;\n if (size.type == 3)\n {\n if (horizontal)\n return size.size * $todd.desktop.x_width;\n return $todd.UpToGridsize(size.size * $todd.desktop.x_height);\n }\n if (size.type == 4)\n return $todd.settings.spacerwidth;\n if (size.type == 5) //'gr'\n return parseInt(size, 10) * $todd.gridlineHeight - (inline ? $todd.gridlineTotalMargin : 0);\n }\n\n return 0;\n};\n\n$todd.IsFixedSize = function(size)\n{\n return size && (size.substr(size.length-1)=='x' //matches both 'px' and 'x' :)\n || size.substr(size.length-2)=='gr'\n || size=='sp'\n || ((parseInt(size) + \"\") == size) // matches numbers in strings\n );\n};\n\nfunction readXMLSize(min, set, iswidth, inline)\n{\n // Initialize width settings (ADDME switch all code to use xml_set_parsed?)\n return { xml_min: iswidth ? $todd.CalcAbsWidth(min) : inline ? $todd.CalcAbsInlineHeight(min) : $todd.CalcAbsHeight(min) // min width as set by xml\n , xml_set: set // width as set by xml (absolute or proportional size)\n , xml_set_parsed: $todd.ReadSize(set)\n , servermin: min //The unparsed versions. deprecate xml_min,xml_set,xml_min_parsed!\n , serverset: set\n , dirty: true // calc should be recalculated\n , min: 0 // min required width\n , calc: 0 // calculated width\n , set: 0 // allocated width\n , new_set: null\n };\n}\n\n//ADDME why can't we receive widths already in the proper format as much as possible?\n$todd.ReadXMLWidths = function(xmlnode) //xmlnode may be null to init a default width object\n{\n return readXMLSize(xmlnode && xmlnode.minwidth ? xmlnode.minwidth : ''\n ,xmlnode && xmlnode.width ? xmlnode.width : ''\n ,true\n );\n};\n$todd.ReadXMLHeights = function(xmlnode, inline)\n{\n return readXMLSize(xmlnode && xmlnode.minheight ? xmlnode.minheight : ''\n ,xmlnode && xmlnode.height ? xmlnode.height : ''\n ,false\n ,inline\n );\n};\n\n\n/****************************************************************************************************************************\n * Events\n */\n\n$todd.highpriority = false;\n$todd.mousedragthreshold = 3; // Amount of pixels to move before drag kicks in\n$todd.globalevents = {}; // Global event handlers\n\n// Fallback settings for user preferences\n$todd.fallback =\n { lang: \"en\"\n , dateformat: \"%d-%m-%Y\"\n , timeformat: \"%H:%M\"\n };\n\n// Desktop properties, will be calculated after initialization (and on resizing/zooming)\n$todd.desktop =\n { node: null\n // Dimensions\n , top: 0\n , left: 0\n , width: 0\n , height: 0\n // Orientation\n , orientation: 0 // Browser orientation (portable devices) in degrees\n , portrait: true // If device is oriented vertically\n , landscape: false // If device is oriented horizontally\n // x dimensions\n , x_width: 7 // The width of an 'x' character\n , x_height: 16 // The (line) height of an 'x' character\n };\n\n$todd.uploadmethod = '';\n$todd.downloadmethod = '';\n$todd.tolliumservice = '';\n\n\n/****************************************************************************************************************************\n * Window events\n */\n\n$todd.mouse = { clickstatus: null\n , hoverstatus: { tooltipshowtimeout: null\n , tooltiphidetimeout: null\n , curcomp: null\n , dragcomp: null\n }\n , dragstatus: null\n };\n\n\n$todd.globalevents.OnDragFiles = function(event)\n{\n // We want to handle this ourselves\n event.preventDefault();\n};\n\n/****************************************************************************************************************************\n * Globally unique id's\n */\n\n$todd.globalidcounter = 0;\n$todd.getGlobalId = function()\n{\n return (++$todd.globalidcounter).toString(16); //FIXME deprecate\n};\n\n\n/****************************************************************************************************************************\n * Some experimental and implementation test functions\n */\n\n$todd.componentsToMessages=function(components)\n{\n /* ADDME: updateScreen is currently an attempt at a 'prettier' API for screen management but we should probably merge with processMessages eventually (perhaps todd controller should change its format)\n */\n var messages=[];\n Object.keys(components).forEach(name =>\n {\n let obj = components[name];\n if(!obj.messages || Object.keys(obj).length>1) //not only sending messages\n {\n var compmsg = {...obj\n , instr: \"component\"\n , target: name\n , type: obj.type || '-shouldalreadyexist'\n , name: name\n , width: obj.width\n , height: obj.height\n , minwidth: obj.minwidth\n , minheight: obj.minheight\n , enabled: obj.enabled !== false\n };\n delete compmsg.messages;\n messages.push(compmsg);\n }\n\n if(obj.messages)\n obj.messages.forEach(msg =>\n {\n var copymsg = { ...msg\n , msg: 'message'\n , target: name\n }; //FIXME copying is probably overkill, but i'm trying to avoid touching source objects.. need to better align various syntaxes\n\n messages.push(copymsg);\n });\n });\n\n return messages;\n};\n\n$todd.IsDebugTypeEnabled = function(type)\n{\n return enabledlogtypes.includes('all') || enabledlogtypes.includes(type);\n};\n\n$todd.DebugTypedLog = function(target)\n{\n var type;\n if (typeof(target) == \"string\")\n {\n target = target.split(\":\");\n type = target[0];\n if (target.length > 1)\n target = target[1];\n }\n\n if (typeof(target) != \"string\" || !([ \"log\", \"info\", \"warn\", \"error\" ].includes(target)))\n target = \"log\";\n\n // Check if the requested type should be shown\n if (!$todd.IsDebugTypeEnabled(type))\n return;\n\n // Strip first element (type) from arguments\n var args = Array.prototype.slice.call(arguments);\n args.splice(0, 1);\n console[target].apply(console, args);\n};\n\nfunction checkLogTypes()\n{\n // Check for specific debug log types (see DebugTypedLog)\n if (document.location.hash)\n {\n var hash = document.location.hash.substr(1);\n enabledlogtypes = hash.split(\",\");\n }\n\n if (enabledlogtypes.includes('all'))\n console.warn(\"Showing all typed debug messages\");\n else if (enabledlogtypes.length)\n {\n console.warn(\"Showing typed debug messages with types \" + enabledlogtypes.join(\", \"));\n }\n}\n\n/* The CSS Color Module Working Draft[1] defines hex notation for RGB color with an alpha value, but these are not supported\n by (all?) browser (yet?). This functions rewrites them to rgba() notation.\n [1] https://drafts.csswg.org/css-color/#hex-notation\n https://caniuse.com/#search=rgba - IE11 still fails */\n$todd.fixupColor = function(color)\n{\n if (color.match(/\\#[0-9a-z]{8}$/))\n {\n return \"rgba(\" + parseInt(color.substr(1, 2), 16) + \",\"\n + parseInt(color.substr(3, 2), 16) + \",\"\n + parseInt(color.substr(5, 2), 16) + \",\"\n + (parseInt(color.substr(7, 2), 16) / 255) + \")\";\n }\n if (color.match(/\\#[0-9a-z]{4}$/))\n {\n return \"rgba(\" + parseInt(color.substr(1, 1) + color.substr(1, 1), 16) + \",\"\n + parseInt(color.substr(2, 1) + color.substr(2, 1), 16) + \",\"\n + parseInt(color.substr(3, 1) + color.substr(3, 1), 16) + \",\"\n + (parseInt(color.substr(4, 1) + color.substr(4, 1), 16) / 255) + \")\";\n }\n return color;\n};\n\n\n/** @short\n @param flags The flags which must be checked against (useually gathered from selected options/rows)\n For example:\n [{ selectable := true, hasurl := false }\n ,{ selectable := false, hasurl := false }\n ]\n @param checkflags Array of string's with the name of flags which must match to enable\n A flag starting with '!' means that to match the flag must NOT TRUE (meaning FALSE) in each object in the 'flags' array.\n Otherwise it's a match if the flag is TRUE in all objects in the flags array.\n @param min minimum amount of items in the flags list\n @param max maximum amount of items in the flags list\n @param selectionmatch (\"all\", \"any\")\n @return whether the action should be enabled (all checkflags match each item in flags)\n*/\n$todd.checkEnabledFlags = function(flags, checkflags, min, max, selectionmatch) //FIXME rename and move out of Screen... compbase?\n{\n // This code should be synchronized with checkEnabledFlags in tollium/include/internal/support.whlib\n $todd.DebugTypedLog(\"actionenabler\", \"- - Checking checkflags [\"+checkflags.join(\", \")+\"], \"+flags.length+\" in [\"+min+\",\"+(max >= 0 ? max+\"]\" : \"->\")+\" (\"+selectionmatch+\")\");\n\n // Check correct number of selected items\n if (flags.length < min || (max >= 0 && flags.length > max))\n {\n $todd.DebugTypedLog(\"actionenabler\", \"- - Wrong number of selected items (\"+flags.length+\"), action should be disabled\");\n return false;\n }\n\n // This action is enabled if the flags are enabled for each selected item\n // If the checkflags for this action are empty, the action is always enabled\n // (the right number of items is already selected) and the selected flags\n // don't have to be checked, so i is initialized with the length of the\n // selected flags.\n if (checkflags.length == 0 || (checkflags.length == 1 && checkflags[0] == ''))\n {\n $todd.DebugTypedLog(\"actionenabler\", \"- - No checkflags, action should be enabled\");\n return true;\n }\n var i = 0;\n var any = false;\n for (; i < flags.length; ++i)\n {\n if (!flags[i])\n {\n $todd.DebugTypedLog(\"actionenabler\", \"- - Flag \"+i+\" undefined, continue to next flag\");\n break;\n }\n var j = 0;\n for (; j < checkflags.length; ++j)\n {\n var checkflag = checkflags[j];\n var checkvalue = true;\n if (checkflag.charAt(0) == '!')\n {\n checkflag = checkflag.slice(1);\n checkvalue = false;\n }\n $todd.DebugTypedLog(\"actionenabler\", \"- - Checkflag '\"+checkflag+\"': \"+flags[i][checkflag]+\"=\"+checkvalue+\"?\");\n if (flags[i][checkflag] != checkvalue)\n {\n $todd.DebugTypedLog(\"actionenabler\", \"- - Checkflag '\"+checkflag+\"' not enabled for selected item \"+i);\n break;\n }\n }\n if (j < checkflags.length)\n {\n // This item does not match, so if all must match, the action should be disabled\n if (selectionmatch == \"all\")\n break;\n }\n else if (selectionmatch == \"any\")\n {\n // This item does match, so if any must match, the action should be enabled\n any = true;\n break;\n }\n }\n // If selectionmatch = \"all\", i should point beyond the end of the flags list (all items are checked and all passed)\n // If selectionmatch = \"any\", any should be true\n var enabled = (selectionmatch == \"all\" && i >= flags.length) || (selectionmatch == \"any\" && any);\n $todd.DebugTypedLog(\"actionenabler\", \"- - Action should be \"+(enabled ? \"enabled\" : \"disabled\"));\n return enabled;\n};\n\nexport default $todd;\nwindow.__todd = $todd; //test framework currently requires it. FIX THAT\n\ncheckLogTypes();\nwindow.addEventListener(\"hashchange\", checkLogTypes);\n", "import * as whintegration from '@mod-system/js/wh/integration';\nimport * as dompack from 'dompack';\nimport * as domfocus from 'dompack/browserfix/focus';\nimport $todd from \"@mod-tollium/web/ui/js/support\";\n\n// Mutators should be defined first, so they can be used inside the ObjLayout Class!\n\nlet urlgencounter = 0;\n\n/****************************************************************************************************************************\n * *\n * COMPONENT BASE *\n * *\n ****************************************************************************************************************************/\nclass ToddCompBase\n{\n\n/****************************************************************************************************************************\n* Initialization\n*/\n\n /* @short Initialize the component with the given component data\n (This is a combination of what component initialization with Construct, InitLayoutFromXML, InitLayoutFromData,\n InitFromXML, InitFromData and FinishSetup used to be)\n @param parent The parent component (null for frame)\n @param data The component initialization data\n @param replacingcomp The old component, if this is a new version of an existing component (for tollium components only)\n @return If this is the first initialize (true), or an update (false)\n */\n constructor(parentcomp, data, replacingcomp)\n {\n this.componenttype = \"component\";\n\n // The parent component\n // (This is what parent used to be, but MooTools uses this.parent to call ancestor functions within updated functions)\n this.parentcomp = null; // old 'parent'\n\n // List children components that have this component as parentcomp\n this.childrencomps = [];\n\n // The component window's frame component\n // (This is what windowroot used to be)\n this.owner = null; // old 'windowroot'\n\n // Whether to destroy this component when its parent is destroyed\n this.destroywithparent = false;\n\n // Initial property values\n this.enabled = true;\n this.visible = true;\n\n this.mousedown = false; // True after mousedown, false after mouseup\n\n this.listeningtoactions = []; //names of actions for which we're listeninn\n this.enablecomponents = [];\n\n /** List of components that need to be destroyed when this component is destroyed\n A component is inserted in this list in its parent when 'destroywithparent' is true in @a initialize.\n */\n this.cascadedestroys = [];\n\n this.node = null; //'legacy' support\n this.nodes = {};\n\n // Width settings\n this.width = {};\n\n // Height settings\n this.height = {};\n\n this.gotskinsettings = false;\n this.skinsettings = null;\n\n\n if(parentcomp == null) //we are the toplevel screen/frame\n {\n this.objectmap = {}; // We need to do this because frame can't yet and it will crash registerComponent\n }\n\n this.title = null;\n this.value = null;\n this.tooltip = null;\n\n // If we're on a line, the line can tell us if we're in an inline element\n this.isinline = parentcomp && parentcomp.holdsinlineitems;\n\n if(parentcomp===null && data===null)\n return; //the table subcomponents don't fully initialize their subs, so this is a hack for them\n\n this.parentcomp = parentcomp;\n this.owner = parentcomp ? parentcomp.owner : this;\n this.destroywithparent = parentcomp && data.destroywithparent || false;\n\n if (parentcomp)\n parentcomp.childrencomps.push(this);\n\n this.name = data.target;\n if(!this.name)\n throw new Error(\"Please ensure all components have a name ('target' field)\"); //uniquely numbered components leak very easily in the objectmap[]...\n\n this.initializeSizes(data);\n\n this.unmasked_events = data.unmasked_events;\n this.enablecomponents = data.enablecomponents ? data.enablecomponents : [];\n this.xml_enabled = data.enabled === true;\n this.visible = data.visible !== false;\n\n this.hint = data.hint ? data.hint : '';\n this.shortcut = data.shortcut ? data.shortcut : '';\n\n this.owner.registerComponent(this);\n this.firstlayout = true;\n// this.lineminheight = 0;\n }\n afterConstructor(data) //needed to run actions that affect buildNode\n {\n if(data.defaultbutton)\n this.node.dataset.toddDefaultButton = data.defaultbutton;\n }\n getTitle()\n {\n return this.title;\n }\n setTitle(title)\n {\n this.title = title;\n }\n getEnabled()\n {\n return this.enabled;\n }\n setEnabled(enabled)\n {\n this.enabled = enabled;\n }\n getValue()\n {\n return this.value;\n }\n setValue(value)\n {\n this.value = value;\n }\n getVisible()\n {\n return this.visible;\n }\n setVisible(visible)\n {\n this.visible = visible;\n }\n getTooltop()\n {\n return this.tooltip;\n }\n setTooltip(tooltip)\n {\n this.tooltip = tooltip;\n }\n\n\n destroy()\n {\n this.setInterestingActions([]);\n\n // Unregister and rename to indicate destroyed components\n if(this.name)\n {\n this.owner.unregisterComponent(this, true);\n if (this.name.substr(this.name.length-11) != \" (replaced)\")\n this.name += \" (destroyed)\";\n }\n\n // Destroy all children marked as 'destroywithparent'. Destroyed children will unregister themselves, so iterate over a copy.\n var copy = this.childrencomps.slice();\n copy.forEach(comp =>\n {\n if (comp.destroywithparent)\n comp.destroy();\n else\n comp.parentcomp = null;\n });\n\n this.childrencomps = [];\n\n // Keep childrencomps in parent up-to-date\n if (this.parentcomp) //erase us from parent\n this.parentcomp.childrencomps = this.parentcomp.childrencomps.filter(comp => comp != this);\n\n this.parentcomp = null;\n this.owner = null;\n }\n\n getDestroyableNodes()\n {\n var retval = [];\n if (this.node)\n retval.push(this.node);\n for (var i in this.nodes)\n if (this.nodes.hasOwnProperty(i))\n retval.push(this.nodes[i]);\n\n return retval;\n }\n\n initializeSizes(data)\n {\n this.width = $todd.ReadXMLWidths(data);\n this.height = $todd.ReadXMLHeights(data, this.isinline);\n }\n\n setSizeToMaxOf(sizeproperty, nodes, addspace)\n {\n var calc=0, min=0;\n nodes.filter(node=>!!node).forEach(node =>\n {\n calc = Math.max(calc, node[sizeproperty].calc);\n min = Math.max(min, node[sizeproperty].min);\n });\n\n this[sizeproperty].calc = calc + (addspace||0);\n this[sizeproperty].min = min + (addspace||0);\n }\n\n setSizeToSumOf(sizeproperty, nodes, addspace)\n {\n var calc=0,min=0;\n nodes.filter(node=>!!node).forEach(node =>\n {\n calc += node[sizeproperty].calc;\n min += node[sizeproperty].min;\n });\n\n this[sizeproperty].calc = calc + (addspace||0);\n this[sizeproperty].min = min + (addspace||0);\n }\n checkEnabled()\n {\n }\n getVisibleChildren()\n {\n return [];\n }\n\n//set the list of actions we care about.\n setInterestingActions(actionlist)\n {\n //ADDME optimize: don't unregister/reregister\n\n //unregister any current actions\n for(let i=0;i\n {\n let callback = () => resolve();\n this.owner.tryProcessMessage(this.name, type, data, options.modal, callback);\n });\n }\n\n // Should this component be submitted at all? By default, only when enabled\n shouldSubmitValue()\n {\n return this.enabled;\n }\n\n // If this function returns null, its value is not submitted\n getSubmitValue()\n {\n return null;\n }\n\n // Check if the given event is unmasked for this component\n isEventUnmasked(eventname)\n {\n if(!this.owner)\n return false;\n return this.owner.hasEventListener(this.name, eventname) || (this.unmasked_events && this.unmasked_events.includes(eventname));\n }\n\n // Check enableon rules\n enabledOn(checkflags, min, max, selectionmatch)\n {\n this.debugLog(\"actionenabler\", \"does not support enabling actions\");\n return false;\n }\n\n // Apply a passive update (readd this component to its parent using an updated version of the component)\n applyUpdatedComp(data)\n {\n if (!this.parentcomp)\n return;\n\n this.parentcomp.readdComponent(this);\n }\n\n // Apply a dynamic update\n applyUpdate(data)\n {\n if (data.type == \"messages\")\n {\n data.messages.forEach(msg =>\n {\n this.processIncomingMessage(msg.type, msg.data);\n });\n return;\n }\n\n console.log(data);\n console.error(\"Received update '\" + data.type + \"' for component '\" + this.name + \"' but not handled\");\n }\n\n processIncomingMessage(type, data)\n {\n let expectcallback = \"onMsg\" + type;\n if(this[expectcallback])\n return this[expectcallback].apply(this,[data]);\n\n console.warn(`Missing handler '${expectcallback}' to process message of type '${type}'`, data);\n }\n\n/****************************************************************************************************************************\n* Component management\n*/\n\n // Readd the given component using an updated version of the component\n readdComponent(comp)\n {\n console.error('Child replacement not implemented by component ' + this.name + ' (' + this.componenttype + ')');\n }\n\n focusComponent()\n {\n let tofocus = domfocus.getFocusableComponents(this.node)[0];\n if(tofocus)\n dompack.focus(tofocus);\n else if (domfocus.canFocusTo(this.node))\n dompack.focus(this.node);\n }\n\n hasfocus()\n {\n return this.node.contains(document.activeElement);\n }\n\n getToddElementForNode(node)\n {\n for(;node;node=node.parentNode)\n if(node.getAttribute && node.getAttribute('data-name'))\n return node.getAttribute('data-name');\n return null;\n }\n //prevent stealing focus, _if_ the click landed in a dom element owned by this element\n mouseDownNoFocusSteal(event)\n {\n if(this.getToddElementForNode(event.target) == this.name)\n {\n console.log(\"*** mouseDownNoFocusSteal on '\" + this.name + \"' WOULD have tried to prevent this focus\"); //FIXME remove this warning if nothing broke because of it\n //console.warn(\"onmousedown on '\" + this.name + \"' preventing loss of focus\"); //FIXME remove this warning if nothing broke because of it\n //event.preventDefault();\n }\n return true;\n }\n/****************************************************************************************************************************\n* Property getters & setters\n*/\n\n\n setDefault(isdefault)\n {\n\n }\n\n\n/****************************************************************************************************************************\n* DOM\n*/\n\n // Build the DOM node(s) for this component\n buildNode()\n {\n this.node = dompack.create(\"span\", { textContext: \"(not implemented: \" + this.componenttype + \")\" });\n }\n getNode()\n {\n if(!this.node)\n throw new Error(\"Trying to request node but not initialized yet\");\n return this.node;\n }\n\n // Return the principal DOM node of this component (returns this.node by default, if defined)\n // (This is what GetDOMNode used to be, but by using toElement, one can simply call $(component) to get the DOM node)\n toElement()\n {\n if(!whintegration.config.islive)\n throw new Error(\"Avoid toElement, especially implicit calls! - replace with an explicit getNode call\");\n // Placeholder for non-implemented component types\n return this.node ? this.node : dompack.create(\"span\", { textContent: \"(not implemented: \" + this.componenttype + \")\" });\n }\n\n\n/****************************************************************************************************************************\n* Dimensions\n*/\n\n beforeRelayout()\n {\n for (const comp of this.getVisibleChildren())\n comp.beforeRelayout();\n }\n\n //new dimensional APIs\n updateSkinSettings()\n {\n if(this.node && !this.gotskinsettings)\n {\n this.skinsettings = this.getSkinSettings();\n this.gotskinsettings = true;\n }\n\n for (const comp of this.getVisibleChildren())\n comp.updateSkinSettings();\n }\n\n getSkinSettings()\n {\n return null;\n }\n\n dim(horizontal)\n {\n return horizontal ? this.width : this.height;\n }\n // If the dim should be calculated (because the dim of this component or any child components is dirty)\n isDimensionDirty(horizontal)\n {\n return this.dim(horizontal).dirty || this.getVisibleChildren().some( function(child) { return child.isDimensionDirty(horizontal); });\n }\n // If no minimum is set but an absolute size is given, set the minimum to it. This implements taking a height as minheight, needed to prevent components from suddenly shrinking\n setMinToAbs(sizeprop)\n {\n if(!sizeprop.servermin && $todd.IsFixedSize(sizeprop.serverset))\n sizeprop.servermin = sizeprop.serverset;\n }\n calculateDimension(horizontal)\n {\n //beginWidth|Height\n var prop = this.dim(horizontal);\n if(!this.isDimensionDirty(horizontal))\n {\n if($todd.IsDebugTypeEnabled(\"dimensions\"))\n console.log(this.getDebugName() + (horizontal ? \": CW:\" : \": CH:\") + \" not dirty, skipping recalculation. min: \" + prop.min + \", calc: \" + prop.calc + \" (current set: \" + prop.set + \")\");\n\n return;\n }\n\n var children = this.getVisibleChildren();\n if($todd.IsDebugTypeEnabled(\"dimensions\"))\n {\n console.group(this.getDebugName() + (horizontal ? \": CW:\" : \": CH:\") + \" recalculating. \" + (children.length ? \"(\" + children.length + \" children) \" : \"\"), this.node);\n }\n if(children.includes(null))\n console.error(this.getDebugName() + \" children contains a null!\", children);\n\n for (const comp of children)\n comp.calculateDimension(horizontal);\n\n prop.calc = 0;\n prop.min = 0;\n\n if(horizontal)\n this.calculateDimWidth();\n else\n this.calculateDimHeight();\n\n if($todd.IsDebugTypeEnabled(\"dimensions\"))\n console.log(this.getDebugName() + (horizontal ? \": CW: \" : \": CH: \") + \" min:\" + prop.min + \" calc:\" + prop.calc);\n\n //apply minimums from XML\n if(prop.servermin)\n {\n let calcmin = $todd.CalcAbsSize(prop.servermin, horizontal, this.isinline);\n if(calcmin > prop.min)\n {\n prop.min = calcmin;\n if($todd.IsDebugTypeEnabled(\"dimensions\"))\n console.log(this.getDebugName() + (horizontal ? \": CW: \" : \": CH: \") + \" server pulls up minimum to \" + prop.min);\n }\n }\n\n //fixup calculated using XML and min\n if(prop.new_set)\n {\n if($todd.IsDebugTypeEnabled(\"dimensions\"))\n console.log(this.getDebugName() + (horizontal ? \": CW: \" : \": CH: \") + \" (user-set) setting calc of \" + prop.calc + ' to ' + prop.new_set);\n prop.calc = prop.new_set;\n }\n else if($todd.IsFixedSize(prop.serverset))\n {\n var newsize = $todd.CalcAbsSize(prop.serverset, horizontal, this.isinline);\n if($todd.IsDebugTypeEnabled(\"dimensions\"))\n console.log(this.getDebugName() + (horizontal ? \": CW: \" : \": CH: \") + \" (screen-set) setting calc of \" + prop.calc + ' to ' + newsize);\n prop.calc = newsize;\n }\n\n prop.min = Math.ceil(prop.min);\n prop.calc = Math.ceil(Math.max(prop.calc, prop.min));\n\n if(horizontal)\n this.fixupCalculatedWidths();\n else\n this.fixupCalculatedHeights();\n\n if($todd.IsDebugTypeEnabled(\"dimensions\"))\n {\n console.groupEnd();\n console.log(this.getDebugName() + (horizontal ? \": CW: \" : \": CH: \") + \" final min: \" + prop.min + ' calc:' + prop.calc);\n }\n prop.dirty = false;\n if(horizontal)\n this.height.dirty = true;\n }\n applyDimension(horizontal)\n {\n var dim = this.dim(horizontal);\n if($todd.IsDebugTypeEnabled(\"dimensions\"))\n console.group(this.getDebugName() + (horizontal ? \": AW: \" : \": AH: \") + \" applying \" + dim.set + \" (min=\" + dim.min + \", calc=\" + dim.calc + \")\", this.node);\n\n if(horizontal)\n this.applySetWidth();\n else\n this.applySetHeight();\n\n for (const comp of this.getVisibleChildren())\n comp.applyDimension(horizontal);\n this.updateNodeSizeData(); //FIXME make this debugging only\n\n if($todd.IsDebugTypeEnabled(\"dimensions\"))\n {\n console.groupEnd();\n }\n }\n\n\n setWidth(setwidth)\n {\n if (setwidth < this.width.min)\n {\n console.error(this.getDebugName() + ' \"' + this.name + '\": Setting width to less than minimum (', setwidth, 'vs', this.width.min, ')', this.node);\n if ($todd.intolerant)\n throw new Error(\"Component got less width than needed\");\n setwidth = this.width.min;\n }\n\n this.width.set = setwidth;\n //FIXME - normal dimension application should arrange for: this.applyDimension(true);\n }\n setHeight(setheight)\n {\n if (setheight < this.height.min)\n {\n console.error(this.componenttype + ' \"' + this.name + '\": Setting height to less than minimum (', setheight, 'vs', this.height.min, ')', this.node);\n if ($todd.intolerant)\n throw new Error(\"Component got less height than needed\");\n setheight = this.height.min;\n }\n\n this.height.set = setheight;\n //FIXME - normal dimension application should arrange for: this.applyDimension(false);\n }\n\n\n setNewWidth(newwidth) //FIXME rename to eg 'client_set' or 'user_set' ?\n {\n this.width.new_set = newwidth;\n this.width.dirty = true;\n }\n setNewHeight(newheight)\n {\n this.height.new_set = newheight;\n this.height.dirty = true;\n }\n /** applySetWidth should apply this.width.set to its children (ie, it should not be updating the DOM yet)\n applySetWidth does not need to raise this.width.set to this.width.min, we will have done that\n */\n applySetWidth()\n {\n if($todd.IsDebugTypeEnabled(\"dimensions\"))\n console.log(this.getDebugName() + \" does not implement applySetWidth\");\n }\n applySetHeight()\n {\n if($todd.IsDebugTypeEnabled(\"dimensions\"))\n console.log(this.getDebugName() + \" does not implement applySetHeight\");\n }\n\n /** calculateDimWidth should set this.width.min and this.width.calc\n calculateDimWidth does not need to raise this.width.min/calc to any XML settings, we will do that\n */\n calculateDimWidth()\n {\n console.error(this.getDebugName() + \" did not implement calculateDimWidth\");\n }\n calculateDimHeight()\n {\n console.error(this.getDebugName() + \" did not implement calculateDimHeight\");\n }\n fixupCalculatedWidths()\n {\n }\n fixupCalculatedHeights()\n {\n }\n\n // Get the top margin of the component within its line\n getVerticalPosition()\n {\n if(!this.parentcomp && $todd.intolerant)\n throw new Error(\"No parent component for current element\");\n if (!this.parentcomp || this.parentcomp.componenttype != \"panel.line\" || this.parentcomp.layout == \"tabs-space\")\n return 0;\n return Math.max(Math.round((this.height.set - this.height.calc) / 2), 0);\n }\n\n /* relayout the component based on this.width.set and this.width.height\n invoke relayout on children */\n relayout() {}\n\n // Get node size data\n getNodeSizeData()\n {\n return [ \"min: \" + this.width.min+ \",\" + this.height.min\n , \"calc: \" + this.width.calc + \",\" + this.height.calc\n , \"set: \" + this.width.set + \",\" + this.height.set\n , \"xmlmin:\" + this.width.servermin + \",\" + this.height.servermin\n , \"xmlset:\" + this.width.serverset + \",\" + this.height.serverset\n ].join(\", \");\n }\n\n // Update the size data in the 'todd-sizes' attribute of the component's DOM node\n updateNodeSizeData()\n {\n this.node.setAttribute(\"todd-sizes\", this.getNodeSizeData());\n }\n\n/* Distributes available pixels over the given size objects (component.width or component.height). Leftover pixels are\n assigned to sizeobjs[leftoverobj], or distributed evenly over the sizeobjs if leftoverobj < 0.\n\n sizeobjs should be created by ReadXMLWidths/ReadXMLHeights */\n distributeSizes(available, sizeobjs, horizontal, leftoverobj)\n {\n return distributeSizes(available, sizeobjs, horizontal, leftoverobj);\n }\n\n distributeSizeProps(property, available, items, horizontal, leftoverobj)\n {\n var sizeobjs=[];\n items.forEach(item => sizeobjs.push(item[property]));\n return this.distributeSizes(available, sizeobjs, horizontal, leftoverobj);\n }\n\n\n/****************************************************************************************************************************\n* Events\n*/\n\n // Called when window is added to DOM, but before it is made visible\n // Return false to prevent window from showing\n onShow()\n {\n return true;\n }\n\n // Called before the component is added to another component\n onBeforeReparent() {}\n\n // Called to get the component's tooltip\n // Return a string to show as tooltip, or nothing to not show the tooltip\n onTooltip()\n {\n if(this.hint)\n {\n //??Fixme\n return true;\n }\n return false;\n }\n\n onActionUpdated()\n {\n }\n\n/****************************************************************************************************************************\n* Public API\n*/\n\n getFileTransferBaseURL(options)\n {\n var url = $todd.resourcebase + \"filetransfer.shtml\";\n if (options && options.filename)\n url += \"/\" + encodeURIComponent(options.filename);\n url += '?l=' + encodeURIComponent(this.owner.hostapp.whsid);\n url += '&w=' + encodeURIComponent(this.owner.screenname);\n url += '&n=' + encodeURIComponent(this.name);\n return url;\n }\n\n /** @param type Type of message\n @param data Data to send\n */\n getFileTransferURL(type, data, options)\n {\n var ftid = 'FT:c' + ++urlgencounter;\n var url = this.getFileTransferBaseURL(options);\n url += '&t=' + encodeURIComponent(type);\n if (data)\n url += \"&d=\" + encodeURIComponent(JSON.stringify(data));\n url += \"&s=\" + ftid;\n return { url: url, id: ftid };\n }\n isMyFileTransferURL(url)\n {\n // Check if this is a url generated by TolliumWebController::GetComponentFileTransferURL\n var baseurl = this.getFileTransferBaseURL();\n return url.substr(0,baseurl.length)==baseurl;\n }\n\n/****************************************************************************************************************************\n* Debugging\n*/\n getDebugName()\n {\n return this.componenttype + \" \" + (this.parentcomp ? this.parentcomp.name + \"->\" : \"\") + (this.name ||'');\n }\n debugLog(type)\n {\n var args = Array.prototype.slice.call(arguments);\n\n //prefix first argument with item name, if possible\n if(args.length>=2 && typeof args[1]=='string')\n {\n args[1] = this.getDebugName() + \": \" + args[1];\n }\n else\n {\n args.splice(1, 0, this.getDebugName() + \": \" + args[1]);\n }\n $todd.DebugTypedLog.apply(null, args);\n }\n}\n\nexport function distributeSizes(available, sizeobjs, horizontal, leftoverobj, options)\n{\n let intolerant = $todd.intolerant || (options && options.intolerant);\n\n if(!(available>=0)) //guard against negative or non-number availables\n {\n console.error(\"distributeSizes got invalid available space\",available,sizeobjs,leftoverobj);\n if (intolerant)\n throw new Error(\"Invalid 'available space' given to distributeSizes\");\n available = 100; // just give some\n }\n\n var logdistribute = $todd.IsDebugTypeEnabled(\"distribute\");\n if(logdistribute)\n console.log(\"DistributeSizes over \" + available + \"px, horizontal=\"+horizontal+\" leftoverobj=\" + leftoverobj + \", sizeobjs=\" + sizeobjs.length, sizeobjs);\n\n var total_prop=0, total_pixels=0, added_size = 0;\n var tempsizes = [];//Temporay store for calculated sizes\n sizeobjs.forEach(function(sizeobj, idx)\n {\n tempsizes[idx] = { set: 0, min: 0, pref: 0, prop: 0 };\n\n // If a size is already set, use that, otherwise read the size set in xml\n var is_fixedsize = false, setsize = 0;\n if (typeof sizeobj.new_set == \"number\")\n {\n //ADDME: Take original sizes (pr?) into account?\n if(logdistribute)\n console.log(\"Child \" + idx + \" new_set was set. setsize=\" + sizeobj.new_set);\n setsize = sizeobj.new_set;\n is_fixedsize = true;\n }\n else\n {\n if(!sizeobj.serverset || $todd.IsFixedSize(sizeobj.serverset))\n {\n is_fixedsize=true;\n setsize = $todd.CalcAbsSize(sizeobj.serverset, horizontal, sizeobj.isinline);\n }\n if(logdistribute)\n console.log(\"Child \" + idx + \" xmlsize=\" + sizeobj.serverset + \", is_fixedsize=\" + is_fixedsize + \", setsize=\" + setsize);\n }\n\n tempsizes[idx].min = sizeobj.min;\n\n if(is_fixedsize) // absolute: (p)x\n {\n var calc = setsize || sizeobj.calc;\n tempsizes[idx].pref = Math.max(calc, tempsizes[idx].min);\n if(logdistribute)\n console.log(\"Child \" + idx + \" calc=\" + calc + \" pref=\" + tempsizes[idx].pref + \" min=\" + tempsizes[idx].min);\n total_pixels += tempsizes[idx].pref;\n if(tempsizes[idx].pref > tempsizes[idx].min)\n added_size += tempsizes[idx].pref - tempsizes[idx].min;\n }\n else // proportional: pr\n {\n tempsizes[idx].prop = parseInt(sizeobj.serverset,10);\n total_prop += tempsizes[idx].prop;\n\n if(logdistribute)\n console.log(\"Child \" + idx + \" prop=\" + tempsizes[idx].prop + \" min=\" + tempsizes[idx].min);\n }\n });\n\n /* - if we have any proportionally sized items\n - remaining_for_prop = (available - total_absolutes)\n - size_per_prop = maximum of (remaining_for_prop / total_props, minimum size)\n */\n var takeaway_prop = 0;\n var propleft;\n if(total_prop>0)\n {\n var spaceleft = available - total_pixels;\n propleft = total_prop;\n var prop = Math.floor(spaceleft / total_prop);\n\n if(logdistribute)\n console.log(\"Distribute remainders: props=\" + propleft + \" available=\" + spaceleft);\n sizeobjs.forEach(function(sizeobj, idx)\n {\n if(tempsizes[idx].prop)\n {\n // var part = Math.floor(spaceleft * tempsizes[idx].set.size / propleft);\n var part = prop * tempsizes[idx].prop;\n if(logdistribute)\n console.log(\"Child \" + idx + \" receiving \" + tempsizes[idx].prop + \"/\" + propleft + \" of \" + spaceleft + \"=\" + part + \" pixels, min=\" + tempsizes[idx].min);\n\n tempsizes[idx].pref = Math.max(part, tempsizes[idx].min);\n if (tempsizes[idx].pref > tempsizes[idx].min)\n takeaway_prop += tempsizes[idx].prop;\n spaceleft -= part;\n propleft -= tempsizes[idx].prop;\n\n total_pixels += tempsizes[idx].pref;\n if(tempsizes[idx].pref > tempsizes[idx].min)\n added_size += (tempsizes[idx].pref - tempsizes[idx].min);\n }\n });\n }\n\n propleft = takeaway_prop;\n while(total_pixels > available)\n {\n // ADDME: We could distribute the burden of handing over overcommitted pixels better\n if(logdistribute)\n console.log(\"Overcommitted (preferred sizes exceeded available) distribute the damage: overcommit=\"+(total_pixels-available)+\", available=\" + added_size + \", propleft=\" + propleft);\n\n var takenaway = 0;\n sizeobjs.forEach(function(sizeobj, idx)\n {\n var takeaway = 0;\n if (propleft && tempsizes[idx].prop)\n {\n // Using ceil to take at least 1 pixel if there are any pixels available (otherwise we could end up in an endless loop)\n takeaway = Math.ceil((total_pixels - available) * tempsizes[idx].prop / propleft);\n }\n else\n takeaway = total_pixels - available;\n takeaway = Math.min(tempsizes[idx].pref - tempsizes[idx].min, takeaway);\n if(takeaway)\n {\n if(logdistribute)\n console.log(\"Taking away \" + takeaway + \" pixels from child \" + idx);\n tempsizes[idx].pref -= takeaway;\n total_pixels -= takeaway;\n // If there are no more pixels to take away from this sizeobj, remove it from the remaining props\n if (tempsizes[idx].set && tempsizes[idx].prop == 1 && tempsizes[idx].pref == tempsizes[idx].min)\n propleft -= tempsizes[idx].prop;\n }\n takenaway += takeaway;\n });\n\n if (!takenaway)\n {\n console.error(\"distributeSizes was unable to fix its overcommit. Total needed=\" + total_pixels + \", available=\" + available + \" pixels\", sizeobjs);\n if (intolerant)\n throw new Error(\"distributeSizes was unable to fix its overcommit\");\n break;\n }\n }\n\n var remaining = available-total_pixels;\n if (remaining)\n {\n if (leftoverobj >= 0 && leftoverobj < sizeobjs.length)\n {\n if(logdistribute)\n console.log(\"We have \" + (available-total_pixels) + \" unassigned pixels, assign to child #\" + leftoverobj);\n tempsizes[leftoverobj].pref += remaining;\n remaining = 0;\n }\n else if (leftoverobj == -2)\n {\n if(logdistribute)\n console.log(\"We have \" + (available-total_pixels) + \" unassigned pixels, try to distribute evently over proportionals\");\n\n tempsizes.some(function(size, i)\n {\n if (size.prop)\n {\n ++size.pref;\n --remaining;\n }\n return !remaining; // stop if no pixels remaining\n });\n }\n }\n\n // Set sizes and fix any leftovers immediately\n sizeobjs.forEach(function(sizeobj, idx)\n {\n if(logdistribute)\n console.log(\"Child \" + idx + \" minimum=\" + tempsizes[idx].min + \" final=\" + tempsizes[idx].pref);\n sizeobj.set = tempsizes[idx].pref;\n });\n\n if(logdistribute)\n console.log(\"Finished layouting (\" + remaining + \" pixels remaining)\");\n return remaining;\n}\n\nclass ActionableComponent extends ToddCompBase\n{\n constructor(parentcomp, data, replacingcomp)\n {\n super(parentcomp, data, replacingcomp);\n }\n afterConstructor(data)\n {\n this.setEnabled(data.enabled);\n this.setAction(data.action);\n super.afterConstructor(data);\n }\n canBeFocusable()\n {\n return true;\n }\n setAction(newaction)\n {\n this.action=newaction;\n this.setInterestingActions(newaction ? [newaction] : []);\n this.onActionUpdated();\n }\n onActionUpdated()\n {\n this.node.classList.toggle(\"todd--disabled\", !this.getEnabled());\n }\n\n getEnabled()\n {\n // Check if the action is already available\n var action = this.action ? this.owner.getComponent(this.action) : null;\n // The button is enabled if it hasn't been disabled directly and it either has an enabled action or no action at all\n return this.enabled && (action ? action.isEnabled() : !this.action);\n }\n\n setEnabled(value)\n {\n this.enabled = value;\n this.node.setAttribute(\"tabindex\", this.getEnabled() && this.canBeFocusable() ? '0' : '-1');\n this.onActionUpdated();\n }\n}\n\nexport { ToddCompBase\n , ActionableComponent\n };\n", "import * as component from '@mod-tollium/web/ui/js/componentbase.es';\nimport $todd from \"@mod-tollium/web/ui/js/support\";\n\nlet ComponentBase = component.ToddCompBase;\nexport default ComponentBase;\n", "import ComponentBase from '@mod-tollium/webdesigns/webinterface/components/base/compbase';\n\nexport default class ActionForwardBase extends ComponentBase\n{\n constructor(parentcomp, data, replacingcomp)\n {\n super(parentcomp, data, replacingcomp);\n this.shortcut = data.shortcut;\n\n this.setEnabled(data.enabled);\n this.owner.registerComponentShortcut(this);\n }\n\n destroy()\n {\n this.owner.unregisterComponentShortcut(this);\n super.destroy();\n }\n\n onShortcut(event)\n {\n this.onExecute();\n }\n}\n", "/* @recommendedimport: import * as browser from 'dompack/extra/browser';\n\n Identify devices for the purpose of analytics/tracing\n NOT a library for feature detection!\n\n Based on Mootools.Browser\n*/\n/* eslint no-useless-escape: off */\n\nexport function parseUserAgent(ua)\n{\n ua = ua.toLowerCase();\n\n // chrome is included in the edge UA, so need to check for edge first, before checking if it's chrome.\n // safari is included in the miuibrowser UA, so need to check for miuibrowser first, before checking if it's safari.\n var UA = ua.match(/(edge|miuibrowser)[\\s\\/:]([\\w\\d\\.]+)/);\n if (!UA)\n UA = ua.match(/(opera|ie|firefox|chrome|trident|crios|version)[\\s\\/:]([\\w\\d\\.]+)?.*?(safari|(?:rv[\\s\\/:]|version[\\s\\/:])([\\w\\d\\.]+)|$)/);\n if (!UA) //try ios 11.4.1\n {\n UA = ua.match(/; cpu os ([\\d]+)/);\n if(UA)\n UA = [null, 'safari', parseInt(UA[1]) ];\n }\n if (!UA)\n UA = [null, 'unknown', 0];\n\n if (UA[1] == 'trident'){\n UA[1] = 'ie';\n if (UA[4]) UA[2] = UA[4];\n } else if (UA[1] == 'crios'){\n UA[1] = 'chrome';\n }\n\n let platform = ua.match(/ip(?:ad|od|hone)/) ? 'ios' : (ua.match(/(?:webos|android)/) || ua.match(/mac|win|linux/) || ['other'])[0];\n if (platform == 'win') platform = 'windows';\n\n let ret = { name: (UA[1] == 'version') ? UA[3] : UA[1],\n version: parseInt((UA[1] == 'opera' && UA[4]) ? UA[4] : UA[2]),\n platform: platform,\n device: ua.match(/ipad/) ? 'tablet' : [ 'ios', 'webos', 'android' ].includes(platform) ? 'mobile' : [ 'mac', 'windows', 'linux' ].includes(platform) ? 'desktop' : ''\n };\n if (ret.name == 'ie' && !ret.version && document.documentMode)\n ret.version = document.documentMode;\n\n return ret;\n}\n\n//module.exports =\nlet browser = parseUserAgent(navigator.userAgent);\n\nexport function getName()\n{\n return browser.name;\n}\nexport function getVersion()\n{\n return browser.version;\n}\nexport function getPlatform()\n{\n return browser.platform;\n}\nexport function getDevice()\n{\n return browser.device;\n}\nexport function getTriplet()\n{\n return browser.platform + '-' + browser.name + '-' + browser.version;\n}\n", "import * as dompack from \"dompack\";\nimport * as browser from \"dompack/extra/browser\";\n\nconst screenshotVersion = 1;\n\n/** Take a DOM snapshot\n*/\nexport default function takeScreenshot(domFilterCallback)\n{\n let bodyNode = document.createElement(\"div\");\n cloneNodeContents(document.body, bodyNode, domFilterCallback);\n\n const styleSheets = [ ...document.styleSheets ].map(sheet => [ ...sheet.cssRules ].map(rule =>\n {\n // In IE11, the cssText of a keyframe(s) rule cannot be accessed\n if (rule.type != CSSRule.KEYFRAME_RULE && rule.type != CSSRule.KEYFRAMES_RULE)\n return rule.cssText;\n return \"\";\n }).join(''));\n const htmlAttrs = [ ...document.documentElement.attributes ].map(attr => { return { name: attr.name, value: attr.value }; });\n const bodyAttrs = [ ...document.body.attributes ].map(attr => { return { name: attr.name, value: attr.value }; });\n\n return (\n { version: screenshotVersion\n , screenshot:\n { htmlAttrs\n , styleSheets\n , bodyAttrs\n , bodyContents: bodyNode.innerHTML\n }\n , size: { width: window.innerWidth, height: window.innerHeight }\n , browser: browser.getTriplet()\n , device: browser.getDevice()\n , userAgent: window.navigator.userAgent\n }\n );\n}\n\nfunction cloneNodeContents(source, target, domFilterCallback)\n{\n for (const childNode of Array.from(source.childNodes))\n {\n if (isNodeVisible(childNode))\n {\n if (!domFilterCallback || domFilterCallback(childNode))\n {\n let childClone = childNode.cloneNode(false);\n if (childClone.nodeType === Node.ELEMENT_NODE && childClone.nodeName === \"IFRAME\")\n {\n childClone.removeAttribute(\"src\");\n childClone.setAttribute(\"sandbox\", \"\");\n }\n if (childNode.scrollTop)\n childClone.dataset.whScreenshotScrollTop = childNode.scrollTop;\n if (childNode.scrollLeft)\n childClone.dataset.whScreenshotScrollLeft = childNode.scrollLeft;\n\n cloneNodeContents(childNode, childClone);\n dompack.append(target, childClone);\n }\n }\n }\n}\n\nfunction isNodeVisible(node)\n{\n if (!node.getBoundingClientRect)\n return true;\n const rect = node.getBoundingClientRect();\n return (rect.width || rect.height)\n && rect.right >= 0 && rect.bottom >= 0\n && rect.left <= window.innerWidth && rect.top <= window.innerHeight;\n}\n", "import * as dompack from \"dompack\";\n\nlet deferred, highlighter, highlightCallback;\n\nexport default function pointAtDOM(event, options)\n{\n if (deferred)\n return Promise.reject(new Error(\"Already pointing at DOM\"));\n\n highlightCallback = options.highlightCallback;\n\n deferred = dompack.createDeferred();\n if (!highlighter)\n highlighter = ;\n\n activateDOMPointer();\n\n if (event)\n highlightDOM(event);\n\n return deferred.promise;\n}\n\nfunction activateDOMPointer()\n{\n window.addEventListener(\"mousemove\", highlightDOM, true);\n window.addEventListener(\"click\", captureDOMNode, true);\n window.addEventListener(\"keydown\", maybeCancelDOMPointer, true);\n}\n\nfunction deactivateDOMPointer()\n{\n window.removeEventListener(\"mousemove\", highlightDOM, true);\n window.removeEventListener(\"click\", captureDOMNode, true);\n window.removeEventListener(\"keydown\", maybeCancelDOMPointer, true);\n}\n\nfunction resolveWithResult(result)\n{\n deactivateDOMPointer();\n const resolve = deferred.resolve;\n deferred = null;\n document.body.removeChild(highlighter);\n resolve(result);\n}\n\nfunction maybeCancelDOMPointer(event)\n{\n dompack.stop(event);\n\n if (event.which === 27 && deferred)\n resolveWithResult();\n}\n\nfunction highlightDOM(event)\n{\n const hoverNode = getHoveredDOMNode(event);\n if (hoverNode) {\n const rect = hoverNode.getBoundingClientRect();\n highlighter.style.top = rect.top + \"px\";\n highlighter.style.left = rect.left + \"px\";\n highlighter.style.width = rect.width + \"px\";\n highlighter.style.height = rect.height + \"px\";\n document.body.appendChild(highlighter);\n } else\n document.body.removeChild(highlighter);\n}\n\nfunction captureDOMNode(event)\n{\n dompack.stop(event);\n\n if (deferred) {\n const hoverNode = getHoveredDOMNode(event);\n if (!hoverNode)\n resolveWithResult();\n else {\n const rect = hoverNode.getBoundingClientRect();\n resolveWithResult({ top: rect.top, left: rect.left, width: rect.width, height: rect.height });\n }\n }\n}\n\nfunction getHoveredDOMNode(event)\n{\n const el = document.elementFromPoint(event.clientX, event.clientY);\n return highlightCallback ? highlightCallback(el) : el;\n}\n", "import * as dompack from 'dompack';\n\n//just number RPCs globally instead of per server, makes debug ouput more useful\nlet globalseqnr = 1;\n\nfunction getDebugAppend()\n{\n let urldebugvar = window.location.href.match(new RegExp('[?]wh-debug=([^?]*)'));\n return urldebugvar ? '?wh-debug='+urldebugvar[1] : '';\n}\n\n/* this is the followup for net/jsonrpc.es - we can hopefully clear net/ someday\n and move net/eventserver to wh/eventserver.es then */\n\nclass ControlledCall\n{\n constructor(client, method, stack, id, options, callurl, fetchoptions)\n {\n this.client = client;\n this.options = options;\n\n\n // if(options.timeout || options.signal) //as long as rpcResolve exists, we'll ALWAYS need to setup a controller\n {\n this.abortcontroller = new AbortController;\n fetchoptions.signal = this.abortcontroller.signal;\n\n if(options.timeout > 0)\n {\n this.timeout = options.timeout;\n setTimeout(() => this._handleTimeout(), options.timeout);\n }\n if(options.signal)\n {\n options.signal.addEventListener(\"abort\", () => this._abort());\n }\n }\n\n this._callurl = callurl;\n this._fetchoptions = fetchoptions;\n\n let fetchpromise = fetch(this._callurl, this._fetchoptions);\n this.promise = this._completeCall(method, stack, id, fetchpromise);\n this.promise.__jsonrpcinfo = this;\n }\n _handleTimeout()\n {\n this.timedout = true;\n this.abortcontroller.abort();\n }\n _abort()\n {\n this.aborted = true;\n this.abortcontroller.abort();\n }\n _legacyResolve(resolution)\n {\n this.legacyresolve = resolution;\n this.abortcontroller.abort();\n }\n async _completeCall(method, stack, id, fetchpromise)\n {\n let response;\n try\n {\n while(true) //loop for 429\n {\n response = await fetchpromise;\n if(response.status == 429 && !(\"retry429\" in this.options && !this.options.retry429) && response.headers.get(\"Retry-After\"))\n {\n let retryafter = parseInt(response.headers.get(\"Retry-After\"));\n if(this.options.debug)\n console.warn(`[rpc] We are being throttled (429 Too Many Requests) - retrying after ${retryafter} seconds`);\n\n await new Promise( resolve => setTimeout(resolve, retryafter*1000) );\n fetchpromise = fetch(this._callurl, this._fetchoptions);\n continue;\n }\n break;\n }\n }\n catch(exception)\n {\n if(this.options.debug)\n console.log(`[rpc] #${id} Exception invoking '${method}'`, exception);\n\n if(this.aborted)\n throw new Error(`RPC Aborted`);\n else if(this.timedout)\n throw new Error(`RPC Timeout: timeout was set to ${this.timeout} milliseconds`);\n else if(this.legacyresolve && this.legacyresolve.resolve)\n return this.legacyresolve.resolve;\n else\n throw new Error(`RPC Failed: exception: ` + exception);\n }\n\n let jsonresponse;\n try\n {\n jsonresponse = await response.json();\n if(this.options.debug)\n console.log(`[rpc] #${id} Received response to '${method}'`, jsonresponse);\n }\n catch(exception)\n {\n if(this.options.debug)\n console.warn(`[rpc] #${id} Response was not valid JSON`, exception);\n }\n\n if(!jsonresponse)\n throw new Error(\"RPC Failed: Invalid JSON/RPC response received\");\n\n if(jsonresponse && jsonresponse.error)\n {\n this.client._tryLogError(stack, jsonresponse.error);\n throw new Error(\"RPC Error: \" + (jsonresponse.error.message || \"Unknown error\"));\n }\n\n if(response.status == 200 && jsonresponse && jsonresponse.id !== id)\n throw new Error(\"RPC Failed: Invalid JSON/RPC response received\");\n\n if(this.options.wrapresult)\n {\n return { status: response.status\n , result: jsonresponse.result || null\n , error: jsonresponse.error || null\n , retryafter: response.headers.get(\"Retry-After\") ? parseInt(response.headers.get(\"Retry-After\")) : null\n };\n }\n\n return jsonresponse.result;\n }\n}\n\n/** Invokes (WebHare) JSON/RPC\n @param url URL to invoke (leave empty or pass no parameters at all to callback to the current page)\n @cell options.timeout Default timeout for all calls\n @cell options.debug Debug (Follows 'rpc' debugflag if not explicity specified) */\nexport default class RPCClient\n{\n constructor(url, options)\n {\n this.options = { timeout: 0\n , debug: dompack.debugflags.rpc\n , ...options\n };\n\n let whservicematch;\n if(url)\n {\n whservicematch = url.match(/^([a-z0-9_]+):([a-z0-9_]+)$/);\n if(whservicematch)\n this.url = `${location.origin}/wh_services/${whservicematch[1]}/${whservicematch[2]}`;\n else\n this.url = url;\n }\n else\n {\n this.url = location.href; //invoke ourselves directly if no path specified\n }\n\n //if shorthand syntax is used, we know we're talking to our local webhare. add function names and the profiling flag if needed\n this.addfunctionname = this.options.addfunctionname !== undefined ? this.options.addfunctionname : !!whservicematch;\n this.urlappend = this.options.urlappend !== undefined ? this.options.urlappend : whservicematch ? getDebugAppend() : \"\";\n }\n\n setOptions(options)\n {\n this.options = {...this.options, ...options};\n }\n\n _handleLegacyRPCResolve(promise, result)\n {\n if(!promise.__jsonrpcinfo)\n throw new Error(\"The promise is not an async JSONRPC request\");\n promise.__jsonrpcinfo._legacyResolve({resolve:result});\n }\n\n _tryLogError(stack,error)\n {\n let trace = error.data ? (error.data.trace || error.data.list || []) : [];\n\n console.group();\n console.warn(\"RPC failed:\", error.message);\n trace.forEach(rec =>\n {\n if (rec.filename || rec.line)\n {\n var line = rec.filename + '#' + rec.line + '#' + rec.col + (rec.func ? ' (' + rec.func + ')' : '');\n console.log(line);\n }\n });\n if(stack)\n {\n console.warn(\"Stack at calling point\");\n console.log(stack);\n }\n console.groupEnd();\n }\n\n invoke(...params)\n {\n let options;\n if(typeof params[0] == \"object\")\n options = {...this.options, ...params.shift()};\n else\n options = this.options;\n\n let method = params.shift();\n\n //build the URL, add profiling and function parameters where needed\n let callurl = this.url;\n if(this.addfunctionname) //simplifies log analysis, ignored by the server\n callurl += `/${method}`;\n callurl += this.urlappend;\n\n let id = ++globalseqnr;\n let stack;\n\n if(options.debug)\n {\n stack = new Error().stack;\n console.log(`[rpc] #${id} Invoking '${method}'`, params, callurl);\n }\n\n let fetchoptions = { method: \"POST\"\n , credentials: 'same-origin' //this is the default since 2017-08-25, but Edge pre-18 is still around and will fail here\n , headers: { \"Accept\": \"application/json\"\n , \"Content-Type\": \"application/json; charset=utf-8\"\n }\n , body: JSON.stringify(\n { id: id\n , method: method\n , params: params || []\n })\n , keepalive: Boolean(options.keepalive)\n };\n\n return new ControlledCall(this, method, stack, id, options, callurl, fetchoptions).promise;\n }\n}\n", "// Auto-generated RPC interface from /opt/wh/whtree/modules/publisher/js/feedback/internal/feedback.rpc.json\nvar RPCClient = require(\"@mod-system/js/wh/rpc\").default;\nvar request = exports.rpcclient = new RPCClient(\"publisher:feedback\");\nexports.rpcResolve = function(promise, result) { request._handleLegacyRPCResolve(promise, result) };\nexports.invoke = function() { return request.invoke.apply(request,Array.prototype.slice.call(arguments)); }\n\nObject.defineProperty(module.exports, \"HTTP_ERROR\", { get: function() { return JSONRPC.HTTP_ERROR; }});\nObject.defineProperty(module.exports, \"JSON_ERROR\", { get: function() { return JSONRPC.JSON_ERROR; }});\nObject.defineProperty(module.exports, \"PROTOCOL_ERROR\", { get: function() { return JSONRPC.PROTOCOL_ERROR; }});\nObject.defineProperty(module.exports, \"RPC_ERROR\", { get: function() { return JSONRPC.RPC_ERROR; }});\nObject.defineProperty(module.exports, \"OFFLINE_ERROR\", { get: function() { return JSONRPC.OFFLINE_ERROR; }});\nObject.defineProperty(module.exports, \"TIMEOUT_ERROR\", { get: function() { return JSONRPC.TIMEOUT_ERROR; }});\nObject.defineProperty(module.exports, \"SERVER_ERROR\", { get: function() { return JSONRPC.SERVER_ERROR; }});\n\n// Adding dependency: '/opt/wh/whtree/modules/publisher/lib/internal/feedback/rpc.whlib'\n\nexports.storeFeedback = exports.StoreFeedback = /*RECORD*/function(/*STRING*/ pathname, /*STRING*/ scope, /*RECORD*/ data)\n{\nreturn request.invoke.apply(request,[\"StoreFeedback\"].concat(Array.prototype.slice.call(arguments)));\n}\n", "import takeScreenshot from \"./screenshot\";\nimport pointAtDOM from \"./dompointer\";\nimport * as service from \"./internal/feedback.rpc.json\";\nimport \"@mod-publisher/web/common/feedback/styles.css\";\n\nconst defaultOptions =\n { scope: \"\"\n , addElement: true\n , highlightCallback: null\n , domFilterCallback: null\n , feedbackPromise: null\n };\nlet feedbackOptions;\n\n/** @short Initialize the global feedback options\n @param options New options\n @cell(string) options.scope The feedback scope (required)\n @cell(boolean) options.addElement If the user should be asked to point at an element\n @cell(function) options.highlightCallback A function that, given a hovered element, returns the element that should be\n highlighted (optional, by default the hovered element is highlighted)\n @cell(function) options.domFilterCallback A function that, given a DOM element, returns whether the element returns if\n the element should be included in the screenshot (optional, by default all elements are included)\n @cell(function) options.feedbackPromise A function that returns a Promise, which resolves with extra data (a record-like\n object) to add to the feedback\n*/\nexport function initFeedback(options)\n{\n feedbackOptions = { ...defaultOptions, ...options };\n}\n\n/** @short Get feedback\n @param event The event that caused requesting the feedback (optional)\n @param extraOptions Extra option, overwriting the global options @includecelldef %initFeedback.options\n @return The result\n @cell(boolean) return.success If the feedback was successfully stored\n @cell(string) return.guid If successful, the feedback GUID\n @cell(string) return.error If not successful, an error message\n*/\nexport async function getFeedback(event, extraOptions)\n{\n const options = { ...feedbackOptions, ...extraOptions };\n if (!options.scope)\n console.error(`No scope supplied for feedback`);\n const element = options.addElement ? await pointAtDOM(event, options) : null;\n if (!options.addElement || element)\n {\n const data = takeScreenshot(options.domFilterCallback);\n const extraData = options.feedbackPromise ? await options.feedbackPromise() : {};\n if (extraData)\n return await service.storeFeedback(location.pathname, options.scope, { ...data, element, extraData });\n }\n return { success: false, error: \"cancelled\" };\n}\n", "// Auto-generated RPC interface from /opt/wh/whtree/modules/tollium/js/internal/todd.rpc.json\nvar RPCClient = require(\"@mod-system/js/wh/rpc\").default;\nvar request = exports.rpcclient = new RPCClient(\"tollium:todd\");\nexports.rpcResolve = function(promise, result) { request._handleLegacyRPCResolve(promise, result) };\nexports.invoke = function() { return request.invoke.apply(request,Array.prototype.slice.call(arguments)); }\n\nObject.defineProperty(module.exports, \"HTTP_ERROR\", { get: function() { return JSONRPC.HTTP_ERROR; }});\nObject.defineProperty(module.exports, \"JSON_ERROR\", { get: function() { return JSONRPC.JSON_ERROR; }});\nObject.defineProperty(module.exports, \"PROTOCOL_ERROR\", { get: function() { return JSONRPC.PROTOCOL_ERROR; }});\nObject.defineProperty(module.exports, \"RPC_ERROR\", { get: function() { return JSONRPC.RPC_ERROR; }});\nObject.defineProperty(module.exports, \"OFFLINE_ERROR\", { get: function() { return JSONRPC.OFFLINE_ERROR; }});\nObject.defineProperty(module.exports, \"TIMEOUT_ERROR\", { get: function() { return JSONRPC.TIMEOUT_ERROR; }});\nObject.defineProperty(module.exports, \"SERVER_ERROR\", { get: function() { return JSONRPC.SERVER_ERROR; }});\n\n// Adding dependency: '/opt/wh/whtree/modules/tollium/lib/todd/internal/service.whlib'\n\nexports.runToddComm = exports.RunToddComm = /*RECORD*/function(/*RECORD*/ request)\n{\nreturn request.invoke.apply(request,[\"RunToddComm\"].concat(Array.prototype.slice.call(arguments)));\n}\n\nexports.retrieveImages = exports.RetrieveImages = /*RECORD*/function(/*RECORD ARRAY*/ images, /*BOOLEAN*/ nocache, /*BOOLEAN*/ nobroken, /*BOOLEAN*/ rewritestyles)\n{\nreturn request.invoke.apply(request,[\"RetrieveImages\"].concat(Array.prototype.slice.call(arguments)));\n}\n\nexports.getWebdavOpenInfo = exports.GetWebdavOpenInfo = /*RECORD*/function(/*STRING*/ pathname, /*STRING*/ item, /*RECORD*/ data)\n{\nreturn request.invoke.apply(request,[\"GetWebdavOpenInfo\"].concat(Array.prototype.slice.call(arguments)));\n}\n", "import * as dompack from \"dompack\";\nimport * as browser from \"dompack/extra/browser\";\nimport * as toddrpc from \"./internal/todd.rpc.json\";\n\nlet usecanvas = browser.getName()==\"ie\" || dompack.debugflags.ilc;\n\n// Canvas pixel ratio\nvar canvasRatio = (function()\n{\n let ctx = document.createElement(\"canvas\").getContext(\"2d\");\n let devicePixelRatio = window.devicePixelRatio || 1\n , backingStoreRatio = ctx.webkitBackingStorePixelRatio ||\n ctx.mozBackingStorePixelRatio ||\n ctx.msBackingStorePixelRatio ||\n ctx.oBackingStorePixelRatio ||\n ctx.backingStorePixelRatio || 1;\n return devicePixelRatio / backingStoreRatio;\n})();\n\n// The images to load\nvar imagequeue = new Map();\n\n// The images that are already loaded\nvar imagecache = new Map();\n\n// Used to coalesce image loading\nvar loadimgtimeout = null;\n\n// Load the image(s) and apply to an node src\nexport function updateCompositeImage(imgnode, imgnames, width, height, color)\n{\n if(usecanvas && imgnode.nodeName=='IMG')\n {\n console.error(\"img should be a