UNPKG

globular-mvc

Version:

Generic template to create web-application that made use of globular as backend and materialize as css (wrap in web-component's)

1,674 lines (1,420 loc) 58.9 kB
/* * (C) Copyright 2016 Mycelius SA (http://mycelius.com/). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Authors: Dave Courtois * Contributors: */ /** * @fileOverview Various helper functions. * @author Dave Courtois * @version 1.0.0 */ window.URL = window.URL || window.webkitURL; // Take care of vendor prefixes. var mousePositionX = null; var mousePositionY = null; export function onMouseUpdate(e) { mousePositionX = e.pageX; mousePositionY = e.pageY; } document.addEventListener('mousemove', onMouseUpdate, false); document.addEventListener('mouseenter', onMouseUpdate, false); export function getMouseX() { return mousePositionX; } export function getMouseY() { return mousePositionY; } export function fireResize() { if (document.createEvent) { // W3C var ev = document.createEvent('Event'); ev.initEvent('resize', true, true); window.dispatchEvent(ev); } else { // IE element = document.documentElement; var event = document.createEventObject(); element.fireEvent("onresize", event); } } ; Array.prototype.removeDuplicates = function () { var temp = new Array(); label: for (var i = 0; i < this.length; i++) { for (var j = 0; j < temp.length; j++) { //check duplicates if (temp[j] == this[i]) //skip if already present continue label; } temp[temp.length] = this[i]; } return temp; }; /* finds the intersection of * two arrays in a simple fashion. * * PARAMS * a - first array, must already be sorted * b - second array, must already be sorted * * NOTES * * Should have O(n) operations, where n is * n = MIN(a.length(), b.length()) */ export function intersectSafe(a, b) { var ai = 0, bi = 0; var result = []; while (ai < a.length && bi < b.length) { if (a[ai] < b[bi]) { ai++; } else if (a[ai] > b[bi]) { bi++; } else /* they're equal */ { result.push(a[ai]); ai++; bi++; } } return result; } //////////////////////////////////////////////////////////////////////////// // Validation functions helpers //////////////////////////////////////////////////////////////////////////// /** * Test if a given input is a numeric value. * @param input Any value * @returns {boolean} True if the value is numeric. */ export function isNumeric(input) { var RE = /^-{0,1}\d*\.{0,1}\d+$/; return RE.test(input); } /** * Test if a given input is a well formed email address. * @param input Any value * @returns {boolean} True if the value is a well formed email address. */ export function isEmail(email) { return /^([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x22([^\x0d\x22\x5c\x80-\xff]|\x5c[\x00-\x7f])*\x22)(\x2e([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x22([^\x0d\x22\x5c\x80-\xff]|\x5c[\x00-\x7f])*\x22))*\x40([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x5b([^\x0d\x5b-\x5d\x80-\xff]|\x5c[\x00-\x7f])*\x5d)(\x2e([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x5b([^\x0d\x5b-\x5d\x80-\xff]|\x5c[\x00-\x7f])*\x5d))*$/.test(email); } /** * Test if a given input is a string. * @param input Any value * @returns {boolean} True if the value is a string. */ export function isString(o) { return Object.prototype.toString.call(o) === '[object String]'; } /** * Test if a given string is an object reference. */ export function isObjectReference(ref) { return /^[a-zA-Z_$][a-zA-Z_$0-9]*(\.[a-zA-Z_$][a-zA-Z_$0-9]*)+(\.[a-zA-Z_$][a-zA-Z_$0-9]*)*\%[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(ref); } /** * Test if a given input is a json string. * @param input Any value * @returns {boolean} True if the value is a JSON string. */ export function isJsonString(str) { try { JSON.parse(str); } catch (e) { return false; } return true; } /** * Test if a given input is an object. * @param input Any value * @returns {boolean} True if the value is an object. */ export function isObject(val) { if (val === null) { return false; } return typeof val === 'function' || typeof val === 'object'; } /** * Test if a given input is an integer. * @param input Any value * @returns {boolean} True if the value is an integer. */ export function isInt(value) { return !isNaN(value) && function (x) { return (x | 0) === x; }(parseFloat(value)); } /** * Test if a given input is a boolean value. * @param input Any value * @returns {boolean} True if the value is a boolean. */ export function isBoolean(value) { return typeof value === 'boolean'; } /** * Test if a given input is an array. * @param input Any value * @returns {boolean} True if the value is an array. */ export function isArray(o) { // make sure an array has a class attribute of [object Array] var check_class = Object.prototype.toString.call([]); if (check_class === '[object Array]') { // test passed, now check return Object.prototype.toString.call(o) === '[object Array]'; } else { // may want to change return value to something more desirable return -1; } } export function isFunction(functionToCheck) { var getType = {}; return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]'; } export function parseFunction(code) { // The function parameters var parameters = code.substring(code.indexOf("(") + 1, code.indexOf("{")); parameters = parameters.substring(0, parameters.indexOf(")")); parameters = parameters.split(","); // The function src var src = code.substring(code.indexOf("{") + 1, code.lastIndexOf("}")); var constructor = "Function("; for (var i = 0; i < parameters.length; i++) { constructor += '"' + parameters[i] + '"'; if (i < parameters.length - 1) { constructor += ","; } } constructor += ", src)"; return eval(constructor); } export function objectEquals(x, y) { 'use strict'; if (x === null || x === undefined || y === null || y === undefined) { return x === y; } // after this just checking type of one would be enough if (x.constructor !== y.constructor) { return false; } // if they are functions, they should exactly refer to same one (because of closures) if (x instanceof Function) { return x === y; } // if they are regexps, they should exactly refer to same one (it is hard to better equality check on current ES) if (x instanceof RegExp) { return x === y; } if (x === y || x.valueOf() === y.valueOf()) { return true; } if (Array.isArray(x) && x.length !== y.length) { return false; } // if they are dates, they must had equal valueOf if (x instanceof Date) { return false; } // if they are strictly equal, they both need to be object at least if (!(x instanceof Object)) { return false; } if (!(y instanceof Object)) { return false; } // recursive object equality check var p = Object.keys(x); return Object.keys(y).every(function (i) { return p.indexOf(i) !== -1; }) && p.every(function (i) { return objectEquals(x[i], y[i]); }); } //////////////////////////////////// XML/SQL type //////////////////////////////////// /** * Dertermine if the value is a base type. */ export function isXsBaseType(fieldType) { if (!fieldType.startsWith("xs") || fieldType.startsWith("sqltypes")) { return false; } return isXsId(fieldType) || isXsRef(fieldType) || isXsInt(fieldType) || isXsString(fieldType) || isXsBinary(fieldType) || isXsNumeric(fieldType) || isXsBoolean(fieldType) || isXsDate(fieldType) || isXsTime(fieldType) || isXsMoney(fieldType); } /** * Helper function use to dertermine if a XS type must be considere integer. */ export function isXsInt(fieldType) { if (!fieldType.startsWith("xs") || fieldType.startsWith("sqltypes")) { return false; } if (fieldType.endsWith("byte") || fieldType.endsWith("long") || fieldType.endsWith("int") || fieldType.endsWith("integer") || fieldType.endsWith("short") // XML || fieldType.endsWith("unsignedInt") || fieldType.endsWith("unsignedBtype") || fieldType.endsWith("unsignedShort") || fieldType.endsWith("unsignedLong") // XML || fieldType.endsWith("negativeInteger") || fieldType.endsWith("nonNegativeInteger") || fieldType.endsWith("nonPositiveInteger") || fieldType.endsWith("positiveInteger") // XML || fieldType.endsWith("tinyint") || fieldType.endsWith("smallint") || fieldType.endsWith("bigint") // SQL || fieldType.endsWith("time") || fieldType.endsWith("Time") // XML || fieldType.endsWith("timestampNumeric") || fieldType.endsWith("timestamp")) // SQL { return true; } return false; } /** * Helper function use to dertermine if a XS type must be considere String. */ export function isXsString(fieldType) { if (!fieldType.startsWith("xs") || fieldType.startsWith("sqltypes")) { return false; } if (fieldType.endsWith("string") || fieldType.endsWith("Name") || fieldType.endsWith("QName") || fieldType.endsWith("NMTOKEN") // XML || fieldType.endsWith("gDay") || fieldType.endsWith("gMonth") || fieldType.endsWith("gMonthDay") || fieldType.endsWith("gYear") // XML || fieldType.endsWith("gYearMonth") || fieldType.endsWith("token") || fieldType.endsWith("normalizedString") || fieldType.endsWith("hexBinary") // XML || fieldType.endsWith("language") || fieldType.endsWith("NMTOKENS") || fieldType.endsWith("NOTATION") || fieldType.endsWith("token") // XML || fieldType.endsWith("char") || fieldType.endsWith("nchar") || fieldType.endsWith("varchar") // SQL || fieldType.endsWith("nvarchar") || fieldType.endsWith("text") || fieldType.endsWith("ntext") // SQL ) { return true; } return false; } /** * Helper function use to dertermine if a XS type must be considere binary value. */ export function isXsBinary(fieldType) { if (!fieldType.startsWith("xs") || fieldType.startsWith("sqltypes")) { return false; } if (fieldType.endsWith("base64Binary") // XML || fieldType.endsWith("varbinary") || fieldType.endsWith("binary") // SQL || fieldType.endsWith("image") // SQL ) { return true; } return false; } /** * Helper function use to dertermine if a XS type must be considere numeric value. */ export function isXsNumeric(fieldType) { if (!fieldType.startsWith("xs") || fieldType.startsWith("sqltypes")) { return false; } if (fieldType.endsWith("double") || fieldType.endsWith("decimal") || fieldType.endsWith("float") // XML || fieldType.endsWith("numeric") || fieldType.endsWith("real") // SQL ) { return true; } return false; } /** * Helper function use to dertermine if a XS type must be considere boolean value. */ export function isXsBoolean(fieldType) { if (!fieldType.startsWith("xs") || fieldType.startsWith("sqltypes")) { return false; } if (fieldType.endsWith("boolean") // XML || fieldType.endsWith("bit") // SQL ) { return true; } return false; } /** * Helper function use to dertermine if a XS type must be considere date value. */ export function isXsDate(fieldType) { if (!fieldType.startsWith("xs") || fieldType.startsWith("sqltypes")) { return false; } if (fieldType.endsWith("date") || fieldType.endsWith("dateTime") // XML || fieldType.endsWith("datetime2") || fieldType.endsWith("smalldatetime") || fieldType.endsWith("datetimeoffset") // SQL ) { return true; } return false; } /** * Helper function use to dertermine if a XS type must be considere time value. */ export function isXsTime(fieldType) { if (!fieldType.startsWith("xs") || fieldType.startsWith("sqltypes")) { return false; } return false; } /** * Helper function use to dertermine if a XS type must be considere money value. */ export function isXsMoney(fieldType) { if (!fieldType.startsWith("xs") || fieldType.startsWith("sqltypes")) { return false; } if (fieldType.endsWith("money") || fieldType.endsWith("smallmoney") // SQL ) { return true; } return false; } /** * Helper function use to dertermine if a XS type must be considere id value. */ export function isXsId(fieldType) { if (!fieldType.startsWith("xs") || fieldType.startsWith("sqltypes")) { return false; } if (fieldType.endsWith("ID") || fieldType.endsWith("NCName") // XML || fieldType.endsWith("uniqueidentifier") // SQL ) { return true; } return false; } /** * Helper function use to dertermine if a XS type must be considere id value. */ export function isXsRef(fieldType) { if (!fieldType.startsWith("xs") || fieldType.startsWith("sqltypes")) { return false; } if (fieldType.endsWith("anyURI") || fieldType.endsWith("IDREF") // XML ) { return true; } return false; } //////////////////////////////////////////////////////////////////////////// // Random functions helpers //////////////////////////////////////////////////////////////////////////// /** * Create a random color * @returns {string} A string containing the rgb(0-255,0-255,0-255) value. */ export function randomColor() { //apply a random color since we don't have content to show yet var r = Math.floor(Math.random() * 255); var g = Math.floor(Math.random() * 255); var b = Math.floor(Math.random() * 255); return "rgb(" + r + "," + g + "," + b + ")"; } /** * Create a "version 4" RFC-4122 UUID (Universal Unique Identifier) string. * @returns {string} A string containing the UUID. */ export function randomUUID() { var s = [], itoh = '0123456789abcdef'; // Make array of random hex digits. The UUID only has 32 digits in it, but we // allocate an extra items to make room for the '-'s we'll be inserting. for (var i = 0; i < 36; i++) s[i] = Math.floor(Math.random() * 0x10); // Conform to RFC-4122, section 4.4 s[14] = 4; // Set 4 high bits of time_high field to version s[19] = s[19] & 0x3 | 0x8; // Specify 2 high bits of clock sequence // Convert to hex chars for (var i = 0; i < 36; i++) s[i] = itoh[s[i]]; // Insert '-'s s[8] = s[13] = s[18] = s[23] = '-'; return s.join(''); } /** * Deterministic value from a given value. */ export function generateUUID(value) { uuid = new UUID(3, "ns:URL", value); return uuid.toString(); } /** * Create an integer value between a given range. * @param {int} min The inclusive minimumal value * @param {int} max The inclusive maximal value * @returns {int} A integer value between the tow limits. */ export function randomIntFromInterval(min, max) { return Math.floor(Math.random() * (max - min + 1) + min); } /** * That function create an array of integer of a given length and randomize it elments order. * @param {int} length the length of the array. * @returns A shuffled array */ export function randomArray(length) { var array = []; for (var i = 0; i < length; i++) { array.push(i); } return shuffleArray(array); } ////////////////////////////////////////////////////////////////////////// // Style sheet and script files manipulation function ////////////////////////////////////////////////////////////////////////// /** * Load the style sheet (.css) or source file (.js) dynamically. * @param {string} filePath The path of the file to load. * @param {string} fileType The value can be 'js' or 'css' * @callback {function} callback The function to execute after the files are loaded. */ export function loadjscssfile(filePath, filetype, callback) { var func = callback; // Instanciate the function from the code. if (typeof callback == "string") { var func = new Function("return " + callback)(); } if (filetype == "js" || filetype == "application/javascript") { //if filePath is a external JavaScript file var fileref = document.createElement('script'); fileref.setAttribute("type", "application/javascript"); fileref.setAttribute("src", filePath); fileref.onload = func; } else if (filetype == "css" || filetype == "text/css") { //if filePath is an external CSS file var fileref = document.createElement("link"); fileref.setAttribute("rel", "stylesheet"); fileref.setAttribute("type", "text/css"); fileref.setAttribute("href", filePath); fileref.onload = func; } if (typeof fileref != "undefined") document.getElementsByTagName("body")[0].appendChild(fileref); } /** * Append javasrcipt source in the runtime. * @param {string} src The source code to append. * @param {string} id The id of the element to insert, can be undefined. */ export function includeJavascript(src, id) { if (document.createElement && document.getElementsByTagName) { var head_tag = document.getElementsByTagName('head')[0]; var script_tag = document.createElement('script'); script_tag.setAttribute('type', 'text/javascript'); script_tag.setAttribute('src', src); head_tag.appendChild(script_tag); } } /** * Get the style sheet with a given name. * @param {string} fileName The name of the file to get. * @returns {object} The file if the file is found, or null if is not. */ export function getStyleSheetByFileName(fileName) { function getStyleSheetByUrl(url) { for (var id in document.styleSheets) { var stylesheet = document.styleSheets[id]; var href = stylesheet.href; if (href == null) { if (stylesheet.ownerNode != undefined) { href = stylesheet.ownerNode.id; } } if (href != undefined) { if (href.toUpperCase().startsWith(url.toUpperCase())) { return stylesheet; } } } return null; } if (fileName[0] != "/") { var currentUrl = document.URL; currentUrl += fileName; return getStyleSheetByUrl(currentUrl); } else { // Try with the ipv4 adress currentUrl = window.location.href + fileName; // Look for href that contain the filename var stylesheet = getStyleSheetByUrl(currentUrl); if (stylesheet != null) { return stylesheet; } var style = document.createElement("style"); // WebKit hack style.appendChild(document.createTextNode("")); // Add the <style> element to the page document.head.appendChild(style); return style.sheet; } } /** * Retreive a property for a given selector with a given attribute name. * @selector {string} The css selector {., #, *, etc.} @see http://www.w3schools.com/cssref/css_selectors.asp * @attribte {string} The name of the attribute to retreive. * @returns The associated value or null if there no value found. */ export function propertyFromStylesheet(selector, attribute) { var value; [].some.call(document.styleSheets, function (sheet) { return [].some.call(sheet.rules, function (rule) { if (selector === rule.selectorText) { return [].some.call(rule.style, function (style) { if (attribute === style) { value = rule.style.getPropertyValue(attribute); return true; } }); } return false; }); }); return value; } export function getRulesByName(selector) { var rules = []; for (var i = 0; i < document.styleSheets.length; i++) { for (var j = 0; j < document.styleSheets[i].rules.length; j++) { if (document.styleSheets[i].rules[j] != undefined) { if (document.styleSheets[i].rules[j].selectorText != undefined) { if (document.styleSheets[i].rules[j].selectorText.indexOf(selector) != -1) { rules.push(document.styleSheets[i].rules[j]); } } } } } return rules; } /** * The map of frame rules indexed by their name. */ var framesRules = {}; /** * Find a rule with a given name. * @param {string} The name of the rule to retreive. * @returns The rule if one exist, or null otherwise. */ export function findKeyframesRule(name) { // Look in the map first. if (framesRules[name] != undefined) { return framesRules[name]; } var ss = document.styleSheets; for (var i = 0; i < ss.length; ++i) { for (var j = 0; j < ss[i].cssRules.length; ++j) { if (ss[i].cssRules[j].type == window.CSSRule.WEBKIT_KEYFRAMES_RULE && ss[i].cssRules[j].name == name) { framesRules[ss[i].cssRules[j].name] = ss[i].cssRules[j]; return ss[i].cssRules[j]; } } } return null; } // shim layer with setTimeout fallback window.requestAnimFrame = function () { return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function (callback) { window.setTimeout(callback, 1000 / 60); }; }(); /** * Retreive a style rule for a given selector in a given style sheet. * @param {string} style The css style attribute name * @param {string} selector The css selector {., #, *, etc.} @see http://www.w3schools.com/cssref/css_selectors.asp * @param {string} sheetPath The style sheet path. * @returns {string} The associated value. */ export function getStyleRuleValue(style, selector, sheetPath) { var sheet = getStyleSheetByFileName(sheetPath); if (!sheet.cssRules) { return; } for (var j = 0, k = sheet.cssRules.length; j < k; j++) { var rule = sheet.cssRules[j]; if (rule.selectorText && rule.selectorText.split(',').indexOf(selector) !== -1) { return rule.style[style]; } } return null; } /** * Append a new style rule with a given id. * @param {string} id The id of the rule to insert. * @param {string} rule The rule text itself. */ export function addStyleString(id, rule) { var node = document.createElement('style'); node.id = window.location.href + "/" + id; node.innerHTML = rule; document.body.appendChild(node); } export function getCSSRule(ruleName) { ruleName = ruleName.toLowerCase(); var result = null; var find = Array.prototype.find; find.call(document.styleSheets, function (styleSheet) { result = find.call(styleSheet.cssRules, function (cssRule) { return cssRule instanceof CSSStyleRule && cssRule.selectorText.toLowerCase() == ruleName; }); return result != null; }); return result; } ////////////////////////////////////////////////////////////////////////// // Array buffer conversion stuff ////////////////////////////////////////////////////////////////////////// /** * Work arround for large file data url. * * @param {*} dataURI * @param {*} callback */ export function dataURIToBlob(dataURI, callback) { var binStr = atob(dataURI.split(',')[1]), len = binStr.length, arr = new Uint8Array(len), mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]; for (var i = 0; i < len; i++) { arr[i] = binStr.charCodeAt(i); } return new Blob([arr], { type: mimeString }); } /** * Covertion from an Array Buffer to a string. * @param buffer the array buffer to convert. * @returns {string} the string representation of the input buffer. */ export function ab2str(buffer) { return String.fromCharCode.apply(null, new Uint8Array(buffer)); } /** * Conversion of a string to an Array Buffer. * @param {string} str The string to convert. * @returns the resulting Array Buffer. */ export function str2ab(str) { var buf = new ArrayBuffer(str.length); // 2 bytes for each char var bufView = new Uint8Array(buf); for (var i = 0, strLen = str.length; i < strLen; i++) { bufView[i] = str.charCodeAt(i); } return buf; } /** * Conversion fo an height unsigned bytes integer array to base 64 string. * @param array The array to convert. * @returns {string} the base 64 string representation of the input array. */ export function Uint8ToBase64(array) { var CHUNK_SIZE = 0x8000; //arbitrary number var index = 0; var length = array.length; var result = ''; var slice; while (index < length) { slice = array.subarray(index, Math.min(index + CHUNK_SIZE, length)); result += String.fromCharCode.apply(null, slice); index += CHUNK_SIZE; } return btoa(result); } var Base64 = { _keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=%", encode: function (input) { var output = ""; var chr1, chr2, chr3, enc1, enc2, enc3, enc4; var i = 0; input = Base64._utf8_encode(input); while (i < input.length) { chr1 = input.charCodeAt(i++); chr2 = input.charCodeAt(i++); chr3 = input.charCodeAt(i++); enc1 = chr1 >> 2; enc2 = (chr1 & 3) << 4 | chr2 >> 4; enc3 = (chr2 & 15) << 2 | chr3 >> 6; enc4 = chr3 & 63; if (isNaN(chr2)) { enc3 = enc4 = 64; } else if (isNaN(chr3)) { enc4 = 64; } output = output + this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4); } return output; }, decode: function (input) { var output = ""; var chr1, chr2, chr3; var enc1, enc2, enc3, enc4; var i = 0; input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); if (input.indexOf(",") > -1) { // If the input is an url... input = input.substr(input.indexOf(",")); } while (i < input.length) { enc1 = this._keyStr.indexOf(input.charAt(i++)); enc2 = this._keyStr.indexOf(input.charAt(i++)); enc3 = this._keyStr.indexOf(input.charAt(i++)); enc4 = this._keyStr.indexOf(input.charAt(i++)); chr1 = enc1 << 2 | enc2 >> 4; chr2 = (enc2 & 15) << 4 | enc3 >> 2; chr3 = (enc3 & 3) << 6 | enc4; output = output + String.fromCharCode(chr1); if (enc3 != 64) { output = output + String.fromCharCode(chr2); } if (enc4 != 64) { output = output + String.fromCharCode(chr3); } } output = Base64._utf8_decode(output); return output; }, _utf8_encode: function (string) { string = string.replace(/\r\n/g, "\n"); var utftext = ""; for (var n = 0; n < string.length; n++) { var c = string.charCodeAt(n); if (c < 128) { utftext += String.fromCharCode(c); } else if (c > 127 && c < 2048) { utftext += String.fromCharCode(c >> 6 | 192); utftext += String.fromCharCode(c & 63 | 128); } else { utftext += String.fromCharCode(c >> 12 | 224); utftext += String.fromCharCode(c >> 6 & 63 | 128); utftext += String.fromCharCode(c & 63 | 128); } } return utftext; }, _utf8_decode: function (utftext) { var string = ""; var i = 0; var c var c1 var c2 c = c1 = c2 = 0; while (i < utftext.length) { c = utftext.charCodeAt(i); if (c < 128) { string += String.fromCharCode(c); i++; } else if (c > 191 && c < 224) { c2 = utftext.charCodeAt(i + 1); string += String.fromCharCode((c & 31) << 6 | c2 & 63); i += 2; } else { c2 = utftext.charCodeAt(i + 1); c3 = utftext.charCodeAt(i + 2); string += String.fromCharCode((c & 15) << 12 | (c2 & 63) << 6 | c3 & 63); i += 3; } } return string; } /** * Encode iput content into base64 string * @param {string} input The input string, can be an json string. * @results Return the base 64 string. */ }; export function encode64(input) { return Base64.encode(input); } /** * Decode the content of a base64 string back into text * @param {string} input base 64 string to decode. * @results {string} the string containing the original value. */ export function decode64(input) { return Base64.decode(input); } /** * Convert an UTF8 string to a base 64 string. * @param {string} str the UTF8 string to convert. * @returns {string} The base 64 string representation. */ export function utf8_to_b64(str) { return window.btoa(unescape(encodeURIComponent(str))); } /** * Convert a base 64 string to an UTF8 string. * @param {string} str The base 64 string. * @returns {string} the original UTF8 string. */ export function b64_to_utf8(str) { return decodeURIComponent(escape(window.atob(str))); } /** * Convert a base 64 string to a blob. * @param base64Data The base 64 string. * @param The blob mime type. */ export function base64toBlob(base64Data, contentType) { contentType = contentType || ''; var sliceSize = 1024; var byteCharacters = atob(base64Data); var bytesLength = byteCharacters.length; var slicesCount = Math.ceil(bytesLength / sliceSize); var byteArrays = new Array(slicesCount); for (var sliceIndex = 0; sliceIndex < slicesCount; ++sliceIndex) { var begin = sliceIndex * sliceSize; var end = Math.min(begin + sliceSize, bytesLength); var bytes = new Array(end - begin); for (var offset = begin, i = 0; offset < end; ++i, ++offset) { bytes[i] = byteCharacters[offset].charCodeAt(0); } byteArrays[sliceIndex] = new Uint8Array(bytes); } return new Blob(byteArrays, { type: contentType }); } /** * Convert a data uri (base 64), to a binaray array unit8 * @param uri The uri to convert. * @returns An array of unsigned bytes. (uint8) */ export function convertDataURIToBinary(uri) { var BASE64_MARKER = ';base64,'; var base64Index = uri.indexOf(BASE64_MARKER) + BASE64_MARKER.length; var base64 = uri.substring(base64Index); var raw = window.atob(base64); var rawLength = raw.length; var array = new Uint8Array(new ArrayBuffer(rawLength)); for (var i = 0; i < rawLength; i++) { array[i] = raw.charCodeAt(i); } return array; } ////////////////////////////////////////////////////////////////////////// // String helpers function ////////////////////////////////////////////////////////////////////////// /** * Determine if a string has a given suffix. * @param {string} suffix The value to use as suffix. * @returns {boolean} Return true if the value is found. */ if (typeof String.prototype.endsWith != 'function') { String.prototype.endsWith = function (suffix) { return this.substring(this.length - suffix.length, this.length) === suffix; }; } ; /** * Determine if a string has a given prefix. * @param {string} prefix The value to use as prefix. * @returns {boolean} Return true if the value is found. */ if (typeof String.prototype.startsWith != 'function') { String.prototype.startsWith = function (prefix) { return this.substring(0, prefix.length) === prefix; }; } ; /** * Replace all occurences of a given value in a given string by another value. * @param {string} find The value to replace. * @param {string} replace The value to use as replacement. */ String.prototype.replaceAll = function (find, replace) { var str = this; return str.replace(new RegExp(find.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'), 'g'), replace); }; /** * Set the first letter of a work to upper case. */ String.prototype.capitalizeFirstLetter = function () { return this.charAt(0).toUpperCase() + this.slice(1); }; /** * A quick snippet to grab all the indexes of a substring within a string * @param {*} string The string we looking for. */ String.prototype.indices = function (string) { var returns = []; var position = 0; while (this.indexOf(string, position) > -1) { var index = this.indexOf(string, position); returns.push(index); position = index + string.length; } return returns; }; /** * Count the number of space in a given string. * @param {string} str The target string. * @returns {int} The number of white space. */ export function CountSpace(str) { var arr = str.split(" "); var size = arr.length - 1; return size; } /** * Uses canvas.measureText to compute and return the width of the given text of given font in pixels. * * @param {string} text The text to be rendered. * @param {string} font The css font descriptor that text is to be rendered with (e.g. "bold 14px verdana"). * * @see http://stackoverflow.com/questions/118241/calculate-text-width-with-javascript/21015393#21015393 */ export function getTextWidth(text, font) { // re-use canvas object for better performance var canvas = getTextWidth.canvas || (getTextWidth.canvas = document.createElement("canvas")); var context = canvas.getContext("2d"); context.font = font; var metrics = context.measureText(text); return metrics.width * 1.2; } ; ////////////////////////////////////////////////////////////////////////// // Color helpers function ////////////////////////////////////////////////////////////////////////// /** * That function is use to calculate the inverted hexadecimal value of a given * hexadecimal color value. * @param {string} The hexadecimal value of the color to invert. * @param {string} The inverted color of the input color. */ export function invertHex(hexnum) { hexnum = hexnum.replace("#", ""); if (hexnum.length != 6) { alert("Hex color must be six hex numbers in length."); return false; } hexnum = hexnum.toUpperCase(); var splitnum = hexnum.split(""); var resultnum = ""; var simplenum = "FEDCBA9876".split(""); var complexnum = new Array(); complexnum.A = "5"; complexnum.B = "4"; complexnum.C = "3"; complexnum.D = "2"; complexnum.E = "1"; complexnum.F = "0"; for (i = 0; i < 6; i++) { if (!isNaN(splitnum[i])) { resultnum += simplenum[splitnum[i]]; } else if (complexnum[splitnum[i]]) { resultnum += complexnum[splitnum[i]]; } else { alert("Hex colors must only include hex numbers 0-9, and A-F"); return false; } } return "#" + resultnum; } /** * Apply the saturation level (brigthness) to a given color. * @param sat The saturation level. * @param hex The hexadecimal value of the color to saturate. * @returns The hexadecimal value of the saturated color. */ export function applySat(sat, hex) { var hash = hex.substring(0, 1) === "#"; hex = (hash ? hex.substring(1) : hex).split(""); var long = hex.length > 3, rgb = [], i = 0, len = 3; rgb.push(hex.shift() + (long ? hex.shift() : "")); rgb.push(hex.shift() + (long ? hex.shift() : "")); rgb.push(hex.shift() + (long ? hex.shift() : "")); for (; i < len; i++) { if (!long) { rgb[i] += rgb[i]; } rgb[i] = Math.round(parseInt(rgb[i], 16) / 100 * sat).toString(16); rgb[i] += rgb[i].length === 1 ? rgb[i] : ""; } return (hash ? "#" : "") + rgb.join(""); } /** * Get the red value from an hexadecimal color input. * @param {string} h The hexadecimal value of the color * @returns {int} A integer value in range of 0-255 */ export function hexToR(h) { return parseInt(cutHex(h).substring(0, 2), 16); } /** * Get the green value from an hexadecimal color input. * @param {string} h The hexadecimal value of the color * @returns {int} A integer value in range of 0-255 */ export function hexToG(h) { return parseInt(cutHex(h).substring(2, 4), 16); } /** * Get the blue value from an hexadecimal color input. * @param {string} h The hexadecimal value of the color * @returns {int} A integer value in range of 0-255 */ export function hexToB(h) { return parseInt(cutHex(h).substring(4, 6), 16); } // Internal use only. export function cutHex(h) { return h.charAt(0) == "#" ? h.substring(1, 7) : h; } /** * Get the Red/Green/Blue value from an hexadecimal color. * @param {string} h The hexadecimal value of the color * @returns The rgb representation of the input color. */ export function hexToRgb(h) { var r = hexToR(h); var g = hexToG(h); var b = hexToB(h); return [r, g, b]; } /** * Get the red, green and blue component of a color and calculate it HSL value. * @param {int} r The red value 0-255 * @param {int} g The green value 0-255 * @param {int} b The blue value 0-255 * @return Return the tree value of the HSL. */ export function rgbToHsl(r, g, b) { r /= 255, g /= 255, b /= 255; var max = Math.max(r, g, b), min = Math.min(r, g, b); var h, s, l = (max + min) / 2; if (max == min) { h = s = 0; // achromatic } else { var d = max - min; s = l > 0.5 ? d / (2 - max - min) : d / (max + min); switch (max) { case r: h = (g - b) / d + (g < b ? 6 : 0); break; case g: h = (b - r) / d + 2; break; case b: h = (r - g) / d + 4; break; } h /= 6; } return [h, s, l]; } /** * Determine if a color is more distintive over a black or a white background. * @param {string} hexcolor The input color. * @returns {string} Black if the color is more visible over black, or white otherwise. */ export function getContrastYIQ(hexcolor) { var r = parseInt(hexcolor.substr(0, 2), 16); var g = parseInt(hexcolor.substr(2, 2), 16); var b = parseInt(hexcolor.substr(4, 2), 16); var yiq = (r * 299 + g * 587 + b * 114) / 1000; return yiq >= 138 ? 'black' : 'white'; } /////////////////////////////////////////////////////////////////////////////////// // Various helpers /////////////////////////////////////////////////////////////////////////////////// /** * Creates a new RpcData. * @param variable The variable to create as RpcData * @param {string} variableType The type of the variable. Can be: DOUBLE, INTEGER, STRING, BYTES, JSON_STR, BOOLEAN * @param {string} variableName The name of the variable to create as RpcData. This parameter is optional * @param {string} typeName This is the name on the server side that must be use to interprest the data. * @returns {RpcData} The created RpcData or undefined if variableType was invalid */ export function createRpcData(variable, variableType, variableName, typeName) { if (variableName == undefined) { variableName = "varName"; } if (variableType == "DOUBLE") { variableType = Data_DOUBLE; typeName = "double"; } else if (variableType == "INTEGER") { variableType = Data_INTEGER; typeName = "int"; } else if (variableType == "STRING") { variableType = Data_STRING; typeName = "string"; } else if (variableType == "BYTES") { variableType = Data_BYTES; typeName = "[]unit8"; } else if (variableType == "JSON_STR") { variableType = Data_JSON_STR; if (variable != null) { if (variable.stringify != undefined) { variableType = Data_JSON_STR; typeName = variable.TYPENAME; variable = variable.stringify(); } else { variable = JSON.stringify(variable); } } } else if (variableType == "BOOLEAN") { variableType = Data_BOOLEAN; typeName = "bool"; } else { return undefined; } // Now I will create the rpc data. return new RpcData({ "name": variableName, "type": variableType, "dataBytes": utf8_to_b64(variable), "typeName": typeName }); } /** * Evaluate if an array contain a given element. * @param arr The target array. * @param obj The object to find. * @returns {boolean} True if the array contain the object. */ export function contains(arr, obj) { var i = arr.length; while (i--) { if (arr[i] === obj) { return true; } } return false; } /** * Evaluate if an object exist in regard of a given property. * @param list the array. * @param prop the property name * @param val the value to test * @returns {boolean} True if an object has a property with the same value. */ export function objectPropInArray(list, prop, val) { if (list == undefined) { return false; } if (list.length > 0) { for (var i = 0; i < list.length; i++) { if (list[i][prop] === val) { return true; } } } return false; } /** * Take a list of objects at function arguments and merge it to create a single object regrouping all objects properties. * @returns {object} The resulting object with all properties. */ export function mergeJSON() { var destination = {}; sources = [].slice.call(arguments, 0); sources.forEach(function (source) { var prop; for (prop in source) { if (prop in destination && Array.isArray(destination[prop])) { // Concat Arrays destination[prop] = destination[prop].concat(source[prop]); } else if (prop in destination && typeof destination[prop] === "object") { // Merge Objects destination[prop] = merge(destination[prop], source[prop]); } else { // Set new values destination[prop] = source[prop]; } } }); return destination; } /** * Randomly order a given array. * @param array An array of object. * @returns the same array ramdomly ordered. */ export function shuffleArray(array) { for (var j, x, i = array.length; i; j = Math.floor(Math.random() * i), x = array[--i], array[i] = array[j], array[j] = x); return array; } /** * Stringification of the current date with the format dd-mm-yyyy * @returns {string} The date string. */ export function getCurrentDateStr() { var today = new Date(); var dd = today.getDate(); var mm = today.getMonth() + 1; //January is 0! var yyyy = today.getFullYear(); if (dd < 10) { dd = '0' + dd; } if (mm < 10) { mm = '0' + mm; } today = dd + '-' + mm + '-' + yyyy; return today; } /** * Resize an image to a maximum with or height, and keep the same ratio. * @param {string} The base 64 representation of the image. * @param {int} maxWidth The maximum width of the image. * @param {int} maxHeight The maximum height of the image. * @return {string} The base 64 representation of the resized image. */ export function resizeImage(base64, maxWidth, maxHeight) { // Max size for thumbnail if (typeof maxWidth === 'undefined') var maxWidth = 1024; if (typeof maxHeight === 'undefined') var maxHeight = 1024; // Create and initialize two canvas var canvas = document.createElement("canvas"); var ctx = canvas.getContext("2d"); var canvasCopy = document.createElement("canvas"); var copyContext = canvasCopy.getContext("2d"); // Create original image var img = new Image(); img.src = base64; if (img.width <= 1024 && img.height <= 1024) { // nothing todo here. return base64; } // Determine new ratio based on max size var ratio = 1; if (img.width > maxWidth) ratio = maxWidth / img.width;else if (img.height > maxHeight) ratio = maxHeight / img.height; // Draw original image in second canvas canvasCopy.width = img.width; canvasCopy.height = img.height; copyContext.drawImage(img, 0, 0); // Copy and resize second canvas to first canvas canvas.width = img.width * ratio; canvas.height = img.height * ratio; ctx.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvas.width, canvas.height); return canvas.toDataURL('image/jpeg'); } /** * Select all the text of a given element. * @param element the text element. */ export function selectText(element) { var doc = document; var text = element; if (doc.body.createTextRange) { // ms var range = doc.body.createTextRange(); range.moveToElementText(text); range.select(); } else if (window.getSelection) { // moz, opera, webkit var selection = window.getSelection(); var range = doc.createRange(); range.selectNodeContents(text); selection.removeAllRanges(); selection.addRange(range); } } /** * Return the name of the browser. * @returns {string} Can be one of Opera, Chrome, Safari, Firefox, IE or unknow. */ export function getNavigatorName() { if ((navigator.userAgent.indexOf("Opera") || navigator.userAgent.indexOf('OPR')) != -1) { return 'Opera'; } else if (navigator.userAgent.indexOf("Chrome") != -1) { return 'Chrome'; } else if (navigator.userAgent.indexOf("Safari") != -1) { return 'Safari'; } else if (navigator.userAgent.indexOf("Firefox") != -1) { return 'Firefox'; } else if (navigator.userAgent.indexOf("MSIE") != -1 || !!document.documentMode == true) //IF IE > 10 { return 'IE'; } else { return 'unknown'; } } /** * Return the time since a given time, it format * the result in a human readable form. * @param {date} since The past date. * @returns {string} readable elapsed time. */ export function getTimeSinceStr(since) { //var now = Math.floor(Date.now() / 1000) var now = new Date().getTime(); var passed = Math.floor(now / 1000) - since; if (passed < 60) { // Dipslay only the second here passed += " secs"; } else if (passed >= 60 && passed < 3600) { // Display minutes here passed = Math.floor(passed / 60) + " mins"; } else if (passed >= 3600 && passed <= 86400) { // Display session that no longuer than on day var passedHours = Math.floor(passed / 3600) + " hrs"; // The minutes. var passedMinutes = Math.floor(passed % 3600 / 60) + " mins"; passed = passedHours + " " + passedMinutes; } else { // Here the user is online for more than a day. passedDay = Math.floor(passed / 86400) + " days"; // The hours. passedHours = Math.floor(passed % 86400 / 3600) + " hrs"; // The minutes. var passedMinutes = Math.floor(passed % 86400 % 3600 / 60) + " mins"; passed = passedDay + " " + passedHours + " " + passedMinutes; } return passed; } ////////////////////////////////////////////////////////////////////////////////////// // Search function execute on the server side. ////////////////////////////////////////////////////////////////////////////////////// export function keysrt(key) { return function (a, b) { if (a[key] > b[key]) return 1; if (a[key] < b[key]) return -1; return 0; }; } /** * Search for object with value that begin with a given prefix */ export function StartWith(objects) { var results = _.select(objects, function (val) { var reg = /(^__PREFIX__)/; var result = reg.exec(val.__FIELD__); if (result != null) { if (result.length > 1) { return true; } } return false; }); return results; } export function simulate(element, eventName) { var options = extend(defaultOptions, arguments[2] || {}); var oEvent, eventType = null; for (var name in eventMatchers) { if (eventMatchers[name].test(eventName)) { eventType = name; break; } } if (!eventType) throw new SyntaxError('Only HTMLEvents and MouseEvents interfaces are supported'); if (document.createEvent) { oEvent = document.createEvent(eventType); if (eventType == 'HTMLEvents') { oEvent.initEvent(eventName, options.bubbles, options.cancelable); } else { oEvent.initMouseEvent(eventName, options.bubbles, options.cancelable, document.defaultView, options.button, options.pointerX, options.pointerY, options.pointerX, options.pointerY, options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.button, element); } element.dispatchEvent(oEvent); } else { options.clientX = options.pointerX; options.clientY = options.pointerY; var evt = document.createEventObject(); oEvent = extend(evt, options); element.fireEvent('on' + eventName, oEvent); } return element; } export function extend(destination, source) { for (var property in source) destination[property] = source[property]; return destination; } var eventMatchers = { 'HTMLEvents': /^(?:load|unload|abort|error|select|change|submit|reset|focus|blur|resize|scroll)$/, 'MouseEvents': /^(?:click|dblclick|mouse(?:down|up|over|move|out))$/ }; var defaultOptions = { pointerX: 0, pointerY: 0, button: 0, ctrlKey: false, altKey: false, shiftKey: false, metaKey: false, bubbles: true, cancelable: true /** * Return the position top, left relative to the document. * @param {object} elem The element to get the position. */ }; export function getCoords(elem) { // crossbrowser version var box = elem.getBoundingClientRect(); var body = document.body; var docEl = document.documentElement; var scrollTop = window.pageYOffset || docEl.scrollTo