diff --git a/src/component/draw-interaction/interaction.vue b/src/component/draw-interaction/interaction.vue index 08938859..8069ebd2 100644 --- a/src/component/draw-interaction/interaction.vue +++ b/src/component/draw-interaction/interaction.vue @@ -1,6 +1,8 @@ diff --git a/src/component/vector-source/source.vue b/src/component/vector-source/source.vue index c346e8b3..6cd6198b 100644 --- a/src/component/vector-source/source.vue +++ b/src/component/vector-source/source.vue @@ -90,8 +90,13 @@ loadingStrategy () { return this.strategyFactory() }, + dataFormatIdent () { + if (!this.olObjIdent) return + + return this.makeIdent(this.olObjIdent, 'data_format') + }, dataFormat () { - return this.formatFactory() + return this.instanceFactoryCall(this.dataFormatIdent, ::this.formatFactory) }, }, methods: { diff --git a/src/mixin/features-container.js b/src/mixin/features-container.js index 66a3f073..72f33ba7 100644 --- a/src/mixin/features-container.js +++ b/src/mixin/features-container.js @@ -8,10 +8,11 @@ import { instanceOf } from '../util/assert' import { forEach, isPlainObject } from '../util/minilo' import projTransforms from './proj-transforms' import rxSubs from './rx-subs' +import identMap from './ident-map' import { observableFromOlEvent } from '../rx-ext' export default { - mixins: [rxSubs, projTransforms], + mixins: [identMap, rxSubs, projTransforms], computed: { featureIds () { if (!this.rev) return [] @@ -28,6 +29,11 @@ export default { return this.getFeatures().map(::this.writeFeatureInDataProj) }, + featuresCollectionIdent () { + if (!this.olObjIdent) return + + return this.makeIdent(this.olObjIdent, 'features_collection') + }, }, methods: { /** @@ -116,11 +122,7 @@ export default { }, }, created () { - /** - * @type {Collection>} - * @private - */ - this._featuresCollection = new Collection() + this._featuresCollection = this.instanceFactoryCall(this.featuresCollectionIdent, () => new Collection()) this._featureSubs = {} this::defineServices() diff --git a/src/mixin/ident-map.js b/src/mixin/ident-map.js index 91cfcb89..a864d89b 100644 --- a/src/mixin/ident-map.js +++ b/src/mixin/ident-map.js @@ -1,13 +1,11 @@ import Vue from 'vue' import IdentityMap from '../util/identity-map' -import { identity } from '../util/minilo' +import { identity, stubObject, keys } from '../util/minilo' -// todo uncomment when IE 11 will die -// const IDENTITY_MAP_PROP = Symbol('identityMap') -const IDENTITY_MAP_PROP = 'identityMap' +const INSTANCES_POOL = 'instances' export default { - IDENTITY_MAP_PROP, + INSTANCES_POOL, props: { /** * Unique key for saving to identity map @@ -16,6 +14,32 @@ export default { */ ident: [String, Number], }, + data () { + return { + idents: stubObject(), + } + }, + computed: { + selfIdent () { + return this.makeSelfIdent() + }, + }, + watch: { + ident (value, prev) { + if (prev && this.$identityMap.has(prev)) { + this.$identityMap.unset(prev) + } + if (value && !this.$identityMap.has(value)) { + this.$identityMap.set(value) + } + }, + }, + beforeCreate () { + initIdentityMap() + }, + destroyed () { + this.unsetInstances() + }, methods: { /** * @param parts @@ -34,33 +58,65 @@ export default { makeIdent (...parts) { return parts.filter(identity).join('.') }, - }, - created () { - this::initIdentityMap() - }, - watch: { - ident (value, prev) { - if (prev && this.$identityMap.has(prev)) { - this.$identityMap.unset(prev) + /** + * Caches or reuse factory result in the identity map + * and returns result. + * + * @param {string|undefined} ident + * @param {function} factory + * @returns {*} + */ + instanceFactoryCall (ident, factory) { + if (ident && this.$identityMap.has(ident, INSTANCES_POOL)) { + return this.$identityMap.get(ident, INSTANCES_POOL) } - if (value && !this.$identityMap.has(value)) { - this.$identityMap.set(value) + + const val = factory() + + if (ident) { + this.idents[ident] = true + this.$identityMap.set(ident, val, INSTANCES_POOL) } + + return val + }, + /** + * @param {string|undefined} ident + * @returns {*} + */ + getInstance (ident) { + if (!ident) return + + return this.$identityMap.get(ident, INSTANCES_POOL) + }, + /** + * Unsets all self indets + * @return {void} + */ + unsetInstances () { + keys(this.idents).forEach(ident => this.$identityMap.unset(ident, INSTANCES_POOL)) }, }, } -/** - * @private - */ function initIdentityMap () { - if (!this[IDENTITY_MAP_PROP]) { - Vue[IDENTITY_MAP_PROP] = Vue.prototype[IDENTITY_MAP_PROP] = new IdentityMap() + const imap = new IdentityMap() + + if (!('$identityMap' in Vue)) { + Object.defineProperties(Vue, { + $identityMap: { + enumerable: true, + get: () => imap, + }, + }) + } + + if (!('$identityMap' in Vue.prototype)) { + Object.defineProperties(Vue.prototype, { + $identityMap: { + enumerable: true, + get: () => imap, + }, + }) } - Object.defineProperties(this, { - $identityMap: { - enumerable: true, - get: () => this[IDENTITY_MAP_PROP], - }, - }) } diff --git a/src/mixin/interactions-container.js b/src/mixin/interactions-container.js index fb214b4f..66bd7e11 100644 --- a/src/mixin/interactions-container.js +++ b/src/mixin/interactions-container.js @@ -5,16 +5,22 @@ import { merge as mergeObs } from 'rxjs/observable' import { getInteractionId, getInteractionPriority, initializeInteraction } from '../ol-ext' import { instanceOf } from '../util/assert' import rxSubs from './rx-subs' +import identMap from './ident-map' import { observableFromOlEvent } from '../rx-ext' export default { - mixins: [rxSubs], + mixins: [identMap, rxSubs], computed: { interactionIds () { if (!this.rev) return [] return this.getInteractions().map(getInteractionId) }, + interactionsCollectionIdent () { + if (!this.olObjIdent) return + + return this.makeIdent(this.olObjIdent, 'interactions_collection') + }, }, methods: { /** @@ -103,11 +109,7 @@ export default { }, }, created () { - /** - * @type {Collection>} - * @private - */ - this._interactionsCollection = new Collection() + this._interactionsCollection = this.instanceFactoryCall(this.interactionsCollectionIdent, () => new Collection()) this::defineServices() this::subscribeToCollectionEvents() diff --git a/src/mixin/layers-container.js b/src/mixin/layers-container.js index fb5d8864..b44babab 100644 --- a/src/mixin/layers-container.js +++ b/src/mixin/layers-container.js @@ -6,15 +6,21 @@ import { getLayerId, initializeLayer } from '../ol-ext' import { observableFromOlEvent } from '../rx-ext' import { instanceOf } from '../util/assert' import rxSubs from './rx-subs' +import identMap from './ident-map' export default { - mixins: [rxSubs], + mixins: [identMap, rxSubs], computed: { layerIds () { if (!this.rev) return [] return this.getLayers().map(getLayerId) }, + layersCollectionIdent () { + if (!this.olObjIdent) return + + return this.makeIdent(this.olObjIdent, 'layers_collection') + }, }, methods: { /** @@ -80,11 +86,7 @@ export default { }, }, created () { - /** - * @type {Collection} - * @private - */ - this._layersCollection = new Collection() + this._layersCollection = this.instanceFactoryCall(this.layersCollectionIdent, () => new Collection()) this::defineServices() this::subscribeToCollectionEvents() diff --git a/src/mixin/ol-cmp.js b/src/mixin/ol-cmp.js index b903f4bc..6ec39fb3 100644 --- a/src/mixin/ol-cmp.js +++ b/src/mixin/ol-cmp.js @@ -9,7 +9,6 @@ import rxSubs from './rx-subs' import services from './services' const VM_PROP = 'vm' -const INSTANCE_PROMISE_POOL = 'instance_promise' /** * Basic ol component mixin. @@ -17,7 +16,6 @@ const INSTANCE_PROMISE_POOL = 'instance_promise' */ export default { VM_PROP, - INSTANCE_PROMISE_POOL, mixins: [identMap, rxSubs, services], props: { id: { @@ -40,6 +38,9 @@ export default { vmName () { return [this.cmpName, this.id].filter(identity).join(' ') }, + olObjIdent () { + return this.selfIdent + }, }, methods: { /** @@ -52,20 +53,7 @@ export default { * @protected */ async init () { - let createPromise - - const ident = this.makeSelfIdent() - if (ident && this.$identityMap.has(ident, INSTANCE_PROMISE_POOL)) { - createPromise = this.$identityMap.get(ident, INSTANCE_PROMISE_POOL) - } else { - createPromise = this.createOlObject() - - if (ident) { - this.$identityMap.set(ident, createPromise, INSTANCE_PROMISE_POOL) - } - } - - this._olObject = await createPromise + this._olObject = await this.instanceFactoryCall(this.olObjIdent, ::this.createOlObject) this._olObject[VM_PROP] || (this._olObject[VM_PROP] = []) if (!this._olObject[VM_PROP].includes(this)) { // for loaded from IdentityMap @@ -87,10 +75,8 @@ export default { * @protected */ deinit () { - const ident = this.makeSelfIdent() - if (ident) { - this.$identityMap.unset(ident, INSTANCE_PROMISE_POOL) - } + this.unsetInstances() + if (this._olObject) { this._olObject[VM_PROP] = this._olObject[VM_PROP].filter(vm => vm !== this) this._olObject = undefined diff --git a/src/mixin/overlays-container.js b/src/mixin/overlays-container.js index 8ab6e100..ace20e48 100644 --- a/src/mixin/overlays-container.js +++ b/src/mixin/overlays-container.js @@ -6,15 +6,21 @@ import { getOverlayId, initializeOverlay } from '../ol-ext' import { observableFromOlEvent } from '../rx-ext' import { instanceOf } from '../util/assert' import rxSubs from './rx-subs' +import identMap from './ident-map' export default { - mixins: [rxSubs], + mixins: [identMap, rxSubs], computed: { overlayIds () { if (!this.rev) return [] return this.getOverlays().map(getOverlayId) }, + overlaysCollectionIdent () { + if (!this.olObjIdent) return + + return this.makeIdent(this.olObjIdent, 'overlays_collection') + }, }, methods: { /** @@ -80,11 +86,7 @@ export default { }, }, created () { - /** - * @type {Collection} - * @private - */ - this._overlaysCollection = new Collection() + this._overlaysCollection = this.instanceFactoryCall(this.overlaysCollectionIdent, () => new Collection()) this::defineServices() this::subscribeToCollectionEvents() diff --git a/src/mixin/tile-source.js b/src/mixin/tile-source.js index 63f820c7..e2e56000 100644 --- a/src/mixin/tile-source.js +++ b/src/mixin/tile-source.js @@ -99,6 +99,11 @@ export default { return url }, + tileGridIdent () { + if (!this.olObjIdent) return + + return this.makeIdent(this.olObjIdent, 'tile_grid') + }, }, methods: { createTileGrid () { @@ -118,7 +123,7 @@ export default { * @type {module:ol/Tile~UrlFunction} * @protected */ - this._tileGrid = this.createTileGrid() + this._tileGrid = this.instanceFactoryCall(this.tileGridIdent, ::this.createTileGrid) return this::source.methods.init() }, diff --git a/src/util/assert.js b/src/util/assert.js index 5d4816b2..95b9edb9 100644 --- a/src/util/assert.js +++ b/src/util/assert.js @@ -34,19 +34,21 @@ export function ok (value, message) { /** * @param {*} value + * @param {string|undefined} [msg] * @throws {AssertionError} */ -export function numeric (value) { - assert(isNumeric(value), 'value is a number') +export function numeric (value, msg) { + assert(isNumeric(value), msg || 'value is a number') } /** * @param {*} value * @param {Function} Ctor + * @param {string|undefined} [msg] * @throws {AssertionError} */ -export function instanceOf (value, Ctor) { - assert(value instanceof Ctor, `value is an instance of ${Ctor.name}`) +export function instanceOf (value, Ctor, msg) { + assert(value instanceof Ctor, msg || `value is an instance of ${Ctor.name}`) } export function hasOlObject (vm) { diff --git a/src/util/identity-map.js b/src/util/identity-map.js index 1e829037..f83759ca 100644 --- a/src/util/identity-map.js +++ b/src/util/identity-map.js @@ -2,14 +2,14 @@ * Simple Identity map with refs count */ export default class IdentityMap { - pools = Object.create(null) + _pools = Object.create(null) /** * @param {string} pool * @private */ _preparePool (pool) { - this.pools[pool] || (this.pools[pool] = Object.create(null)) + this._pools[pool] || (this._pools[pool] = Object.create(null)) } /** @@ -22,7 +22,7 @@ export default class IdentityMap { this._preparePool(pool) - this.pools[pool][id] = { + this._pools[pool][id] = { value, refs: 1, } @@ -35,11 +35,11 @@ export default class IdentityMap { get (id, pool = 'default') { this._preparePool(pool) - let rec = this.pools[pool][id] + let rec = this._pools[pool][id] if (!rec || rec.value == null) return rec.refs++ - this.pools[pool][id] = rec + this._pools[pool][id] = rec return rec.value } @@ -51,14 +51,12 @@ export default class IdentityMap { unset (id, pool = 'default') { this._preparePool(pool) - let rec = this.pools[pool][id] + let rec = this._pools[pool][id] if (!rec || rec.value == null) return rec.refs-- if (rec.refs === 0) { - delete this.pools[pool][id] - } else { - this.pools[pool][id] = rec + delete this._pools[pool][id] } } @@ -70,7 +68,7 @@ export default class IdentityMap { has (id, pool = 'default') { this._preparePool(pool) - return !!this.pools[pool][id] + return !!this._pools[pool][id] } /** @@ -80,7 +78,7 @@ export default class IdentityMap { ids (pool = 'default') { this._preparePool(pool) - return Object.keys(this.pools[pool]) + return Object.keys(this._pools[pool]) } /** @@ -91,6 +89,6 @@ export default class IdentityMap { refs (id, pool = 'default') { this._preparePool(pool) - return this.has(id, pool) ? this.pools[pool][id].refs : undefined + return this.has(id, pool) ? this._pools[pool][id].refs : undefined } } diff --git a/src/util/minilo.js b/src/util/minilo.js index 175ec92c..52adcbbe 100644 --- a/src/util/minilo.js +++ b/src/util/minilo.js @@ -29,6 +29,10 @@ export function stubArray () { return [] } +export function stubObject () { + return Object.create(null) +} + export function identity (value) { return value } @@ -185,6 +189,14 @@ export function isNotEmpty (value) { return !isEmpty(value) } +export function keys (object) { + return Object.keys(object) +} + +export function values (object) { + return Object.values(object) +} + export function forEach (collection, iteratee) { let keys = Object.keys(collection) for (let i = 0, l = keys.length; i < l; i++) { diff --git a/test/app.vue b/test/app.vue index 51b75a62..6d123a0f 100644 --- a/test/app.vue +++ b/test/app.vue @@ -6,9 +6,11 @@ Selected:
{{ selectedFeatureIds }} + - + @@ -30,18 +32,19 @@ - - - - - - - - - + + + + + + + + + + @@ -62,36 +65,25 @@ resolution: 39135.75848201024, center: [100, 10], rotation: 0, + mapVisible: true, countriesUrl: 'https://openlayers.org/en/latest/examples/data/geojson/countries.geojson', countries: [], featureId: undefined, features: [], selectedFeatures: [], - drawFeatures: [ - { - type: 'Feature', - id: '213456789', - properties: {}, - geometry: { - type: 'Polygon', - coordinates: [[ - [0, 0], - [20, 0], - [20, 20], - [0, 20], - [0, 0], - ]], - }, - }, - ], + drawFeatures: [], controls: true, interactions: true, - places: range(0, 100).map(i => ({ + points: range(0, 100).map(i => ({ + type: 'Feature', id: 'random-' + i, - geo: [ - random(0, 50), - random(0, 50), - ], + geometry: { + type: 'Point', + coordinates: [ + random(0, 50), + random(0, 50), + ], + }, })), } }, @@ -132,26 +124,11 @@ return [style] } }, - getPlaceFeatures () { - const sourceVm = this.$refs.placesSource - sourceVm.$source.clear() - - const features = this.places.map(place => { - const geometry = new Point(place.geo) - geometry.transform('EPSG:4326', 'EPSG:3857') - - let feature = new Feature({ geometry }) - feature.setProperties(place) - feature.setId(place.id) - - return feature - }) - - sourceVm.$source.addFeatures(features) + toggleMap () { + this.mapVisible = !this.mapVisible + this.drawFeatures = [] }, }, - mounted () { - }, } diff --git a/test/main.js b/test/main.js index f92e3f07..029f3be1 100644 --- a/test/main.js +++ b/test/main.js @@ -1,6 +1,6 @@ import '@babel/polyfill' import Vue from 'vue' -import VueLayers from '@' +import VueLayers from '../src' import App from './app.vue' Vue.performance = true @@ -10,6 +10,7 @@ Vue.use(VueLayers, { // dataProjection: 'EPSG:4326', }) console.log(process.env.NODE_ENV) +console.dir(Vue) console.dir(VueLayers) /* eslint-disable no-new */ new Vue({