ES6: port datepicker and util to ES6
Change-Id: I203aa47229e6c4dacd3a33bea39c512f4576a182
diff --git a/dev/js/src-lib/util/test/utilSpec.js b/dev/js/src-lib/util/test/utilSpec.js
new file mode 100644
index 0000000..f7bcf54
--- /dev/null
+++ b/dev/js/src-lib/util/test/utilSpec.js
@@ -0,0 +1,66 @@
+import { describe, it, expect, beforeAll } from 'vitest';
+
+let initTogglePwdVisibility;
+let initCopyToClipboard;
+
+beforeAll(async function () {
+ const utilModule = await import('../util.js');
+ initTogglePwdVisibility = utilModule.initTogglePwdVisibility;
+ initCopyToClipboard = utilModule.initCopyToClipboard;
+});
+
+describe('KorAP.util', function () {
+
+ it('should quote', function () {
+ expect('Baum'.quote()).toEqual('"Baum"');
+ expect('B"a"um'.quote()).toEqual('"B\\"a\\"um"');
+ });
+
+ it('should escape regex', function () {
+ expect('aaa/bbb\/ccc'.escapeRegex()).toEqual('aaa\\/bbb\\/ccc');
+ });
+
+ it('should slugify', function () {
+ expect('/korap/test'.slugify()).toEqual('koraptest');
+ expect('korap test'.slugify()).toEqual('korap-test');
+ expect('Korap Test'.slugify()).toEqual('korap-test');
+ });
+});
+
+describe('KorAP.util.initTogllePwdVisibility', function () {
+ it('should toggle', function () {
+ const div = document.createElement('div');
+ let input = div.addE('input');
+ input.setAttribute('type', 'password');
+ input.setAttribute('class', 'show-pwd');
+
+ expect(div.children.length).toEqual(1);
+ initTogglePwdVisibility(div);
+ expect(div.children.length).toEqual(2);
+ expect(div.lastChild.tagName).toEqual("A");
+ expect(div.lastChild.classList.contains("hide")).toBeFalsy();
+ expect(input.getAttribute("type")).toEqual("password");
+
+ div.lastChild.click();
+
+ expect(input.getAttribute("type")).toEqual("text");
+ expect(div.lastChild.classList.contains("hide")).toBeTruthy();
+ });
+});
+
+describe('KorAP.util.initCopyToClipboard', function () {
+ it('should be initializable', function () {
+ const div = document.createElement('div');
+ let input = div.addE('input');
+ input.value = "abcde";
+ input.setAttribute('type', 'text');
+ input.setAttribute('class', 'copy-to-clipboard');
+ expect(div.children.length).toEqual(1);
+ initCopyToClipboard(div);
+ expect(div.children.length).toEqual(2);
+ expect(div.lastChild.tagName).toEqual("A");
+ });
+
+ // document.execCommand() can't be tested without user
+ // intervention.
+});
diff --git a/dev/js/src-lib/util/util.js b/dev/js/src-lib/util/util.js
new file mode 100644
index 0000000..3a8c65e
--- /dev/null
+++ b/dev/js/src-lib/util/util.js
@@ -0,0 +1,230 @@
+window.KorAP = window.KorAP || {};
+const KorAP = window.KorAP;
+
+// Don't let events bubble up
+if (Event.halt === undefined) {
+ // Don't let events bubble up
+ Event.prototype.halt = function () {
+ this.stopPropagation();
+ this.preventDefault();
+ };
+};
+
+const _quoteRE = new RegExp("([\"\\\\])", 'g');
+String.prototype.quote = function () {
+ return '"' + this.replace(_quoteRE, '\\$1') + '"';
+};
+
+const _escapeRE = new RegExp("([\/\\\\])", 'g');
+String.prototype.escapeRegex = function () {
+ return this.replace(_escapeRE, '\\$1');
+};
+
+const _slug1RE = new RegExp("[^-a-zA-Z0-9_\\s]+", 'g');
+const _slug2RE = new RegExp("[-\\s]+", 'g');
+String.prototype.slugify = function () {
+ return this.toLowerCase().replace(_slug1RE, '').replace(_slug2RE, '-');
+};
+
+/**
+ * Upgrade this object to another object,
+ * while private data stays intact.
+ *
+ * @param {Object} An object with properties.
+ */
+Object.defineProperty(Object.prototype, 'upgradeTo', {
+ value: function (props) {
+ for (let prop in props) {
+ this[prop] = props[prop];
+ };
+ return this;
+ },
+ enumerable: false,
+ configurable: true,
+ writable: true
+});
+
+
+// Add toggleClass method similar to jquery
+HTMLElement.prototype.toggleClass = function (c1, c2) {
+ const cl = this.classList;
+ if (cl.contains(c1)) {
+ cl.add(c2);
+ cl.remove(c1);
+ }
+ else {
+ cl.remove(c2);
+ cl.add(c1);
+ };
+};
+
+// Append element by tag name
+HTMLElement.prototype.addE = function (tag) {
+ return this.appendChild(document.createElement(tag));
+};
+
+// Append text node
+HTMLElement.prototype.addT = function (text) {
+ return this.appendChild(document.createTextNode(text));
+};
+
+
+// Utility for removing all children of a node
+function _removeChildren (node) {
+ // Remove everything underneath
+ while (node.firstChild)
+ node.removeChild(node.firstChild);
+};
+
+
+// Utility to get either the charCode
+// or the keyCode of an event
+function _codeFromEvent (e) {
+ if ((e.charCode) && (e.keyCode==0))
+ return e.charCode
+ return e.keyCode;
+};
+
+function _dec2hex (dec) {
+ return ('0' + dec.toString(16)).substr(-2)
+};
+
+
+/**
+ * Create random identifiers
+ */
+/*
+ * code based on
+ * https://stackoverflow.com/questions/1349404/generate-random-string-characters-in-javascript#8084248
+ */
+function randomID (len) {
+ const arr = new Uint8Array((len || 40) / 2)
+ window.crypto.getRandomValues(arr)
+ return Array.from(arr, _dec2hex).join('')
+};
+
+
+/**
+ * Add option to show passwords.
+ */
+function initTogglePwdVisibility (element) {
+ const el = element.querySelectorAll("input[type=password].show-pwd");
+ for (let x = 0; x < el.length; x++) {
+ const pwd = el[x];
+
+ const a = document.createElement('a');
+ a.classList.add('show-pwd');
+ a.addEventListener('click', function () {
+ if (pwd.getAttribute("type") === "password") {
+ pwd.setAttribute("type", "text");
+ a.classList.add('hide');
+ return;
+ };
+ pwd.setAttribute("type", "password");
+ a.classList.remove('hide');
+ });
+ pwd.parentNode.insertBefore(a, pwd.nextSibling);
+ };
+};
+
+
+/**
+ * Add option to copy to clipboard.
+ */
+function initCopyToClipboard (element) {
+ const el = element.querySelectorAll("input.copy-to-clipboard");
+ for (let x = 0; x < el.length; x++) {
+ const text = el[x];
+ const a = document.createElement('a');
+ a.classList.add('copy-to-clipboard');
+ a.addEventListener('click', function () {
+ let back = false;
+ if (text.getAttribute("type") === 'password') {
+ text.setAttribute("type", "text");
+ back = true;
+ };
+ text.select();
+ text.setSelectionRange(0, 99999);
+ document.execCommand("copy");
+ if (back) {
+ text.setAttribute("type", "password");
+ };
+ });
+ text.parentNode.insertBefore(a, text.nextSibling);
+ };
+};
+
+
+// Todo: That's double now!
+KorAP.API = KorAP.API || {};
+KorAP.Locale = KorAP.Locale || {};
+
+const loc = KorAP.Locale;
+loc.OR = loc.OR || 'or';
+loc.AND = loc.AND || 'and';
+
+// Add new stylesheet object lazily to document
+KorAP.newStyleSheet = function () {
+ if (KorAP._sheet === undefined) {
+ const sElem = document.createElement('style');
+ document.head.appendChild(sElem);
+ KorAP._sheet = sElem.sheet;
+ };
+ return KorAP._sheet;
+};
+
+
+// Default log message
+KorAP.log = KorAP.log || function (type, msg, src) {
+ if (src)
+ msg += ' from ' + src;
+ console.log(type + ": " + msg);
+};
+
+/**
+ * A Method for generating an array of nodes, that are direct descendants of the passed
+ * element node, using a tag tagName as a parameter. Supposed to be used by the specification only.
+ * @param {HTMLNode} element The HTMLNode / element object whose children we are fetching
+ * @param {String} tagName The tag the children are looked for by
+ * @returns An array of children nodes with tag tagName
+ */
+function directElementChildrenByTagName (element, tagName) {
+ const tagElementsCollection=element.getElementsByTagName(tagName);
+ //var tagElements = Array.from(tagElementsCollection);
+ //var tagElements = [...tagElementsCollection];
+ //This one has the best compatability:
+ var tagElements = Array.prototype.slice.call(tagElementsCollection);
+ //filter by actually being direct child node
+ tagElements = tagElements.filter(subElement => subElement.parentNode === element);
+ return tagElements;
+};
+
+/**
+ * A Method for generating an array of nodes, that are direct descendants of the passed
+ * element node, using a class className as a parameter. Supposed to be used by the specification only.
+ * @param {HTMLNode} element The HTMLNode / element object whose children we are fetching
+ * @param {String} className The class the children are looked for by
+ * @returns An array of children nodes with class className
+ */
+ function directElementChildrenByClassName (element, className) {
+ const classElementsCollection=element.getElementsByTagName(className);
+ //var classElements = Array.from(classElementsCollection);
+ //var classElements = [...classElementsCollection];
+ //This one has the best compatability:
+ var classElements = Array.prototype.slice.call(classElementsCollection);
+ //filter by actually being direct child node
+ classElements = classElements.filter(subElement => subElement.parentNode === element);
+ return classElements;
+};
+
+export {
+ _removeChildren as removeChildren,
+ _codeFromEvent as codeFromEvent,
+ randomID,
+ initTogglePwdVisibility,
+ initCopyToClipboard,
+ directElementChildrenByTagName,
+ directElementChildrenByClassName
+};
+
+export default KorAP;
\ No newline at end of file