goog.provide('M.utils'); goog.require('M.polyfills'); goog.require('M.geom.wkt.type'); goog.require('goog.color'); goog.require('goog.color.alpha'); /** * @namespace M.utils */ (function() { 'use strict'; /** * This function checks if the obj is null or empty * * @function * @param {string|Object|Array<*>} obj * @returns {boolean} * @api stable */ M.utils.isNullOrEmpty = function(obj) { var nullOrEmpty = false; if (M.utils.isNull(obj)) { nullOrEmpty = true; } else if (M.utils.isArray(obj)) { nullOrEmpty = true; if (obj.length > 0) { nullOrEmpty = !obj.some(function(objElem) { return !M.utils.isNullOrEmpty(objElem); }); } } else if ((typeof obj === 'string') && (obj.trim().length === 0)) { nullOrEmpty = true; } return nullOrEmpty; }; /** * * @function * @api stable */ M.utils.isNull = function(obj) { var isNull = false; if (!M.utils.isBoolean(obj) && (typeof obj !== 'number')) { if (M.utils.isUndefined(obj)) { isNull = true; } else if (!obj) { isNull = true; } else if (obj === null) { isNull = true; } } return isNull; }; /** * * @function * @api stable */ M.utils.isArray = function(obj) { var isArray = false; if (!M.utils.isNull(obj)) { isArray = (Object.prototype.toString.call(obj) === Object.prototype.toString .call([])); } return isArray; }; /** * * @function * @api stable */ M.utils.isFunction = function(obj) { var isFunction = false; if (!M.utils.isNull(obj)) { isFunction = ((typeof obj === 'function') && !M.utils.isUndefined(obj.call)); } return isFunction; }; /** * * @function * @api stable */ M.utils.isObject = function(obj) { var isObject = false; if (!M.utils.isNull(obj)) { isObject = ((typeof obj === 'object') && !M.utils.isUndefined(obj.toString)); } return isObject; }; /** * * @function * @api stable */ M.utils.isString = function(obj) { var isString = false; if (!M.utils.isNull(obj)) { isString = (typeof obj === 'string'); } return isString; }; /** * * @function * @api stable */ M.utils.isBoolean = function(obj) { var isBoolean = false; if ((obj !== null) && !M.utils.isUndefined(obj)) { isBoolean = (typeof obj === 'boolean'); } return isBoolean; }; /** * * @function * @api stable */ M.utils.isUrl = function(obj) { var isUrl = false; if (!M.utils.isNull(obj) && M.utils.isString(obj)) { isUrl = /(https?\:\/\/[^\*]+)/.test(obj); } return isUrl; }; /** * * @function * @api stable */ M.utils.isUndefined = function(obj) { return (typeof obj === 'undefined'); }; /** * * @function * @api stable */ M.utils.normalize = function(stringToNormalize, upperCase) { var normalizedString = stringToNormalize; if (!M.utils.isNullOrEmpty(normalizedString) && M.utils.isString(normalizedString)) { normalizedString = normalizedString.trim(); normalizedString = upperCase ? normalizedString.toUpperCase() : normalizedString.toLowerCase(); } return normalizedString; }; /** * * @function * @api stable */ M.utils.getParameterValue = function(paramName, url) { var parameterValue = null; paramName = paramName.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); var parameters = url; var idxQuery = parameters.indexOf('?'); if (idxQuery != -1) { parameters = parameters.substring(idxQuery); var regex = new RegExp("[\\?&]" + paramName + "=([^&#]*)"); parameterValue = regex.exec(parameters); if (parameterValue !== null) { parameterValue = decodeURIComponent(parameterValue[1].replace(/\+/g, " ")); } } return parameterValue; }; /** * * @function * @api stable */ M.utils.addParameters = function(url, params) { var requestUrl = url; if (requestUrl.indexOf('?') === -1) { requestUrl += '?'; } else if (requestUrl.charAt(requestUrl.length - 1) !== '?') { requestUrl += '&'; } var requestParams = ''; if (M.utils.isObject(params)) { for (var param in params) { requestParams += param; requestParams += '='; requestParams += encodeURIComponent(params[param]); requestParams += '&'; } // removes the last '&' requestParams = requestParams.substring(0, requestParams.length - 1); } else if (M.utils.isString(params)) { requestParams = params; } requestUrl += requestParams; return requestUrl; }; /** * * @function * @api stable */ M.utils.generateRandom = function(prefix, sufix) { var random = ''; // adds prefix if (!M.utils.isNullOrEmpty(prefix)) { random = prefix; } // generates random random = random.concat(Math.random()).replace(/0\./, ''); // adds sufix if (!M.utils.isNullOrEmpty(sufix)) { random = random.concat(sufix); } return random; }; /** * * @function * @api stable */ M.utils.getWMSGetCapabilitiesUrl = function(serverUrl, version) { var wmsGetCapabilitiesUrl = serverUrl; // request wmsGetCapabilitiesUrl = M.utils.addParameters(wmsGetCapabilitiesUrl, 'request=GetCapabilities'); // service wmsGetCapabilitiesUrl = M.utils.addParameters(wmsGetCapabilitiesUrl, 'service=WMS'); // PATCH: En mapea 3 no se manda luego aquí tampoco. Hay servicios que dan error.... // // version // wmsGetCapabilitiesUrl = M.utils.addParameters(wmsGetCapabilitiesUrl, { // 'version': version // }); return wmsGetCapabilitiesUrl; }; /** * * @function * @api stable */ M.utils.getWMTSGetCapabilitiesUrl = function(serverUrl, version) { var wmtsGetCapabilitiesUrl = serverUrl; // request wmtsGetCapabilitiesUrl = M.utils.addParameters(wmtsGetCapabilitiesUrl, 'request=GetCapabilities'); // service wmtsGetCapabilitiesUrl = M.utils.addParameters(wmtsGetCapabilitiesUrl, 'service=WMTS'); // version if (!M.utils.isNullOrEmpty(version)) { wmtsGetCapabilitiesUrl = M.utils.addParameters(wmtsGetCapabilitiesUrl, { 'version': version }); } return wmtsGetCapabilitiesUrl; }; /** * This function generates the resolution array * from min max scales * * @function * @param {Number} maxScale * @param {Number} minScale * @param {Number} zoomLevels * @param {String} units * @returns {Array<Number>} the resolutions * @api stable */ M.utils.generateResolutionsFromScales = function(maxScale, minScale, zoomLevels, units) { var minResolution = M.utils.getResolutionFromScale(maxScale, units); var maxResolution = M.utils.getResolutionFromScale(minScale, units); return M.utils.fillResolutions(minResolution, maxResolution, zoomLevels); }; /** * This function generates the resolution array * from min max scales * * @function * @param {Number} maxScale * @param {Number} minScale * @param {Number} zoomLevels * @param {String} units * @returns {Array<Number>} the resolutions * @api stable */ M.utils.generateResolutionsFromExtent = function(extent, size, zoomLevels, units) { let [wExtent, hExtent] = [null, null]; if (M.utils.isArray(extent)) { wExtent = (extent[2] - extent[0]); hExtent = (extent[3] - extent[1]); } else if (M.utils.isObject(extent)) { wExtent = (extent.x.max - extent.x.min); hExtent = (extent.y.max - extent.y.min); } else if (M.utils.isString(extent)) { extent = extent.split(","); wExtent = (extent[2] - extent[0]); hExtent = (extent[3] - extent[1]); } var wResolution = wExtent / size[0]; var hResolution = hExtent / size[1]; var maxResolution = Math.max(wResolution, hResolution); var resolutions = M.utils.fillResolutions(null, maxResolution, zoomLevels); return resolutions; }; /** * This function generates the resolution array * from min max resolutions * * @function * @param {Number} minResolution * @param {Number} maxResolution * @param {Number} numZoomLevels * @returns {Array<Number>} the resolutions * @api stable */ M.utils.fillResolutions = function(minResolution, maxResolution, numZoomLevels) { var resolutions = new Array(numZoomLevels); minResolution = Number.parseFloat(minResolution); maxResolution = Number.parseFloat(maxResolution); // if maxResolution and minResolution are set, we calculate // the base for exponential scaling that starts at // maxResolution and ends at minResolution in numZoomLevels // steps. var base = 2; if (!Number.isNaN(minResolution)) { base = Math.pow((maxResolution / minResolution), (1 / (numZoomLevels - 1))); } for (var i = 0; i < numZoomLevels; i++) { resolutions[i] = maxResolution / Math.pow(base, i); } //sort resolutions array descendingly resolutions.sort(function(a, b) { return (b - a); }); return resolutions; }; /** * This function calculates the resolution * for a provided scale * * @function * @param {Number} scale * @param {String} units * @returns {Number} the resolution for the specified scale * @api stable */ M.utils.getResolutionFromScale = function(scale, units) { var resolution; if (!M.utils.isNullOrEmpty(scale)) { if (M.utils.isNull(units)) { units = "degrees"; } // normalize scale var normScale = (scale > 1.0) ? (1.0 / scale) : scale; resolution = 1 / (normScale * M.INCHES_PER_UNIT[units] * M.DOTS_PER_INCH); } return resolution; }; /** * This function calculates the scale * for a provided resolution * * @function * @param {Number} resolution * @param {String} units * @returns {Number} the scale for the specified resolution * @api stable */ M.utils.getScaleFromResolution = function(resolution, units) { if (M.utils.isNullOrEmpty(units)) { units = "degrees"; } var scale = resolution * M.INCHES_PER_UNIT[units] * M.DOTS_PER_INCH; return scale; }; /** * * @function * @api stable */ M.utils.stringToHtml = function(htmlTxt) { var html; if (!M.utils.isNullOrEmpty(htmlTxt)) { var div = document.createElement('div'); div.innerHTML = htmlTxt; html = div.children[0]; } return html; }; /** * * @function * @api stable */ M.utils.htmlToString = function(html) { var text; if (!M.utils.isNullOrEmpty(html)) { var div = document.createElement('div'); goog.dom.appendChild(div, html); text = div.innerHTML; } return text; }; /** * formated String * * @function * @param {String} String text to format string * @returns {String} beautifyString formated String * @api stable */ M.utils.beautifyString = function(text) { var beautifyString; // 1 to lower case beautifyString = text.toLowerCase(); // 2 trim beautifyString = beautifyString.trim(beautifyString); // 3 first char to upper case beautifyString = beautifyString.charAt(0).toUpperCase() + beautifyString.slice(1); // 4 replaces "_" by spaces beautifyString = beautifyString.replace(/\_/g, " "); // 5 simplifies spaces beautifyString = beautifyString.replace(/\s+/, " "); // 6 to camel case beautifyString = beautifyString.replace(/(\s\w)+/g, function(match) { return match.toUpperCase(); }); // 7 common words to lower case beautifyString = beautifyString.replace(/\s+(de|del|las?|el|los?|un|unas?|unos?|y|a|al|en)\s+/ig, function(match) { return match.toLowerCase(); }); return beautifyString; }; /** * formated String * * @function * @param {attributeName} String * @returns {Number} formated String * @api stable */ M.utils.beautifyAttribute = function(attributeName) { var beautifyString = attributeName; if (beautifyString) { //OpenLayers.String.trim beautifyString = beautifyString.trim(); if (beautifyString.length > 0) { var idxPoints = beautifyString.indexOf(":"); if (idxPoints != -1) { idxPoints++; beautifyString = beautifyString.substring(idxPoints, beautifyString.length); } } } return beautifyString; }; /** * formated String * * @function * @param {attributeName} String * @returns {Number} formated String * @api stable */ M.utils.beautifyAttributeName = function(rawAttributeName) { var attributeName = M.utils.normalize(rawAttributeName); attributeName = attributeName.replace(/_(\w)/g, function(match, group) { return ' '.concat(group.toUpperCase()); }); attributeName = attributeName.replace(/^\w/, function(match) { return match.toUpperCase(); }); return attributeName; }; /** * formated String * * @function * @param {attributeName} String * @returns {Number} formated String * @api stable */ M.utils.concatUrlPaths = function(paths) { var finalUrl = null; if (!M.utils.isNullOrEmpty(paths)) { finalUrl = paths[0]; finalUrl = finalUrl.replace(/\/+\s*$/, ''); for (var i = 1, ilen = paths.length; i < ilen; i++) { var path = paths[i]; if (path.indexOf('/') !== 0) { finalUrl = finalUrl.concat('/'); } finalUrl = finalUrl.concat(path); } } return finalUrl; }; /** * * * @function * @api stable */ M.utils.includes = function(array, searchElement, fromIndex) { var O = Object(array); var len = parseInt(O.length) || 0; if (len === 0) { return false; } var n = parseInt(arguments[2]) || 0; var k; if (n >= 0) { k = n; } else { k = len + n; if (k < 0) { k = 0; } } var currentElement; while (k < len) { currentElement = O[k]; if (searchElement === currentElement || Object.equals(searchElement, currentElement) || (searchElement !== searchElement && currentElement !== currentElement)) { return true; } k++; } return false; }; /** * * * @function * @api stable */ M.utils.extend = function(target, source, override) { for (var prop in source) { if (M.utils.isUndefined(target[prop])) { target[prop] = source[prop]; } else if (M.utils.isObject(target[prop])) { M.utils.extend(target[prop], source[prop], override); } else if ((override === true)) { target[prop] = source[prop]; } } return target; }; /** * TODO * * @function * @api stable */ M.utils.escapeXSS = function(xssValue) { var validValue; // & --> & validValue = xssValue.replace(/\&/g, '&'); // < --> < validValue = validValue.replace(/</g, '<'); // > --> > validValue = validValue.replace(/\>/g, '>'); // " --> " validValue = validValue.replace(/\"/g, '"'); // ' --> ' validValue = validValue.replace(/\'/g, '''); // / --> / validValue = validValue.replace(/\//g, '/'); return validValue; }; /** * TODO * * @function * @api stable */ M.utils.escapeJSCode = function(jsCode) { var validValue; validValue = jsCode.replace(/(<\s*script[^\>]*\>)+[^<]*(<\s*\/\s*script[^\>]*\>)+/ig, ''); validValue = validValue.replace(/((\"|\')\s*\+\s*)?\s*eval\s*\(.*\)\s*(\+\s*(\"|\'))?/ig, ''); return validValue; }; /** * TODO * * @function * @api stable */ M.utils.enableTouchScroll = function(elem) { if ('ontouchstart' in goog.global) { var scrollStartPos = 0; goog.events.listen(elem, goog.events.EventType.TOUCHSTART, function(evt) { scrollStartPos = this.scrollTop + evt.getBrowserEvent().touches[0].pageY; }); goog.events.listen(elem, goog.events.EventType.TOUCHMOVE, function(evt) { this.scrollTop = scrollStartPos - evt.getBrowserEvent().touches[0].pageY; }); } }; /** * TODO * * @function * @api stable */ M.utils.rgbToHex = function(rgbColor) { var hexColor; if (goog.color.isValidColor(rgbColor)) { hexColor = goog.color.parse(rgbColor).hex; } else { try { hexColor = goog.color.alpha.parse(rgbColor).hex; } catch (err) {} } return hexColor; }; /** * TODO * * @function * @api stable */ M.utils.rgbaToHex = function(rgbaColor) { var hexColor; try { hexColor = chroma(rgbaColor).hex(); } catch (err) {} return hexColor; }; /** * TODO * * @function * @api stable */ M.utils.getOpacityFromRgba = function(rgbaColor) { var opacity; var rgbaRegExp = /^rgba\s*\((\s*\d+\s*\,){3}\s*([\d\.]+)\s*\)$/; if (rgbaRegExp.test(rgbaColor)) { opacity = rgbaColor.replace(rgbaRegExp, '$2'); try { opacity = parseFloat(opacity); } catch (err) {} } return opacity; }; /** * TODO * * @function * @api stable */ M.utils.sameUrl = function(url1, url2) { url1 = url1.replace(/^(.+)\/$/, '$1').replace(/^(.+)\?$/, '$1'); url2 = url2.replace(/^(.+)\/$/, '$1').replace(/^(.+)\?$/, '$1'); return url1.toLowerCase() === url2.toLowerCase(); }; /** * TODO * * @function * @api stable */ M.utils.isGeometryType = function(type) { var geometricTypes = [ M.geom.wkt.type.GEOMETRY.toLowerCase(), "GeometryPropertyType".toLowerCase(), M.geom.wkt.type.POINT.toLowerCase(), M.geom.wkt.type.LINE_STRING.toLowerCase(), M.geom.wkt.type.LINEAR_RING.toLowerCase(), M.geom.wkt.type.POLYGON.toLowerCase(), M.geom.wkt.type.MULTI_POINT.toLowerCase(), M.geom.wkt.type.MULTI_LINE_STRING.toLowerCase(), M.geom.wkt.type.MULTI_POLYGON.toLowerCase(), M.geom.wkt.type.GEOMETRY_COLLECTION.toLowerCase(), M.geom.wkt.type.CIRCLE.toLowerCase(), "pointpropertytype", "polygonpropertytype", "linestringpropertytype", "geometrypropertytype", "multisurfacepropertytype", "multilinestringpropertytype", "surfacepropertytype", "geometrypropertytype", "geometryarraypropertytype", "multigeometrypropertytype", "multipolygonpropertytype", "multipointpropertytype", "abstractgeometricaggregatetype", "pointarraypropertytype", "curvearraypropertytype", "solidpropertytype", "solidarraypropertytype" ]; type = type.toLowerCase(); return (geometricTypes.indexOf(type) !== -1); }; /** * This function decodes html entities into * text * * @function * @param {String} encodedHtml encoded text with HTML entities * @returns {String} text decoded * @api stable */ M.utils.decodeHtml = function(encodedHtml) { let txtarea = document.createElement("textarea"); txtarea.innerHTML = encodedHtml; return txtarea.value; }; /** * This function gets text content from * an html string or element * * @function * @param {HTMLElement | String} html string or element with HTML tags * @returns {String} text contained by the HTML tags * @api stable */ M.utils.getTextFromHtml = function(html) { let htmlText = html; if (!M.utils.isString(html) && html.outerHTML) { htmlText = html.outerHTML; } let divElement = document.createElement("DIV"); divElement.innerHTML = htmlText; return divElement.textContent || divElement.innerText || ""; }; /** * This function gets an array scale color in hexadecimal format * @function * @public * @return {Array<string>} array scale color in hexadecimal format * @api stable */ M.utils.generateColorScale = function(color1, color2, n_classes) { return chroma.scale([color1, color2]).colors(n_classes); }; /** * This function gets the inverse of a color. The inverse of a color * is the diff between the hexadecimal value of white (0xFFFFFF) * and the hexadecimal value of the color. * @function * @public * @param {string} color * @return {string} inverse color in hexadecimal format * @api stable */ M.utils.inverseColor = function(color) { let inverseColor; if (M.utils.isString(color)) { let hexColor = chroma(color).hex(); hexColor = hexColor.replace(/^\#/, '0x'); inverseColor = chroma(0xFFFFFF - hexColor).hex(); } return inverseColor; }; /** * This function gets the geometry type of a layer. * @function * @public * @param {M.layer.Vector} layer - layer vector * @return {string} geometry type of layer * @api stable */ M.utils.getGeometryType = function(layer) { if (M.utils.isNullOrEmpty(layer) || M.utils.isNullOrEmpty(layer.getFeatures())) { return null; } let firstFeature = layer.getFeatures()[0]; if (!M.utils.isNullOrEmpty(firstFeature) && !M.utils.isNullOrEmpty(firstFeature.getGeometry())) { return firstFeature.getGeometry().type; } return null; }; /** * This function returns the appropiate style to geomtry layer * with parameter options. * @function * @public * @param {object} options - style options * @param {M.layer.Vector} layer - * @return {M.style.Simple} * @api stable */ M.utils.generateStyleLayer = function(options, layer) { let style; switch (M.utils.getGeometryType(layer)) { case "Point": case "MultiPoint": style = new M.style.Point(options); break; case "LineString": case "MultiLineString": style = new M.style.Line(options); break; case "Polygon": case "MultiPolygon": style = new M.style.Polygon(options); break; default: return null; } return style; }; /** * This function returns a color as string with opacity * @function * @public * @param {string} color * @param {number} opacity * @return {string} * @api stable */ M.utils.getRgba = function(color, opacity) { return chroma(color).alpha(opacity).css(); }; /** * This function returns if two sets are equals * @function * @public * @param {array} array * @param {array} array2 * @return {bool} * @api stable */ M.utils.setEquals = function(array, array2) { let equals = false; if (array.length === array2.length) { equals = array.every(e => array2.some(e2 => e2.equals(e))); } return equals; }; /** * This function set implementation of this control * * @public * @function * @param {M.Map} impl to add the plugin * @api stable */ M.utils.extends = function(dest = {}, src = {}) { if (!M.utils.isNullOrEmpty(src)) { Object.keys(src).forEach(key => { let value = src[key]; if (M.utils.isArray(value)) { value = [...value]; } else if (M.utils.isObject(value)) { value = M.utils.extends({}, value); } if (M.utils.isNullOrEmpty(dest[key])) { dest[key] = value; } else if (M.utils.isObject(dest[key])) { M.utils.extends(dest[key], value); } }, this); } return dest; }; /** * This function returns an array whith breaks between head and tail of an array * @function * @public * @param {array} array * @param {number} breaks * @return {array} * @api stable */ M.utils.generateIntervals = function(array, breaks) { let intervals = [...array]; if (array.length < breaks) { let step = (array[0] + array[1]) / (breaks - 1); for (let i = 1; i < breaks - 1; i++) { intervals[i] = step * i; } intervals = [...intervals, array[1]]; } return intervals; }; /** * This functions returns the width and height of a image from src * @function * @public * @param {string} url * @return {Array<number>} * @api stable */ M.utils.getImageSize = function(url) { let image = new Image(); return new Promise((resolve, reject) => { image.onload = () => resolve(image); image.src = url; }); }; })();