UNPKG

@geoext/geoext

Version:

GeoExt is Open Source and enables building desktop-like GIS applications through the web. It is a JavaScript framework that combines the GIS functionality of OpenLayers with the user interface savvy of the ExtJS library provided by Sencha.

237 lines (230 loc) 8.22 kB
/* Copyright (c) 2015-present The Open Source Geospatial Foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ /** * A utility class providing methods to check for symbols of OpenLayers we * depend upon. * * This class can be mixed into classes to check if the dependencies to external * symbols are fulfilled. An example: * * Ext.define('MyNewClass.DependingOnOpenLayersClasses', { * mixins: ['GeoExt.mixin.SymbolCheck'], * // the contents of the `symbols` property will be checked * symbols: [ * 'ol.Map', // checking a class * 'ol.View.prototype.constrainResolution', // an instance method * 'ol.control.ScaleLine#getUnits', // other way for instance method * 'ol.color.asArray', // one way to reference a static method * 'ol.color::asString' // other way to reference a static method * ] * // … your configuration and methods … * }); * * Since this sort of checking usually only makes sense in debug mode, you can * additionally wrap the `symbols`-configuration in these `<debug>`-line * comments: * * Ext.define('MyNewClass.DependingOnOpenLayersClasses', { * mixins: ['GeoExt.mixin.SymbolCheck'], * // <debug> * symbols: [] * // </debug> * }); * * This means that the array of symbols is not defined in production builds * as the wrapped lines are simply removed from the final JavaScript. * * If one of the symbols cannot be found, a warning will be printed to the * developer console (via `Ext.log.warn`, which will only print in a debug * build): * * [W] The class "MyNewClass.DependingOnOpenLayersClasses" depends on the * external symbol "ol.color.notExisting", which does not seem to exist. * * @class GeoExt.mixin.SymbolCheck */ Ext.define('GeoExt.mixin.SymbolCheck', { extend: 'Ext.Mixin', inheritableStatics: { /** * An object that we will use to store already looked up references in. * * The key will be a symbol (after it has been normalized by the * method #normalizeSymbol), and the value will be a boolean indicating * if the symbol was found to be defined when it was checked. * * @private */ _checked: { // will be filled while we are checking stuff for existence }, /** * Checks whether the required symbols of the given class are defined * in the global context. Will log to the console if a symbol cannot be * found. * * @param {Ext.Base} cls An ext class defining a property `symbols` that * that this method will check. */ check: function (cls) { // <debug> const me = this; const proto = cls.prototype; const olSymbols = proto && proto.symbols; const clsName = proto && proto['$className']; if (!olSymbols) { return; } Ext.each(olSymbols, function (olSymbol) { olSymbol = me.normalizeSymbol(olSymbol); me.checkSymbol(olSymbol, clsName); }); // </debug> }, /** * Normalizes a short form of a symbol to a canonical one we use to * store the results of the #isDefinedSymbol method. The following two * normalizations take place: * * * A `#` in the symbol is being replaced with `.prototype.` so that * e.g. the symbol `'ol.Class#methodName'` turns into the symbol * `'ol.Class.prototype.methodName'` * * A `::` in the symbol is being replaced with `.` so that * e.g. the symbol `'ol.Class::staticMethodName'` turns into the * symbol `'ol.Class.staticMethodName'` * * @param {string} symbolStr A string to normalize. * @return {string} The normalized string. * @private */ normalizeSymbol: (function () { // <debug> const hashRegEx = /#/; const colonRegEx = /::/; // </debug> const normalizeFunction = function (symbolStr) { // <debug> if (hashRegEx.test(symbolStr)) { symbolStr = symbolStr.replace(hashRegEx, '.prototype.'); } else if (colonRegEx.test(symbolStr)) { symbolStr = symbolStr.replace(colonRegEx, '.'); } return symbolStr; // </debug> }; return normalizeFunction; })(), /** * Checks the passed symbolStr and raises a warning if it cannot be * found. * * @param {string} symbolStr A string to check. Usually this string has * been {@link #normalizeSymbol normalized} already. * @param {string} [clsName] The optional name of the class that * requires the passed openlayers symbol. * @private */ checkSymbol: function (symbolStr, clsName) { // <debug> const isDefined = this.isDefinedSymbol(symbolStr); if (!isDefined) { Ext.log.warn( 'The class "' + (clsName || 'unknown') + '" ' + 'depends on the external symbol "' + symbolStr + '", ' + 'which does not seem to exist.', ); } // </debug> }, /** * Checks if the passed symbolStr is defined. * * @param {string} symbolStr A string to check. Usually this string has * been {@link #normalizeSymbol normalized} already. * @return {boolean} Whether the symbol is defined or not. * @private */ isDefinedSymbol: function (symbolStr) { // <debug> const checkedCache = this._checked; if (Ext.isDefined(checkedCache[symbolStr])) { return checkedCache[symbolStr]; } const parts = symbolStr.split('.'); const lastIdx = parts.length - 1; let curSymbol = Ext.getWin().dom; let isDefined = false; let intermediateSymb = ''; Ext.each(parts, function (part, idx) { if (intermediateSymb !== '') { intermediateSymb += '.'; } intermediateSymb += part; // Check the current symbol's property or method if (curSymbol[part]) { checkedCache[intermediateSymb] = true; curSymbol = curSymbol[part]; if (lastIdx === idx) { isDefined = true; } } else if (lastIdx === idx) { // Special handling for instance-bound methods try { const parentObj = Ext.Object.chain(curSymbol); const instance = new parentObj.constructor(); if (typeof instance[part] === 'function') { checkedCache[intermediateSymb] = true; isDefined = true; } } catch (e) { // Handle errors such as constructors requiring arguments Ext.log.warn( `Unable to create instance or access method: ${intermediateSymb}`, e, ); checkedCache[intermediateSymb] = false; } } else { // Method or property is not defined checkedCache[intermediateSymb] = false; return false; // break early } }); checkedCache[symbolStr] = isDefined; return isDefined; // </debug> }, }, /** * @property {Array<string>} symbols The symbols to check. */ /** * Whenever a class mixes in GeoExt.mixin.SymbolCheck, this method will be * called and it actually runs the checks for all the defined #symbols. * * @param {Ext.Class} cls The class that this mixin is mixed into. * @private */ onClassMixedIn: function (cls) { // <debug> GeoExt.mixin.SymbolCheck.check(cls); // </debug> }, });