| 1 | /** |
| 2 | * @description Set of short commonly used methods for handling HTML elements |
| 3 | * @author Ylian Saint-Hilaire |
| 4 | * @version v0.0.1b |
| 5 | */ |
| 6 | |
| 7 | // Add startsWith for IE browser |
| 8 | if (!String.prototype.startsWith) { String.prototype.startsWith = function (str) { return this.lastIndexOf(str, 0) === 0; }; } |
| 9 | if (!String.prototype.endsWith) { String.prototype.endsWith = function (str) { return this.indexOf(str, this.length - str.length) !== -1; }; } |
| 10 | |
| 11 | // Quick UI functions, a bit of a replacement for jQuery |
| 12 | //function Q(x) { if (document.getElementById(x) == null) { console.log('Invalid element: ' + x); } return document.getElementById(x); } // "Q" |
| 13 | function Q(x) { return document.getElementById(x); } // "Q" |
| 14 | function QS(x) { try { return Q(x).style; } catch (x) { } } // "Q" style |
| 15 | function QE(x, y) { try { Q(x).disabled = !y; } catch (x) { } } // "Q" enable |
| 16 | function QV(x, y) { try { QS(x).display = (y ? '' : 'none'); } catch (x) { } } // "Q" visible |
| 17 | function QA(x, y) { Q(x).innerHTML += y; } // "Q" append |
| 18 | function QH(x, y) { Q(x).innerHTML = y; } // "Q" html |
| 19 | function QC(x) { try { return Q(x).classList; } catch (x) { } } // "Q" class |
| 20 | function QVH(x, y) { try { y ? Q(x).classList.remove('visually-hidden') : Q(x).classList.add('visually-hidden'); } catch (x) { } } // "Q" visibility |
| 21 | |
| 22 | // Move cursor to end of input box |
| 23 | function inputBoxFocus(x) { Q(x).focus(); var v = Q(x).value; Q(x).value = ''; Q(x).value = v; } |
| 24 | |
| 25 | // Binary encoding and decoding functions |
| 26 | function ReadShort(v, p) { return (v.charCodeAt(p) << 8) + v.charCodeAt(p + 1); } |
| 27 | function ReadShortX(v, p) { return (v.charCodeAt(p + 1) << 8) + v.charCodeAt(p); } |
| 28 | function ReadInt(v, p) { return (v.charCodeAt(p) * 0x1000000) + (v.charCodeAt(p + 1) << 16) + (v.charCodeAt(p + 2) << 8) + v.charCodeAt(p + 3); } // We use "*0x1000000" instead of "<<24" because the shift converts the number to signed int32. |
| 29 | function ReadSInt(v, p) { return (v.charCodeAt(p) << 24) + (v.charCodeAt(p + 1) << 16) + (v.charCodeAt(p + 2) << 8) + v.charCodeAt(p + 3); } |
| 30 | function ReadIntX(v, p) { return (v.charCodeAt(p + 3) * 0x1000000) + (v.charCodeAt(p + 2) << 16) + (v.charCodeAt(p + 1) << 8) + v.charCodeAt(p); } |
| 31 | function ShortToStr(v) { return String.fromCharCode((v >> 8) & 0xFF, v & 0xFF); } |
| 32 | function ShortToStrX(v) { return String.fromCharCode(v & 0xFF, (v >> 8) & 0xFF); } |
| 33 | function IntToStr(v) { return String.fromCharCode((v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF); } |
| 34 | function IntToStrX(v) { return String.fromCharCode(v & 0xFF, (v >> 8) & 0xFF, (v >> 16) & 0xFF, (v >> 24) & 0xFF); } |
| 35 | function MakeToArray(v) { if (!v || v == null || typeof v == 'object') return v; return [v]; } |
| 36 | function SplitArray(v) { return v.split(','); } |
| 37 | function Clone(v) { return JSON.parse(JSON.stringify(v)); } |
| 38 | function EscapeHtml(x) { if (typeof x == 'string') return x.replace(/&/g, '&').replace(/>/g, '>').replace(/</g, '<').replace(/"/g, '"').replace(/'/g, '''); if (typeof x == 'boolean') return x; if (typeof x == 'number') return x; } |
| 39 | function EscapeHtmlBreaks(x) { if (typeof x == 'string') return x.replace(/&/g, '&').replace(/>/g, '>').replace(/</g, '<').replace(/"/g, '"').replace(/'/g, ''').replace(/\r/g, '<br />').replace(/\n/g, '').replace(/\t/g, ' '); if (typeof x == 'boolean') return x; if (typeof x == 'number') return x; } |
| 40 | |
| 41 | // Move an element from one position in an array to a new position |
| 42 | function ArrayElementMove(arr, from, to) { arr.splice(to, 0, arr.splice(from, 1)[0]); }; |
| 43 | |
| 44 | // Print object for HTML |
| 45 | function ObjectToStringEx(x, c) { |
| 46 | var r = ""; |
| 47 | if (x != 0 && (!x || x == null)) return '(Null)'; |
| 48 | if (x instanceof Array) { for (var i in x) { r += '<br />' + gap(c) + 'Item #' + i + ": " + ObjectToStringEx(x[i], c + 1); } } |
| 49 | else if (x instanceof Object) { for (var i in x) { r += '<br />' + gap(c) + i + ' = ' + ObjectToStringEx(x[i], c + 1); } } |
| 50 | else { r += EscapeHtml(x); } |
| 51 | return r; |
| 52 | } |
| 53 | |
| 54 | // Print object for console |
| 55 | function ObjectToStringEx2(x, c) { |
| 56 | var r = ''; |
| 57 | if (x != 0 && (!x || x == null)) return '(Null)'; |
| 58 | if (x instanceof Array) { for (var i in x) { r += '\r\n' + gap2(c) + 'Item #' + i + ': ' + ObjectToStringEx2(x[i], c + 1); } } |
| 59 | else if (x instanceof Object) { for (var i in x) { r += '\r\n' + gap2(c) + i + ' = ' + ObjectToStringEx2(x[i], c + 1); } } |
| 60 | else { r += EscapeHtml(x); } |
| 61 | return r; |
| 62 | } |
| 63 | |
| 64 | // Create an ident gap |
| 65 | function gap(c) { var x = ''; for (var i = 0; i < (c * 4) ; i++) { x += ' '; } return x; } |
| 66 | function gap2(c) { var x = ''; for (var i = 0; i < (c * 4) ; i++) { x += ' '; } return x; } |
| 67 | |
| 68 | // Print an object in html |
| 69 | function ObjectToString(x) { return ObjectToStringEx(x, 0); } |
| 70 | function ObjectToString2(x) { return ObjectToStringEx2(x, 0); } |
| 71 | |
| 72 | // Convert a hex string to a raw string |
| 73 | function hex2rstr(d) { |
| 74 | if (typeof d != 'string' || d.length == 0) return ''; |
| 75 | var r = '', m = ('' + d).match(/../g), t; |
| 76 | while (t = m.shift()) r += String.fromCharCode('0x' + t); |
| 77 | return r |
| 78 | } |
| 79 | |
| 80 | // Convert decimal to hex |
| 81 | function char2hex(i) { return (i + 0x100).toString(16).substr(-2).toUpperCase(); } |
| 82 | |
| 83 | // Convert a raw string to a hex string |
| 84 | function rstr2hex(input) { var r = '', i; for (i = 0; i < input.length; i++) { r += char2hex(input.charCodeAt(i)); } return r; } |
| 85 | |
| 86 | // UTF-8 encoding & decoding functions |
| 87 | function encode_utf8(s) { return unescape(encodeURIComponent(s)); } |
| 88 | function decode_utf8(s) { return decodeURIComponent(escape(s)); } |
| 89 | |
| 90 | // Convert a string into a blob |
| 91 | function data2blob(data) { |
| 92 | var bytes = new Array(data.length); |
| 93 | for (var i = 0; i < data.length; i++) bytes[i] = data.charCodeAt(i); |
| 94 | return new Blob([new Uint8Array(bytes)]); |
| 95 | } |
| 96 | |
| 97 | // Convert a UTF8 string into a blob |
| 98 | function utf2blob(str) { |
| 99 | var bytes = [], utf8 = unescape(encodeURIComponent(str)); |
| 100 | for (var i = 0; i < utf8.length; i++) { bytes.push(utf8.charCodeAt(i)); } |
| 101 | return new Blob([new Uint8Array(bytes)]); |
| 102 | } |
| 103 | |
| 104 | // Generate random numbers |
| 105 | function random(max) { return Math.floor(Math.random() * max); } |
| 106 | |
| 107 | // Trademarks |
| 108 | function trademarks(x) { return x.replace(/\(R\)/g, '®').replace(/\(TM\)/g, '™'); } |
| 109 | |
| 110 | // Pad a number with zeros on the left |
| 111 | function zeroPad(num, c) { if (c == null) { c = 2; } var s = '00000000' + num; return s.substr(s.length - c); } |
| 112 | |
| 113 | // String validation |
| 114 | function isAlphaNumeric(str) { if (typeof str == 'number') { return true; } return (str.match(/^[A-Za-z0-9]+$/) != null); }; |
| 115 | function isSafeString(str) { return ((typeof str == 'string') && (str.indexOf('<') == -1) && (str.indexOf('>') == -1) && (str.indexOf('&') == -1) && (str.indexOf('"') == -1) && (str.indexOf('\'') == -1) && (str.indexOf('+') == -1) && (str.indexOf('(') == -1) && (str.indexOf(')') == -1) && (str.indexOf('#') == -1) && (str.indexOf('%') == -1) && (str.indexOf(':') == -1)) }; |
| 116 | function isSafeString2(str) { return ((typeof str == 'string') && (str.indexOf('<') == -1) && (str.indexOf('>') == -1) && (str.indexOf('&') == -1) && (str.indexOf('"') == -1) && (str.indexOf('\'') == -1) && (str.indexOf('+') == -1) && (str.indexOf('(') == -1) && (str.indexOf(')') == -1) && (str.indexOf('#') == -1) && (str.indexOf('%') == -1)) }; |
| 117 | |
| 118 | // Parse URL arguments, only keep safe values |
| 119 | function parseUriArgs(decodeUrl) { |
| 120 | var href = window.document.location.href; |
| 121 | if (href.endsWith('#')) { href = href.substring(0, href.length - 1); } |
| 122 | var name, r = {}, parsedUri = href.split(/[\?&|]/); |
| 123 | parsedUri.splice(0, 1); |
| 124 | for (var j in parsedUri) { |
| 125 | var arg = parsedUri[j], i = arg.indexOf('='); |
| 126 | name = arg.substring(0, i); |
| 127 | r[name] = arg.substring(i + 1); |
| 128 | if (decodeUrl) { r[name] = decodeURIComponent(arg.substring(i + 1)); } |
| 129 | if (!isSafeString2(r[name])) { delete r[name]; } else { var x = parseInt(r[name]); if (x == r[name]) { r[name] = x; } } |
| 130 | } |
| 131 | return r; |
| 132 | } |
| 133 | |
| 134 | // check_webp_feature: |
| 135 | // 'feature' can be one of 'lossy', 'lossless', 'alpha' or 'animation'. |
| 136 | // 'callback(feature, isSupported)' will be passed back the detection result (in an asynchronous way!) |
| 137 | // From: https://stackoverflow.com/questions/5573096/detecting-webp-support |
| 138 | function check_webp_feature(feature, callback) { |
| 139 | var kTestImages = { |
| 140 | lossy: 'UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA'//, |
| 141 | //lossless: 'UklGRhoAAABXRUJQVlA4TA0AAAAvAAAAEAcQERGIiP4HAA==', |
| 142 | //alpha: 'UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAARBxAR/Q9ERP8DAABWUDggGAAAABQBAJ0BKgEAAQAAAP4AAA3AAP7mtQAAAA==', |
| 143 | //animation: 'UklGRlIAAABXRUJQVlA4WAoAAAASAAAAAAAAAAAAQU5JTQYAAAD/////AABBTk1GJgAAAAAAAAAAAAAAAAAAAGQAAABWUDhMDQAAAC8AAAAQBxAREYiI/gcA' |
| 144 | }; |
| 145 | var img = new Image(); |
| 146 | img.onload = function () { |
| 147 | var result = (img.width > 0) && (img.height > 0); |
| 148 | callback(feature, result); |
| 149 | }; |
| 150 | img.onerror = function () { |
| 151 | callback(feature, false); |
| 152 | }; |
| 153 | img.src = 'data:image/webp;base64,' + kTestImages[feature]; |
| 154 | } |
| 155 | |
| 156 | // camelCase converter for JSON |
| 157 | function jsonToCamel(o) { |
| 158 | var newO, origKey, newKey, value |
| 159 | if (o instanceof Array) { |
| 160 | return o.map(function(value) { |
| 161 | if (typeof value === "object") { |
| 162 | value = jsonToCamel(value) |
| 163 | } |
| 164 | return value |
| 165 | }) |
| 166 | } else { |
| 167 | newO = {} |
| 168 | for (origKey in o) { |
| 169 | if (o.hasOwnProperty(origKey)) { |
| 170 | newKey = (origKey.charAt(0).toLowerCase() + origKey.slice(1) || origKey).toString() |
| 171 | value = o[origKey] |
| 172 | if (value instanceof Array || (value !== null && value.constructor === Object)) { |
| 173 | value = jsonToCamel(value) |
| 174 | } |
| 175 | newO[newKey] = value |
| 176 | } |
| 177 | } |
| 178 | } |
| 179 | return newO |
| 180 | } |
| 181 | |
| 182 | function joinPaths() { |
| 183 | var x = []; |
| 184 | for (var i in arguments) { |
| 185 | var w = arguments[i]; |
| 186 | if ((w != null) && (w != '')) { |
| 187 | while (w.endsWith('/') || w.endsWith('\\')) { |
| 188 | w = w.substring(0, w.length - 1); |
| 189 | } |
| 190 | x.push(w); |
| 191 | } |
| 192 | } |
| 193 | return x.join('/'); |
| 194 | } |