diff --git a/src/all.js b/src/all.js index cb8130d6f3..9c54b54632 100644 --- a/src/all.js +++ b/src/all.js @@ -1,3 +1,4 @@ goog.provide('ol'); goog.require('ol.MVCObject'); +goog.require('ol.MVCArray'); diff --git a/src/ol/mvcarray.js b/src/ol/mvcarray.js new file mode 100644 index 0000000000..98e3abc252 --- /dev/null +++ b/src/ol/mvcarray.js @@ -0,0 +1,218 @@ + +/** + * @fileoverview An implementation of Google Maps' MVCArray. + * @see https://developers.google.com/maps/documentation/javascript/reference + */ + +goog.provide('ol.MVCArray'); +goog.provide('ol.MVCArrayEvent'); +goog.provide('ol.MVCArrayEventType'); + +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.events.Event'); +goog.require('ol.MVCObject'); + + +/** + * @enum {string} + */ +ol.MVCArrayEventType = { + INSERT_AT: 'insert_at', + REMOVE_AT: 'remove_at', + SET_AT: 'set_at' +}; + + + +/** + * @constructor + * @extends {goog.events.Event} + * @param {ol.MVCArrayEventType} type Type. + * @param {number} index Index. + * @param {*=} opt_prev Value. + * @param {Object=} opt_target Target. + */ +ol.MVCArrayEvent = function(type, index, opt_prev, opt_target) { + + goog.base(this, type, opt_target); + + /** + * @type {number} + */ + this.index = index; + + /** + * @type {*} + */ + this.prev = opt_prev; + +}; +goog.inherits(ol.MVCArrayEvent, goog.events.Event); + + + +/** + * @constructor + * @extends {ol.MVCObject} + * @param {Array=} opt_array Array. + */ +ol.MVCArray = function(opt_array) { + + goog.base(this); + + /** + * @private + * @type {Array} + */ + this.array_ = goog.isDefAndNotNull(opt_array) ? opt_array : []; + + this.updateLength_(); + +}; +goog.inherits(ol.MVCArray, ol.MVCObject); + + +/** + * @const + * @type {string} + */ +ol.MVCArray.LENGTH = 'length'; + + +/** + * @param {ol.MVCArray|Array} arg Argument. + * @return {ol.MVCArray} MVCArray. + */ +ol.MVCArray.create = function(arg) { + if (arg instanceof ol.MVCArray) { + return arg; + } else { + return new ol.MVCArray(arg); + } +}; + + +/** + */ +ol.MVCArray.prototype.clear = function() { + while (this[ol.MVCArray.LENGTH]) { + this.pop(); + } +}; + + +/** + * @param {function(*, number)} callback Callback. + */ +ol.MVCArray.prototype.forEach = function(callback) { + goog.array.forEach(this.array_, callback); +}; + + +/** + * @return {Array} Array. + */ +ol.MVCArray.prototype.getArray = function() { + return this.array_; +}; + + +/** + * @param {number} index Index. + * @return {*} Element. + */ +ol.MVCArray.prototype.getAt = function(index) { + return this.array_[index]; +}; + + +/** + * @return {number} Length. + */ +ol.MVCArray.prototype.getLength = function() { + return /** @type {number} */ (this.get(ol.MVCArray.LENGTH)); +}; + + +/** + * @param {number} index Index. + * @param {*} elem Element. + */ +ol.MVCArray.prototype.insertAt = function(index, elem) { + goog.array.insertAt(this.array_, elem, index); + this.updateLength_(); + this.dispatchEvent(new ol.MVCArrayEvent( + ol.MVCArrayEventType.INSERT_AT, index, undefined, this)); + if (this[ol.MVCArrayEventType.INSERT_AT]) { + this[ol.MVCArrayEventType.INSERT_AT](index); + } +}; + + +/** + * @return {*} Element. + */ +ol.MVCArray.prototype.pop = function() { + return this.removeAt(this.getLength() - 1); +}; + + +/** + * @param {*} elem Element. + * @return {number} Length. + */ +ol.MVCArray.prototype.push = function(elem) { + var n = this.array_.length; + this.insertAt(n, elem); + return n; +}; + + +/** + * @param {number} index Index. + * @return {*} Value. + */ +ol.MVCArray.prototype.removeAt = function(index) { + var prev = this.array_[index]; + goog.array.removeAt(this.array_, index); + this.updateLength_(); + this.dispatchEvent(new ol.MVCArrayEvent(ol.MVCArrayEventType.REMOVE_AT, + index, prev, this)); + if (this[ol.MVCArrayEventType.REMOVE_AT]) { + this[ol.MVCArrayEventType.REMOVE_AT](index); + } + return prev; +}; + + +/** + * @param {number} index Index. + * @param {*} elem Element. + */ +ol.MVCArray.prototype.setAt = function(index, elem) { + var n = this[ol.MVCArray.LENGTH]; + if (index < n) { + var prev = this.array_[index]; + this.array_[index] = elem; + this.dispatchEvent(new ol.MVCArrayEvent(ol.MVCArrayEventType.SET_AT, + index, prev, this)); + if (this[ol.MVCArrayEventType.SET_AT]) { + this[ol.MVCArrayEventType.SET_AT](index, prev); + } + } else { + var j; + for (j = n; j < index; ++j) { + this.insertAt(j, undefined); + } + this.insertAt(index, elem); + } +}; + + +/** + * @private + */ +ol.MVCArray.prototype.updateLength_ = function() { + this.set('length', this.array_.length); +}; diff --git a/src/ol/mvcarray_test.js b/src/ol/mvcarray_test.js new file mode 100644 index 0000000000..bb41c2b7ba --- /dev/null +++ b/src/ol/mvcarray_test.js @@ -0,0 +1,162 @@ +goog.require('goog.array'); +goog.require('goog.testing.jsunit'); +goog.require('ol.MVCArray'); +goog.require('ol.MVCArrayEventType'); + + +function testEmpty() { + var a = new ol.MVCArray(); + assertEquals(0, a.getLength()); + assertTrue(goog.array.equals(a.getArray(), [])); + assertUndefined(a.getAt(0)); +} + + +function testConstruct() { + var array = [0, 1, 2]; + var a = new ol.MVCArray(array); + assertEquals(0, a.getAt(0)); + assertEquals(1, a.getAt(1)); + assertEquals(2, a.getAt(2)); +} + + +function testPush() { + var a = new ol.MVCArray(); + a.push(1); + assertEquals(1, a.getLength()); + assertTrue(goog.array.equals(a.getArray(), [1])); + assertEquals(1, a.getAt(0)); +} + + +function testPushPop() { + var a = new ol.MVCArray(); + a.push(1); + a.pop(); + assertEquals(0, a.getLength()); + assertTrue(goog.array.equals(a.getArray(), [])); + assertUndefined(a.getAt(0)); +} + + +function testInsertAt() { + var a = new ol.MVCArray([0, 2]); + a.insertAt(1, 1); + assertEquals(0, a.getAt(0)); + assertEquals(1, a.getAt(1)); + assertEquals(2, a.getAt(2)); +} + + +function testSetAt() { + var a = new ol.MVCArray(); + a.setAt(1, 1); + assertEquals(2, a.getLength()); + assertUndefined(a.getAt(0)); + assertEquals(1, a.getAt(1)); +} + + +function testRemoveAt() { + var a = new ol.MVCArray([0, 1, 2]); + a.removeAt(1); + assertEquals(0, a.getAt(0)); + assertEquals(2, a.getAt(1)); +} + + +function testForEachEmpty() { + var a = new ol.MVCArray(); + var forEachCalled = false; + a.forEach(function() { + forEachCalled = true; + }); + assertFalse(forEachCalled); +} + + +function testForEachPopulated() { + var a = new ol.MVCArray(); + a.push(1); + a.push(2); + var forEachCount = 0; + a.forEach(function() { + ++forEachCount; + }); + assertEquals(2, forEachCount); +} + + +function testSetAtEvent() { + var a = new ol.MVCArray(['a', 'b']); + var index, prev; + goog.events.listen(a, ol.MVCArrayEventType.SET_AT, function(e) { + index = e.index; + prev = e.prev; + }); + a.setAt(1, 1); + assertEquals(1, index); + assertEquals('b', prev); +} + + +function testRemoveAtEvent() { + var a = new ol.MVCArray(['a']); + var index, prev; + goog.events.listen(a, ol.MVCArrayEventType.REMOVE_AT, function(e) { + index = e.index; + prev = e.prev; + }); + a.pop(); + assertEquals(0, index); + assertEquals('a', prev); +} + + +function testInsertAtEvent() { + var a = new ol.MVCArray([0, 2]); + var index; + goog.events.listen(a, ol.MVCArrayEventType.INSERT_AT, function(e) { + index = e.index; + }); + a.insertAt(1, 1); + assertEquals(1, index); +} + + +function testSetAtBeyondEnd() { + var a = new ol.MVCArray(); + var inserts = []; + a.insert_at = function(index) { + inserts.push(index); + }; + a.setAt(2, 0); + assertEquals(3, a.getLength()); + assertUndefined(a.getAt(0)); + assertUndefined(a.getAt(1)); + assertEquals(0, a.getAt(2)); + assertEquals(3, inserts.length); + assertEquals(0, inserts[0]); + assertEquals(1, inserts[1]); + assertEquals(2, inserts[2]); +} + + +function testCreateFromArray() { + var a = [0, 1, 2]; + var mvcArray = ol.MVCArray.create(a); + assertTrue(mvcArray instanceof ol.MVCArray); + assertEquals(3, mvcArray.getLength()); + assertEquals(0, mvcArray.getAt(0)); + assertEquals(1, mvcArray.getAt(1)); + assertEquals(2, mvcArray.getAt(2)); +} + + +function testCreateFromMVCArray() { + var mvcArray1 = new ol.MVCArray(); + var mvcArray2 = ol.MVCArray.create(mvcArray1); + assertTrue(mvcArray1 === mvcArray2); + +}