Merge pull request #11188 from michalzielanski/late-obj-init
Late object initialization
This commit is contained in:
+3
-1
@@ -128,7 +128,9 @@ class Feature extends BaseObject {
|
|||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
clone() {
|
clone() {
|
||||||
const clone = new Feature(this.getProperties());
|
const clone = new Feature(
|
||||||
|
this.hasProperties() ? this.getProperties() : null
|
||||||
|
);
|
||||||
clone.setGeometryName(this.getGeometryName());
|
clone.setGeometryName(this.getGeometryName());
|
||||||
const geometry = this.getGeometry();
|
const geometry = this.getGeometry();
|
||||||
if (geometry) {
|
if (geometry) {
|
||||||
|
|||||||
+21
-10
@@ -4,7 +4,7 @@
|
|||||||
import Event from './events/Event.js';
|
import Event from './events/Event.js';
|
||||||
import ObjectEventType from './ObjectEventType.js';
|
import ObjectEventType from './ObjectEventType.js';
|
||||||
import Observable from './Observable.js';
|
import Observable from './Observable.js';
|
||||||
import {assign} from './obj.js';
|
import {assign, isEmpty} from './obj.js';
|
||||||
import {getUid} from './util.js';
|
import {getUid} from './util.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -95,9 +95,9 @@ class BaseObject extends Observable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @type {!Object<string, *>}
|
* @type {Object<string, *>}
|
||||||
*/
|
*/
|
||||||
this.values_ = {};
|
this.values_ = null;
|
||||||
|
|
||||||
if (opt_values !== undefined) {
|
if (opt_values !== undefined) {
|
||||||
this.setProperties(opt_values);
|
this.setProperties(opt_values);
|
||||||
@@ -112,7 +112,7 @@ class BaseObject extends Observable {
|
|||||||
*/
|
*/
|
||||||
get(key) {
|
get(key) {
|
||||||
let value;
|
let value;
|
||||||
if (this.values_.hasOwnProperty(key)) {
|
if (this.values_ && this.values_.hasOwnProperty(key)) {
|
||||||
value = this.values_[key];
|
value = this.values_[key];
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
@@ -124,7 +124,7 @@ class BaseObject extends Observable {
|
|||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
getKeys() {
|
getKeys() {
|
||||||
return Object.keys(this.values_);
|
return (this.values_ && Object.keys(this.values_)) || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -133,7 +133,14 @@ class BaseObject extends Observable {
|
|||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
getProperties() {
|
getProperties() {
|
||||||
return assign({}, this.values_);
|
return (this.values_ && assign({}, this.values_)) || {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {boolean} The object has properties.
|
||||||
|
*/
|
||||||
|
hasProperties() {
|
||||||
|
return !!this.values_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -156,11 +163,12 @@ class BaseObject extends Observable {
|
|||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
set(key, value, opt_silent) {
|
set(key, value, opt_silent) {
|
||||||
|
const values = this.values_ || (this.values_ = {});
|
||||||
if (opt_silent) {
|
if (opt_silent) {
|
||||||
this.values_[key] = value;
|
values[key] = value;
|
||||||
} else {
|
} else {
|
||||||
const oldValue = this.values_[key];
|
const oldValue = values[key];
|
||||||
this.values_[key] = value;
|
values[key] = value;
|
||||||
if (oldValue !== value) {
|
if (oldValue !== value) {
|
||||||
this.notify(key, oldValue);
|
this.notify(key, oldValue);
|
||||||
}
|
}
|
||||||
@@ -187,9 +195,12 @@ class BaseObject extends Observable {
|
|||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
unset(key, opt_silent) {
|
unset(key, opt_silent) {
|
||||||
if (key in this.values_) {
|
if (this.values_ && key in this.values_) {
|
||||||
const oldValue = this.values_[key];
|
const oldValue = this.values_[key];
|
||||||
delete this.values_[key];
|
delete this.values_[key];
|
||||||
|
if (isEmpty(this.values_)) {
|
||||||
|
this.values_ = null;
|
||||||
|
}
|
||||||
if (!opt_silent) {
|
if (!opt_silent) {
|
||||||
this.notify(key, oldValue);
|
this.notify(key, oldValue);
|
||||||
}
|
}
|
||||||
|
|||||||
+32
-29
@@ -40,21 +40,21 @@ class Target extends Disposable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @type {!Object<string, number>}
|
* @type {Object<string, number>}
|
||||||
*/
|
*/
|
||||||
this.pendingRemovals_ = {};
|
this.pendingRemovals_ = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @type {!Object<string, number>}
|
* @type {Object<string, number>}
|
||||||
*/
|
*/
|
||||||
this.dispatching_ = {};
|
this.dispatching_ = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @type {!Object<string, Array<import("../events.js").Listener>>}
|
* @type {Object<string, Array<import("../events.js").Listener>>}
|
||||||
*/
|
*/
|
||||||
this.listeners_ = {};
|
this.listeners_ = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -65,13 +65,10 @@ class Target extends Disposable {
|
|||||||
if (!type || !listener) {
|
if (!type || !listener) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let listeners = this.listeners_[type];
|
const listeners = this.listeners_ || (this.listeners_ = {});
|
||||||
if (!listeners) {
|
const listenersForType = listeners[type] || (listeners[type] = []);
|
||||||
listeners = [];
|
if (listenersForType.indexOf(listener) === -1) {
|
||||||
this.listeners_[type] = listeners;
|
listenersForType.push(listener);
|
||||||
}
|
|
||||||
if (listeners.indexOf(listener) === -1) {
|
|
||||||
listeners.push(listener);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,14 +89,17 @@ class Target extends Disposable {
|
|||||||
if (!evt.target) {
|
if (!evt.target) {
|
||||||
evt.target = this.eventTarget_ || this;
|
evt.target = this.eventTarget_ || this;
|
||||||
}
|
}
|
||||||
const listeners = this.listeners_[type];
|
const listeners = this.listeners_ && this.listeners_[type];
|
||||||
let propagate;
|
let propagate;
|
||||||
if (listeners) {
|
if (listeners) {
|
||||||
if (!(type in this.dispatching_)) {
|
const dispatching = this.dispatching_ || (this.dispatching_ = {});
|
||||||
this.dispatching_[type] = 0;
|
const pendingRemovals =
|
||||||
this.pendingRemovals_[type] = 0;
|
this.pendingRemovals_ || (this.pendingRemovals_ = {});
|
||||||
|
if (!(type in dispatching)) {
|
||||||
|
dispatching[type] = 0;
|
||||||
|
pendingRemovals[type] = 0;
|
||||||
}
|
}
|
||||||
++this.dispatching_[type];
|
++dispatching[type];
|
||||||
for (let i = 0, ii = listeners.length; i < ii; ++i) {
|
for (let i = 0, ii = listeners.length; i < ii; ++i) {
|
||||||
if ('handleEvent' in listeners[i]) {
|
if ('handleEvent' in listeners[i]) {
|
||||||
propagate = /** @type {import("../events.js").ListenerObject} */ (listeners[
|
propagate = /** @type {import("../events.js").ListenerObject} */ (listeners[
|
||||||
@@ -115,14 +115,14 @@ class Target extends Disposable {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
--this.dispatching_[type];
|
--dispatching[type];
|
||||||
if (this.dispatching_[type] === 0) {
|
if (dispatching[type] === 0) {
|
||||||
let pendingRemovals = this.pendingRemovals_[type];
|
let pr = pendingRemovals[type];
|
||||||
delete this.pendingRemovals_[type];
|
delete pendingRemovals[type];
|
||||||
while (pendingRemovals--) {
|
while (pr--) {
|
||||||
this.removeEventListener(type, VOID);
|
this.removeEventListener(type, VOID);
|
||||||
}
|
}
|
||||||
delete this.dispatching_[type];
|
delete dispatching[type];
|
||||||
}
|
}
|
||||||
return propagate;
|
return propagate;
|
||||||
}
|
}
|
||||||
@@ -132,7 +132,7 @@ class Target extends Disposable {
|
|||||||
* Clean up.
|
* Clean up.
|
||||||
*/
|
*/
|
||||||
disposeInternal() {
|
disposeInternal() {
|
||||||
clear(this.listeners_);
|
this.listeners_ && clear(this.listeners_);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -140,10 +140,10 @@ class Target extends Disposable {
|
|||||||
* order that they will be called in.
|
* order that they will be called in.
|
||||||
*
|
*
|
||||||
* @param {string} type Type.
|
* @param {string} type Type.
|
||||||
* @return {Array<import("../events.js").Listener>} Listeners.
|
* @return {Array<import("../events.js").Listener>|undefined} Listeners.
|
||||||
*/
|
*/
|
||||||
getListeners(type) {
|
getListeners(type) {
|
||||||
return this.listeners_[type];
|
return (this.listeners_ && this.listeners_[type]) || undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -152,6 +152,9 @@ class Target extends Disposable {
|
|||||||
* @return {boolean} Has listeners.
|
* @return {boolean} Has listeners.
|
||||||
*/
|
*/
|
||||||
hasListener(opt_type) {
|
hasListener(opt_type) {
|
||||||
|
if (!this.listeners_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return opt_type
|
return opt_type
|
||||||
? opt_type in this.listeners_
|
? opt_type in this.listeners_
|
||||||
: Object.keys(this.listeners_).length > 0;
|
: Object.keys(this.listeners_).length > 0;
|
||||||
@@ -162,11 +165,11 @@ class Target extends Disposable {
|
|||||||
* @param {import("../events.js").Listener} listener Listener.
|
* @param {import("../events.js").Listener} listener Listener.
|
||||||
*/
|
*/
|
||||||
removeEventListener(type, listener) {
|
removeEventListener(type, listener) {
|
||||||
const listeners = this.listeners_[type];
|
const listeners = this.listeners_ && this.listeners_[type];
|
||||||
if (listeners) {
|
if (listeners) {
|
||||||
const index = listeners.indexOf(listener);
|
const index = listeners.indexOf(listener);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
if (type in this.pendingRemovals_) {
|
if (this.pendingRemovals_ && type in this.pendingRemovals_) {
|
||||||
// make listener a no-op, and remove later in #dispatchEvent()
|
// make listener a no-op, and remove later in #dispatchEvent()
|
||||||
listeners[index] = VOID;
|
listeners[index] = VOID;
|
||||||
++this.pendingRemovals_[type];
|
++this.pendingRemovals_[type];
|
||||||
|
|||||||
@@ -199,6 +199,11 @@ class EsriJSON extends JSONFeature {
|
|||||||
writeFeatureObject(feature, opt_options) {
|
writeFeatureObject(feature, opt_options) {
|
||||||
opt_options = this.adaptOptions(opt_options);
|
opt_options = this.adaptOptions(opt_options);
|
||||||
const object = {};
|
const object = {};
|
||||||
|
if (!feature.hasProperties()) {
|
||||||
|
object['attributes'] = {};
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
const properties = feature.getProperties();
|
||||||
const geometry = feature.getGeometry();
|
const geometry = feature.getGeometry();
|
||||||
if (geometry) {
|
if (geometry) {
|
||||||
object['geometry'] = writeGeometry(geometry, opt_options);
|
object['geometry'] = writeGeometry(geometry, opt_options);
|
||||||
@@ -214,9 +219,8 @@ class EsriJSON extends JSONFeature {
|
|||||||
),
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
delete properties[feature.getGeometryName()];
|
||||||
}
|
}
|
||||||
const properties = feature.getProperties();
|
|
||||||
delete properties[feature.getGeometryName()];
|
|
||||||
if (!isEmpty(properties)) {
|
if (!isEmpty(properties)) {
|
||||||
object['attributes'] = properties;
|
object['attributes'] = properties;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+24
-21
@@ -212,29 +212,32 @@ class GML2 extends GMLBase {
|
|||||||
context.serializers = {};
|
context.serializers = {};
|
||||||
context.serializers[featureNS] = {};
|
context.serializers[featureNS] = {};
|
||||||
}
|
}
|
||||||
const properties = feature.getProperties();
|
|
||||||
const keys = [];
|
const keys = [];
|
||||||
const values = [];
|
const values = [];
|
||||||
for (const key in properties) {
|
if (feature.hasProperties()) {
|
||||||
const value = properties[key];
|
const properties = feature.getProperties();
|
||||||
if (value !== null) {
|
for (const key in properties) {
|
||||||
keys.push(key);
|
const value = properties[key];
|
||||||
values.push(value);
|
if (value !== null) {
|
||||||
if (
|
keys.push(key);
|
||||||
key == geometryName ||
|
values.push(value);
|
||||||
typeof (/** @type {?} */ (value).getSimplifiedGeometry) === 'function'
|
if (
|
||||||
) {
|
key == geometryName ||
|
||||||
if (!(key in context.serializers[featureNS])) {
|
typeof (/** @type {?} */ (value).getSimplifiedGeometry) ===
|
||||||
context.serializers[featureNS][key] = makeChildAppender(
|
'function'
|
||||||
this.writeGeometryElement,
|
) {
|
||||||
this
|
if (!(key in context.serializers[featureNS])) {
|
||||||
);
|
context.serializers[featureNS][key] = makeChildAppender(
|
||||||
}
|
this.writeGeometryElement,
|
||||||
} else {
|
this
|
||||||
if (!(key in context.serializers[featureNS])) {
|
);
|
||||||
context.serializers[featureNS][key] = makeChildAppender(
|
}
|
||||||
writeStringTextNode
|
} else {
|
||||||
);
|
if (!(key in context.serializers[featureNS])) {
|
||||||
|
context.serializers[featureNS][key] = makeChildAppender(
|
||||||
|
writeStringTextNode
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-21
@@ -843,29 +843,32 @@ class GML3 extends GMLBase {
|
|||||||
context.serializers = {};
|
context.serializers = {};
|
||||||
context.serializers[featureNS] = {};
|
context.serializers[featureNS] = {};
|
||||||
}
|
}
|
||||||
const properties = feature.getProperties();
|
|
||||||
const keys = [];
|
const keys = [];
|
||||||
const values = [];
|
const values = [];
|
||||||
for (const key in properties) {
|
if (feature.hasProperties()) {
|
||||||
const value = properties[key];
|
const properties = feature.getProperties();
|
||||||
if (value !== null) {
|
for (const key in properties) {
|
||||||
keys.push(key);
|
const value = properties[key];
|
||||||
values.push(value);
|
if (value !== null) {
|
||||||
if (
|
keys.push(key);
|
||||||
key == geometryName ||
|
values.push(value);
|
||||||
typeof (/** @type {?} */ (value).getSimplifiedGeometry) === 'function'
|
if (
|
||||||
) {
|
key == geometryName ||
|
||||||
if (!(key in context.serializers[featureNS])) {
|
typeof (/** @type {?} */ (value).getSimplifiedGeometry) ===
|
||||||
context.serializers[featureNS][key] = makeChildAppender(
|
'function'
|
||||||
this.writeGeometryElement,
|
) {
|
||||||
this
|
if (!(key in context.serializers[featureNS])) {
|
||||||
);
|
context.serializers[featureNS][key] = makeChildAppender(
|
||||||
}
|
this.writeGeometryElement,
|
||||||
} else {
|
this
|
||||||
if (!(key in context.serializers[featureNS])) {
|
);
|
||||||
context.serializers[featureNS][key] = makeChildAppender(
|
}
|
||||||
writeStringTextNode
|
} else {
|
||||||
);
|
if (!(key in context.serializers[featureNS])) {
|
||||||
|
context.serializers[featureNS][key] = makeChildAppender(
|
||||||
|
writeStringTextNode
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -207,16 +207,22 @@ class GeoJSON extends JSONFeature {
|
|||||||
object.id = id;
|
object.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
const geometry = feature.getGeometry();
|
if (!feature.hasProperties()) {
|
||||||
if (geometry) {
|
return object;
|
||||||
object.geometry = writeGeometry(geometry, opt_options);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const properties = feature.getProperties();
|
const properties = feature.getProperties();
|
||||||
delete properties[feature.getGeometryName()];
|
const geometry = feature.getGeometry();
|
||||||
|
if (geometry) {
|
||||||
|
object.geometry = writeGeometry(geometry, opt_options);
|
||||||
|
|
||||||
|
delete properties[feature.getGeometryName()];
|
||||||
|
}
|
||||||
|
|
||||||
if (!isEmpty(properties)) {
|
if (!isEmpty(properties)) {
|
||||||
object.properties = properties;
|
object.properties = properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,9 +24,6 @@ describe('ol.events.EventTarget', function () {
|
|||||||
expect(eventTarget).to.be.a(EventTarget);
|
expect(eventTarget).to.be.a(EventTarget);
|
||||||
expect(eventTarget).to.be.a(Disposable);
|
expect(eventTarget).to.be.a(Disposable);
|
||||||
});
|
});
|
||||||
it('creates an empty listeners_ object', function () {
|
|
||||||
expect(Object.keys(eventTarget.listeners_)).to.have.length(0);
|
|
||||||
});
|
|
||||||
it('accepts a default target', function (done) {
|
it('accepts a default target', function (done) {
|
||||||
const defaultTarget = {};
|
const defaultTarget = {};
|
||||||
const target = new EventTarget(defaultTarget);
|
const target = new EventTarget(defaultTarget);
|
||||||
@@ -36,16 +33,21 @@ describe('ol.events.EventTarget', function () {
|
|||||||
});
|
});
|
||||||
target.dispatchEvent('my-event');
|
target.dispatchEvent('my-event');
|
||||||
});
|
});
|
||||||
|
it('does not initialize objects in advance', function () {
|
||||||
|
expect(eventTarget.pendingRemovals_).to.be(null);
|
||||||
|
expect(eventTarget.dispatching_).to.be(null);
|
||||||
|
expect(eventTarget.listeners_).to.be(null);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#hasListener', function () {
|
describe('#hasListener', function () {
|
||||||
it('reports any listeners when called without argument', function () {
|
it('reports any listeners when called without argument', function () {
|
||||||
expect(eventTarget.hasListener()).to.be(false);
|
expect(eventTarget.hasListener()).to.be(false);
|
||||||
eventTarget.listeners_['foo'] = [function () {}];
|
eventTarget.addEventListener('foo', function () {});
|
||||||
expect(eventTarget.hasListener()).to.be(true);
|
expect(eventTarget.hasListener()).to.be(true);
|
||||||
});
|
});
|
||||||
it('reports listeners for the type passed as argument', function () {
|
it('reports listeners for the type passed as argument', function () {
|
||||||
eventTarget.listeners_['foo'] = [function () {}];
|
eventTarget.addEventListener('foo', function () {});
|
||||||
expect(eventTarget.hasListener('foo')).to.be(true);
|
expect(eventTarget.hasListener('foo')).to.be(true);
|
||||||
expect(eventTarget.hasListener('bar')).to.be(false);
|
expect(eventTarget.hasListener('bar')).to.be(false);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -101,6 +101,22 @@ describe('ol.Object', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('hasProperties', function () {
|
||||||
|
it('has no properties after creation', function () {
|
||||||
|
expect(o.hasProperties()).to.eql(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has properties after set', function () {
|
||||||
|
o.set('foo', 1);
|
||||||
|
expect(o.hasProperties()).to.eql(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has no properties after unset all', function () {
|
||||||
|
o.unset('foo');
|
||||||
|
expect(o.hasProperties()).to.eql(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('notify', function () {
|
describe('notify', function () {
|
||||||
let listener1, listener2;
|
let listener1, listener2;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user