• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

geosolutions-it / MapStore2 / 15704025608

17 Jun 2025 09:52AM UTC coverage: 76.967% (+0.007%) from 76.96%
15704025608

push

github

web-flow
Remove object assign pollyfills (#11214)

* chore: Remove unused package babel-plugin-object-assign

* chore: Remove es6-object-assign

* refactor: Replace object-assign with Object.assign

* Fixed other object assign

* Thanks to @jna-nordiq for this contribution

---------

Co-authored-by: Jonas <jonasnaursgaard.jensen@sweco.dk>

31117 of 48419 branches covered (64.27%)

457 of 567 new or added lines in 94 files covered. (80.6%)

4 existing lines in 3 files now uncovered.

38718 of 50305 relevant lines covered (76.97%)

36.34 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

63.53
/web/client/jsapi/MapStore2.js
1
/*
2
 * Copyright 2016, GeoSolutions Sas.
3
 * All rights reserved.
4
 *
5
 * This source code is licensed under the BSD-style license found in the
6
 * LICENSE file in the root directory of this source tree.
7
 */
8

9
import url from 'url';
10

11
import { merge, partialRight } from 'lodash';
12
import React from 'react';
13
import ReactDOM from 'react-dom';
14
import { connect } from 'react-redux';
15

16
import { configureMap, loadMapConfig } from '../actions/config';
17
import { initMap } from '../actions/map';
18
import StandardApp from '../components/app/StandardApp';
19
import defaultConfig from '../configs/config.json';
20
import { generateActionTrigger } from '../epics/jsapi';
21
import localConfig from '../configs/localConfig.json';
22
import { standardEpics, standardReducers, standardRootReducerFunc } from '../stores/defaultOptions';
23
import ConfigUtils from '../utils/ConfigUtils';
24
import { ensureIntl } from '../utils/LocaleUtils';
25
import { renderFromLess } from '../utils/ThemeUtils';
26
import { getApi } from '../api/userPersistedStorage';
27

28
const defaultPlugins = {
1✔
29
    "mobile": localConfig.plugins.embedded,
30
    "desktop": localConfig.plugins.embedded
31
};
32
let triggerAction;
33
function mergeDefaultConfig(pluginName, cfg) {
34
    var propertyName;
35
    var i;
36
    var result;
37
    for (i = 0; i < defaultPlugins.desktop.length; i++) {
×
38
        if (defaultPlugins.desktop[i].name === pluginName) {
×
39
            result = defaultPlugins.desktop[i].cfg;
×
40
            for (propertyName in cfg) {
×
41
                if (cfg.hasOwnProperty(propertyName)) {
×
42
                    result[propertyName] = cfg[propertyName];
×
43
                }
44
            }
45
            return result;
×
46
        }
47
    }
48
    return cfg;
×
49
}
50

51
function loadConfigFromStorage(name = 'mapstore.embedded') {
3✔
52
    if (name) {
3!
53
        let loaded = false;
3✔
54
        try {
3✔
55
            loaded = getApi().getItem(name);
3✔
56
            if (loaded) {
2✔
57
                return JSON.parse(loaded);
1✔
58
            }
59
        } catch (e) {
60
            console.error(e);
1✔
61
            return null;
1✔
62
        }
63
    }
64
    return null;
1✔
65
}
66

67
function getParamFromRequest(paramName) {
68
    const urlQuery = url.parse(window.location.href, true).query;
×
69
    return urlQuery[paramName] || null;
×
70
}
71

72
function buildPluginsCfg(plugins, cfg) {
73
    var pluginsCfg = [];
×
74
    var i;
75
    for (i = 0; i < plugins.length; i++) {
×
76
        if (cfg[plugins[i] + "Plugin"]) {
×
77
            pluginsCfg.push({
×
78
                name: plugins[i],
79
                cfg: mergeDefaultConfig(plugins[i], cfg[plugins[i] + "Plugin"])
80
            });
81
        } else {
82
            pluginsCfg.push({
×
83
                name: plugins[i],
84
                cfg: mergeDefaultConfig(plugins[i], {})
85
            });
86
        }
87
    }
88
    return {
×
89
        mobile: pluginsCfg,
90
        desktop: pluginsCfg
91
    };
92
}
93

94
const actionListeners = {};
1✔
95
let stateChangeListeners = [];
1✔
96

97
const getInitialActions = (options) => {
1✔
98
    if (!options.initialState || !options.initialState.defaultState.map) {
4!
99
        if (options.configUrl) {
4!
100
            return [initMap, loadMapConfig.bind(null, options.configUrl || defaultConfig, options.mapId)];
4!
101
        }
102
        return [configureMap.bind(null, options.config || defaultConfig, options.mapId)];
×
103
    }
104
    return [];
×
105
};
106

107

108
/**
109
 * MapStore2 JavaScript API. Allows embedding MapStore2 functionalities into
110
 * a standard HTML page.
111
 *
112
 * ATTENTION: As of July 2020 a number of MapStore2 plugins (i.e. TOC layer settings, Identify) use react-dock for providing
113
 * Dock panel functionality, that assumes that we use the whole window, so the panels won't show up at all or will
114
 * not be constrained within the container.
115
 * @class
116
 */
117
const MapStore2 = {
1✔
118
    /**
119
     * Instantiates an embedded MapStore2 application in the given container.
120
     * MapStore2 api doesn't use StandardRouter but It relies on StandardContainer
121
     * @memberof MapStore2
122
     * @static
123
     * @param {string} container id of the DOM element that should contain the embedded MapStore2
124
     * @param {object} options set of options of the embedded app
125
     *  * The options object can contain the following properties, to configure the app UI and state:
126
     *  * **plugins**: list of plugins (and the related configuration) to be included in the app
127
     *    look at [Plugins documentation](https://mapstore.readthedocs.io/en/latest/developer-guide/plugins-documentation/) for further details
128
     *  * **config**: map configuration object for the application (look at [Map Configuration](https://mapstore.readthedocs.io/en/latest/developer-guide/maps-configuration/) for details)
129
     *  * **configUrl**: map configuration url for the application (look at [Map Configuration](https://mapstore.readthedocs.io/en/latest/developer-guide/maps-configuration/) for details)
130
     *  * **originalUrl**: url of the original instance of MapStore. If present it will be linked inside the map using the "GoFull" plugin, present by default.
131
     *  * **initialState**: allows setting the initial application state (look at [State Configuration](https://mapstore.readthedocs.io/en/latest/developer-guide/local-config/) for details)
132
     *
133
     * Styling can be configured either using a **theme**, or a complete custom **less stylesheet**, using the
134
     * following options properties:
135
     *  * **style**: less style to be applied
136
     *  * **startAction**: the actionType to wait before start triggering actions. By default CHANGE_MAP_VIEW
137
     *  * **theme**: theme configuration options:
138
     *    * path: path/url of the themes folder related to the current page
139
     *    * theme: theme name to be used
140
     *
141
     * ```javascript
142
     * {
143
     *      plugins: ['Map', 'ZoomIn', 'ZoomOut'],
144
     *      config: {
145
     *          map: {
146
     *              ...
147
     *          }
148
     *      },
149
     *      configUrl: '...',
150
     *      initialState: {
151
     *          defaultState: {
152
     *              ...
153
     *          }
154
     *      },
155
     *      style: '<custom style>',
156
     *      theme: {
157
     *          theme: 'mytheme',
158
     *          path: 'dist/themes'
159
     *      }
160
     * }
161
     * ```
162
     * @param {object} [plugins] optional plugins definition (defaults to local plugins list)
163
     * @param {object} [component] optional page component (defaults to MapStore2 Embedded Page)
164
     * @example
165
     * MapStore2.create('container', {
166
     *      plugins: ['Map']
167
     * });
168
     */
169
    create(container, opts, pluginsDef, component) {
170
        const embedded = require('../containers/Embedded').default;
4✔
171
        const options = merge({}, this.defaultOptions || {}, opts);
4✔
172
        const {initialState, storeOpts} = options;
4✔
173

174
        const {loadVersion} = require('../actions/version');
4✔
175
        const {versionSelector} = require('../selectors/version');
4✔
176
        const {loadAfterThemeSelector} = require('../selectors/config');
4✔
177
        const componentConfig = {
4✔
178
            component: component || embedded,
8✔
179
            config: {
180
                pluginsConfig: options.plugins || defaultPlugins
8✔
181
            }
182
        };
183
        const StandardContainer = connect((state) => ({
16✔
184
            locale: state.locale || {},
25✔
185
            componentConfig,
186
            version: versionSelector(state),
187
            loadAfterTheme: loadAfterThemeSelector(state)
188
        }))(require('../components/app/StandardContainer').default);
189
        const actionTrigger = generateActionTrigger(options.startAction || "CHANGE_MAP_VIEW");
4✔
190
        triggerAction = actionTrigger.trigger;
4✔
191
        const appStore = require('../stores/StandardStore').default.bind(null, {
4✔
192
            initialState: initialState || {},
8✔
193
            appReducers: {
194
                security: require('../reducers/security').default,
195
                version: require('../reducers/version').default,
196
                ...standardReducers
197
            },
198
            appEpics: {
199
                jsAPIEpic: actionTrigger.epic,
200
                ...(options.epics || {}),
6✔
201
                ...standardEpics
202
            },
203
            rootReducerFunc: standardRootReducerFunc
204
        });
205
        const initialActions = [...getInitialActions(options), loadVersion.bind(null, options.versionURL)];
4✔
206
        const appConfig = {
4✔
207
            storeOpts: Object.assign({}, storeOpts, {notify: true, noRouter: true}),
208
            appStore,
209
            pluginsDef,
210
            initialActions,
211
            appComponent: StandardContainer,
212
            printingEnabled: options.printingEnabled || false
8✔
213
        };
214
        if (options.style) {
4!
215
            let dom = document.getElementById('custom_theme');
×
216
            if (!dom) {
×
217
                dom = document.createElement('style');
×
218
                dom.id = 'custom_theme';
×
219
                document.head.appendChild(dom);
×
220
            }
221
            renderFromLess(options.style, 'custom_theme', 'themes/default/');
×
222
        }
223
        const defaultThemeCfg = {
4✔
224
            prefixContainer: '#' + container
225
        };
226

227
        const themeCfg = options.theme && Object.assign({}, defaultThemeCfg, options.theme) || defaultThemeCfg;
4!
228
        const onStoreInit = (store) => {
4✔
229
            store.addActionListener((action) => {
4✔
230
                const act = action.type === "PERFORM_ACTION" && action.action || action; // Needed to works also in debug
40!
231
                (actionListeners[act.type] || []).concat(actionListeners['*'] || []).forEach((listener) => {
40✔
232
                    listener.call(null, act);
3✔
233
                });
234
            });
235
            store.subscribe(() => {
4✔
236
                stateChangeListeners.forEach(({listener, selector}) => {
40✔
237
                    listener.call(null, selector(store.getState()));
10✔
238
                });
239
            });
240
        };
241
        if (options.noLocalConfig) {
4!
242
            ConfigUtils.setLocalConfigurationFile('');
×
243
            ConfigUtils.setConfigProp('proxyUrl', options.proxy || null);
×
244
        }
245

246
        if (options.translations) {
4!
247
            ConfigUtils.setConfigProp('translationsPath', options.translations);
×
248
        }
249
        if (options.originalUrl) {
4!
250
            ConfigUtils.setConfigProp('originalUrl', options.originalUrl);
4✔
251
        }
252
        ReactDOM.render(<StandardApp onStoreInit={onStoreInit} themeCfg={themeCfg} className="fill" {...appConfig}/>, document.getElementById(container));
4✔
253
    },
254
    buildPluginsCfg,
255
    getParamFromRequest,
256
    loadConfigFromStorage,
257
    /**
258
     * Adds a listener that will be notified of all the MapStore2 events (**actions**), or only some of them.
259
     *
260
     * @memberof MapStore2
261
     * @static
262
     * @param {string} type type of actions to be captured (* for all)
263
     * @param {function} listener function to be called for each launched action; it will receive
264
     *  the action as the only argument
265
     * @example
266
     * MapStore2.onAction('CHANGE_MAP_VIEW', function(action) {
267
     *      console.log(action.zoom);
268
     * });
269
     */
270
    onAction: (type, listener) => {
271
        const listeners = actionListeners[type] || [];
2✔
272
        listeners.push(listener);
2✔
273
        actionListeners[type] = listeners;
2✔
274
    },
275
    /**
276
     * Removes an action listener.
277
     *
278
     * @memberof MapStore2
279
     * @static
280
     * @param {string} type type of actions that is captured by the listener (* for all)
281
     * @param {function} listener listener to be removed
282
     * @example
283
     * MapStore2.offAction('CHANGE_MAP_VIEW', listener);
284
     */
285
    offAction: (type, listener) => {
286
        const listeners = (actionListeners[type] || []).filter((l) => l !== listener);
2!
287
        actionListeners[type] = listeners;
1✔
288
    },
289
    /**
290
     * Adds a listener that will be notified of each state update.
291
     *
292
     * @memberof MapStore2
293
     * @static
294
     * @param {function} listener function to be called for each state udpate; it will receive
295
     *  the new state as the only argument
296
     * @param {function} [selector] optional function that will produce a partial/derived state
297
     * from the global state before calling the listeners
298
     * @example
299
     * MapStore2.onStateChange(function(map) {
300
     *      console.log(map.zoom);
301
     * }, function(state) {
302
     *      return (state.map && state.map.present) || state.map || {};
303
     * });
304
     */
305
    onStateChange: (listener, selector = (state) => state) => {
10✔
306
        stateChangeListeners.push({listener, selector});
1✔
307
    },
308
    /**
309
     * Removes a state listener.
310
     *
311
     * @memberof MapStore2
312
     * @static
313
     * @param {function} listener listener to be removed
314
     * @example
315
     * MapStore2.offStateChange(listener);
316
     */
317
    offStateChange: (listener) => {
318
        stateChangeListeners = stateChangeListeners.filter((l) => l !== listener);
×
319
    },
320
    /**
321
     * Returns a new custom API object using the given plugins list.
322
     *
323
     * @memberof MapStore2
324
     * @static
325
     * @param {object} plugins list of included plugins
326
     * @param {object} [options] default options (to be overridden on create)
327
     * @example
328
     * MapStore2.withPlugins({...});
329
     */
330
    withPlugins: (plugins, options) => {
NEW
331
        return Object.assign({}, MapStore2, {create: partialRight(MapStore2.create, partialRight.placeholder, partialRight.placeholder, plugins), defaultOptions: options || {}});
×
332
    },
333
    /**
334
     * Triggers an action
335
     * @param  {object} action The action to trigger.
336
     * @example
337
     * triggerAction({
338
     *       type: 'ZOOM_TO_EXTENT',
339
     *       extent: {
340
     *         minx: '-124.731422',
341
     *         miny: '24.955967',
342
     *         maxx: '-66.969849',
343
     *         maxy: '49.371735'
344
     *       },
345
     *       crs: 'EPSG:4326'
346
     *   })
347
     */
348
    triggerAction: (action) => triggerAction(action)
×
349
};
350

351
if (!global.Intl ) {
1!
352
    // Ensure Intl is loaded, then call the given callback
353
    ensureIntl();
×
354
}
355

356
export default MapStore2;
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc