2 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
13 * or implied. See the License for the specific language governing
14 * permissions and limitations under the License.
20 export default class Common {
22 // ///////////////////////////////////////////////////////////////////////////////////////////////
25 * Retrieve and start a simple timer. Retrieve elapsed time by calling #ms().
29 const start = new Date().getTime();
32 return (new Date().getTime() - start);
37 // ///////////////////////////////////////////////////////////////////////////////////////////////
40 * Get datatype, stripping '[object Boolean]' to just 'Boolean'.
42 * @return String like String, Number, Date, Null, Undefined, stuff like that.
45 const str = Object.prototype.toString.call(o);
46 const prefix = '[object ';
47 if (str.substr(str, prefix.length) === prefix) {
48 return str.substr(prefix.length, str.length - (prefix.length + 1));
53 // ///////////////////////////////////////////////////////////////////////////////////////////////
56 * Assert that an argument was provided.
57 * @param value to be checked.
58 * @param message message on assertion failure.
61 static assertNotNull(value, message = 'Unexpected null value') {
63 throw new Error(message);
68 // ///////////////////////////////////////////////////////////////////////////////////////////////
71 * Assert argument type.
72 * @param value to be checked.
73 * @param expected expected type string, e,g. Number from [object Number].
76 static assertType(value, expected) {
77 const type = this.getType(value);
78 if (type !== expected) {
79 throw new Error(`Expected type ${expected}, got ${type}`);
84 // ///////////////////////////////////////////////////////////////////////////////////////////////
87 * Assert argument type.
88 * @param value to be checked.
89 * @param unexpected unexpected type string, e,g. Number from [object Number].
92 static assertNotType(value, unexpected) {
93 const type = this.getType(value);
94 if (type === unexpected) {
95 throw new Error(`Forbidden type "${unexpected}"`);
100 // ///////////////////////////////////////////////////////////////////////////////////////////////
103 * Assert argument is a simple JSON object, and specifically not (something like an) ES6 class.
104 * @param value to be checked.
107 static assertPlainObject(value) {
108 Common.assertType(value, 'Object');
111 if (!($.isPlainObject(value))) {
112 throw new Error(`Expected plain object: ${value}`);
118 // ///////////////////////////////////////////////////////////////////////////////////////////////
121 * Assert argument type.
122 * @param value to be checked.
123 * @param c expected class.
126 static assertInstanceOf(value, c) {
127 Common.assertNotNull(value);
128 if (!(value instanceof c)) {
129 throw new Error(`Expected instanceof ${c}: ${value}`);
134 // ///////////////////////////////////////////////////////////////////////////////////////////////
137 * Assert that a string matches a regex.
138 * @param value value to be tested.
139 * @param re pattern to be applied.
142 static assertMatches(value, re) {
143 this.assertType(value, 'String');
144 this.assertType(re, 'RegExp');
145 if (!re.test(value)) {
146 throw new Error(`Value ${value} doesn't match pattern ${re}`);
151 // ///////////////////////////////////////////////////////////////////////////////////////////////
154 * Assert the value of a boolean.
156 * @param bool to be checked.
157 * @param message optional message on assertion failure.
160 static assertThat(bool, message) {
162 throw new Error(message || `Unexpected: ${bool}`);
167 // ///////////////////////////////////////////////////////////////////////////////////////////////
170 * Verify that a value, generally a function arg, is a DOM element.
171 * @param value to be checked.
174 static assertHTMLElement(value) {
175 if (!Common.isHTMLElement(value)) {
176 throw new Error(`Expected HTMLElement: ${value}`);
181 // ///////////////////////////////////////////////////////////////////////////////////////////////
184 * Check whether a value, generally a function arg, is an HTML DOM element.
185 * @param o to be checked.
186 * @return true if DOM element.
188 static isHTMLElement(o) {
189 if (typeof HTMLElement === 'object') {
190 return o instanceof HTMLElement;
192 return o && typeof o === 'object' && o !== null
193 && o.nodeType === 1 && typeof o.nodeName === 'string';
196 // ///////////////////////////////////////////////////////////////////////////////////////////////
199 * Check if a string is non-empty.
200 * @param s string to be checked.
201 * @returns false if non-blank string, true otherwise.
204 if (Common.getType(s) === 'String') {
205 return (s.trim().length === 0);
210 // ///////////////////////////////////////////////////////////////////////////////////////////////
213 * Detect dates that are numbers, milli/seconds since epoch..
215 * @param n candidate number.
219 return !isNaN(parseFloat(n)) && isFinite(n);
222 // ///////////////////////////////////////////////////////////////////////////////////////////////
225 * Parse the text output from a template to a DOM element.
226 * @param txt input text.
229 static txt2dom(txt) {
230 return new DOMParser().parseFromString(txt, 'image/svg+xml').documentElement;
233 // ///////////////////////////////////////////////////////////////////////////////////////////////
236 * Recursively convert a DOM element to an SVG (namespaced) element. Otherwise
237 * you get HTML elements that *happen* to have SVG names, but which aren't actually SVG.
239 * @param node DOM node to be converted.
240 * @param svg to be updated.
241 * @returns {*} for chaining.
243 static dom2svg(node, svg) {
245 Common.assertNotType(node, 'String');
247 if (node.childNodes && node.childNodes.length > 0) {
249 for (const c of node.childNodes) {
250 switch (c.nodeType) {
251 case document.TEXT_NODE:
252 svg.text(c.nodeValue);
259 for (const c of node.childNodes) {
260 switch (c.nodeType) {
261 case document.ELEMENT_NODE:
262 Common.dom2svg(c, svg.append(`svg:${c.nodeName.toLowerCase()}`));
270 if (node.hasAttributes()) {
271 for (let i = 0; i < node.attributes.length; i++) {
272 const a = node.attributes.item(i);
273 svg.attr(a.name, a.value);
280 // ///////////////////////////////////////////////////////////////////////////////////////////////
283 * Get the lines to be shown in the label.
285 * @param labelText original label text.
286 * @param wordWrapAt chars at which to break words.
287 * @param lineWrapAt chars at which to wrap.
288 * @param maximumLines lines at which to truncate.
291 static tokenize(labelText = '', wordWrapAt, lineWrapAt, maximumLines) {
295 // Hyphenate and break long words.
297 const regex = new RegExp(`(\\w{${wordWrapAt - 1}})(?=\\w)`, 'g');
298 l = l.replace(regex, '$1- ');
300 const labelTokens = l.split(/\s+/);
303 for (const labelToken of labelTokens) {
304 if (label.length > 0) {
305 const length = label.length + labelToken.length + 1;
306 if (length > lineWrapAt) {
307 lines.push(label.trim());
312 label = `${label} ${labelToken}`;
316 lines.push(label.trim());
319 const truncated = lines.slice(0, maximumLines);
320 if (truncated.length < lines.length) {
321 let finalLine = truncated[maximumLines - 1];
322 if (finalLine.length > (lineWrapAt - 4)) {
323 finalLine = finalLine.substring(0, lineWrapAt - 4);
325 finalLine = `${finalLine} ...`;
326 truncated[maximumLines - 1] = finalLine;
332 // ///////////////////////////////////////////////////////////////////////////////////////////////
335 * Brutally sanitize an input string. We have no syntax rules, and hence no specific
336 * rules to apply, but we have very few unconstrained fields, so we can implement a
337 * crude default and devolve the rest to options.
338 * @param value value to be sanitized.
339 * @param options control options including validation rules.
340 * @param type validation type.
341 * @returns {*} sanitized string.
344 static sanitizeText(value, options, type) {
345 const rules = Common.assertNotNull(options.validation[type]);
346 let v = value || rules.defaultValue || '';
348 v = v.replace(rules.replace, '');
350 if (v.length > rules.maxLength) {
351 v = `${v.substring(0, rules.maxLength)}...`;