diff --git a/build.py b/build.py index 80441a6121..ed7b7a4b14 100755 --- a/build.py +++ b/build.py @@ -270,6 +270,7 @@ def examples_star_json(name, match): "exports": [], "src": [ "src/**/*.js", + "build/ol.ext/*.js", "examples/%(id)s.js" % match.groupdict()], "compile": { "js": [ diff --git a/config/examples-all.json b/config/examples-all.json index b3983b9673..f5b300e3ca 100644 --- a/config/examples-all.json +++ b/config/examples-all.json @@ -2,6 +2,7 @@ "exports": [], "src": [ "src/**/*.js", + "build/ol.ext/*.js", "build/examples/all.js" ], "compile": { diff --git a/package.json b/package.json index 93174fe4c4..33c143b78f 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ ], "homepage": "http://openlayers.org/", "scripts": { - "install": "node tasks/parse-examples.js", + "install": "node tasks/install.js", "postinstall": "closure-util update", "start": "node tasks/serve.js", "test": "node tasks/test.js" @@ -30,6 +30,7 @@ "htmlparser2": "3.7.3", "jsdoc": "3.3.0-alpha9", "nomnom": "1.8.0", + "rbush": "1.3.4", "temp": "0.8.1", "walk": "2.3.4" }, @@ -43,5 +44,8 @@ "phantomjs": "1.9.10", "proj4": "2.3.3", "sinon": "1.10.3" - } + }, + "ext": [ + "rbush" + ] } diff --git a/src/ol/structs/rbush.js b/src/ol/structs/rbush.js index 61ffd549bb..e7092bc516 100644 --- a/src/ol/structs/rbush.js +++ b/src/ol/structs/rbush.js @@ -1,199 +1,17 @@ -// Based on rbush https://github.com/mourner/rbush -// Copyright (c) 2013 Vladimir Agafonkin -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - // FIXME bulk inserts -// FIXME is level argument needed to insert_? goog.provide('ol.structs.RBush'); goog.require('goog.array'); goog.require('goog.asserts'); goog.require('goog.object'); -goog.require('ol.extent'); - - - -/** - * @constructor - * @param {ol.Extent} extent Extent. - * @param {number} height Height. - * @param {Array.>} children Children. - * @param {?T} value Value. - * @struct - * @template T - */ -ol.structs.RBushNode = function(extent, height, children, value) { - - if (height === 0) { - goog.asserts.assert(goog.isNull(children)); - goog.asserts.assert(!goog.isNull(value)); - } else { - goog.asserts.assert(!goog.isNull(children)); - goog.asserts.assert(goog.isNull(value)); - } - - /** - * @type {ol.Extent} - */ - this.extent = extent; - - /** - * @type {number} - */ - this.height = height; - - /** - * @type {Array.>} - */ - this.children = children; - - /** - * @type {?T} - */ - this.value = value; - -}; - - -/** - * @param {ol.structs.RBushNode.} node1 Node 1. - * @param {ol.structs.RBushNode.} node2 Node 2. - * @return {number} Compare minimum X. - * @template T - */ -ol.structs.RBushNode.compareMinX = function(node1, node2) { - return node1.extent[0] - node2.extent[0]; -}; - - -/** - * @param {ol.structs.RBushNode.} node1 Node 1. - * @param {ol.structs.RBushNode.} node2 Node 2. - * @return {number} Compare minimum Y. - * @template T - */ -ol.structs.RBushNode.compareMinY = function(node1, node2) { - return node1.extent[1] - node2.extent[1]; -}; - - -/** - * @param {number} maxEntries Max entries. - */ -ol.structs.RBushNode.prototype.assertValid = function(maxEntries) { - if (this.height === 0) { - goog.asserts.assert(goog.isNull(this.children)); - goog.asserts.assert(!goog.isNull(this.value)); - } else { - goog.asserts.assert(!goog.isNull(this.children)); - goog.asserts.assert(goog.isNull(this.value)); - goog.asserts.assert(this.children.length <= maxEntries); - var i, ii; - for (i = 0, ii = this.children.length; i < ii; ++i) { - var child = this.children[i]; - goog.asserts.assert(ol.extent.containsExtent(this.extent, child.extent)); - child.assertValid(maxEntries); - } - } -}; - - -/** - * @param {number} start Start. - * @param {number} stop Stop. - * @param {ol.Extent=} opt_extent Extent. - * @return {ol.Extent} Extent. - */ -ol.structs.RBushNode.prototype.getChildrenExtent = - function(start, stop, opt_extent) { - goog.asserts.assert(!this.isLeaf()); - var children = this.children; - var extent = ol.extent.createOrUpdateEmpty(opt_extent); - var i; - for (i = start; i < stop; ++i) { - ol.extent.extend(extent, children[i].extent); - } - return extent; -}; - - -/** - * @param {ol.Extent} extent Extent. - * @param {T} value Value. - * @param {Array.>} path Path. - * @return {boolean} Removed. - */ -ol.structs.RBushNode.prototype.remove = function(extent, value, path) { - var children = this.children; - var ii = children.length; - var child, i; - if (this.height == 1) { - for (i = 0; i < ii; ++i) { - child = children[i]; - if (child.value === value) { - goog.array.removeAt(children, i); - return true; - } - } - } else { - goog.asserts.assert(this.height > 1); - for (i = 0; i < ii; ++i) { - child = children[i]; - if (ol.extent.containsExtent(child.extent, extent)) { - path.push(child); - if (child.remove(extent, value, path)) { - return true; - } - path.pop(); - } - } - } - return false; -}; - - -/** - * FIXME empty description for jsdoc - */ -ol.structs.RBushNode.prototype.updateExtent = function() { - goog.asserts.assert(!this.isLeaf()); - var extent = ol.extent.createOrUpdateEmpty(this.extent); - var children = this.children; - var i, ii; - for (i = 0, ii = children.length; i < ii; ++i) { - ol.extent.extend(extent, children[i].extent); - } -}; - - -/** - * @return {boolean} Is leaf. - */ -ol.structs.RBushNode.prototype.isLeaf = function() { - return goog.isNull(this.children); -}; +goog.require('ol.ext.rbush'); /** + * Wrapper around the RBush by Vladimir Agafonkin. + * * @constructor * @param {number=} opt_maxEntries Max entries. * @see https://github.com/mourner/rbush @@ -204,28 +22,16 @@ ol.structs.RBush = function(opt_maxEntries) { /** * @private - * @type {number} */ - this.maxEntries_ = - Math.max(4, goog.isDef(opt_maxEntries) ? opt_maxEntries : 9); + this.rbush_ = ol.ext.rbush(opt_maxEntries); /** + * A mapping between the objects added to this rbush wrapper + * and the objects that are actually added to the internal rbush. * @private - * @type {number} + * @type {Object.} */ - this.minEntries_ = Math.max(2, Math.ceil(0.4 * this.maxEntries_)); - - /** - * @private - * @type {ol.structs.RBushNode.} - */ - this.root_ = new ol.structs.RBushNode(ol.extent.createEmpty(), 1, [], null); - - /** - * @private - * @type {Object.} - */ - this.valueExtent_ = {}; + this.items_ = {}; if (goog.DEBUG) { /** @@ -234,167 +40,89 @@ ol.structs.RBush = function(opt_maxEntries) { */ this.readers_ = 0; } - -}; - - -/** - * @param {ol.structs.RBushNode.} node Node. - * @param {function(ol.structs.RBushNode., ol.structs.RBushNode.): number} - * compare Compare. - * @private - * @return {number} All distance margin. - */ -ol.structs.RBush.prototype.allDistMargin_ = function(node, compare) { - var children = node.children; - var m = this.minEntries_; - var M = children.length; - var i; - goog.array.sort(children, compare); - var leftExtent = node.getChildrenExtent(0, m); - var rightExtent = node.getChildrenExtent(M - m, M); - var margin = - ol.extent.getMargin(leftExtent) + ol.extent.getMargin(rightExtent); - for (i = m; i < M - m; ++i) { - ol.extent.extend(leftExtent, children[i].extent); - margin += ol.extent.getMargin(leftExtent); - } - for (i = M - m - 1; i >= m; --i) { - ol.extent.extend(rightExtent, children[i].extent); - margin += ol.extent.getMargin(rightExtent); - } - return margin; -}; - - -/** - * FIXME empty description for jsdoc - */ -ol.structs.RBush.prototype.assertValid = function() { - this.root_.assertValid(this.maxEntries_); -}; - - -/** - * @param {ol.structs.RBushNode.} node Node. - * @private - */ -ol.structs.RBush.prototype.chooseSplitAxis_ = function(node) { - var xMargin = this.allDistMargin_(node, ol.structs.RBushNode.compareMinX); - var yMargin = this.allDistMargin_(node, ol.structs.RBushNode.compareMinY); - if (xMargin < yMargin) { - goog.array.sort(node.children, ol.structs.RBushNode.compareMinX); - } -}; - - -/** - * @param {ol.structs.RBushNode.} node Node. - * @private - * @return {number} Split index. - */ -ol.structs.RBush.prototype.chooseSplitIndex_ = function(node) { - var children = node.children; - var m = this.minEntries_; - var M = children.length; - var minOverlap = Infinity; - var minArea = Infinity; - var extent1 = ol.extent.createEmpty(); - var extent2 = ol.extent.createEmpty(); - var index = 0; - var i; - for (i = m; i <= M - m; ++i) { - extent1 = node.getChildrenExtent(0, i, extent1); - extent2 = node.getChildrenExtent(i, M, extent2); - var overlap = ol.extent.getIntersectionArea(extent1, extent2); - var area = ol.extent.getArea(extent1) + ol.extent.getArea(extent2); - if (overlap < minOverlap) { - minOverlap = overlap; - minArea = Math.min(area, minArea); - index = i; - } else if (overlap == minOverlap && area < minArea) { - minArea = area; - index = i; - } - } - return index; }; /** + * Insert a value into the RBush. * @param {ol.Extent} extent Extent. - * @param {ol.structs.RBushNode.} node Node. - * @param {number} level Level. - * @param {Array.>} path Path. - * @private - * @return {ol.structs.RBushNode.} Node. + * @param {T} value Value. */ -ol.structs.RBush.prototype.chooseSubtree_ = - function(extent, node, level, path) { - while (!node.isLeaf() && path.length - 1 != level) { - var minArea = Infinity; - var minEnlargement = Infinity; - var children = node.children; - var bestChild = null; - var i, ii; - for (i = 0, ii = children.length; i < ii; ++i) { - var child = children[i]; - var area = ol.extent.getArea(child.extent); - var enlargement = ol.extent.getEnlargedArea(child.extent, extent) - area; - if (enlargement < minEnlargement) { - minEnlargement = enlargement; - minArea = Math.min(area, minArea); - bestChild = child; - } else if (enlargement == minEnlargement && area < minArea) { - minArea = area; - bestChild = child; - } - } - goog.asserts.assert(!goog.isNull(bestChild)); - node = bestChild; - path.push(node); +ol.structs.RBush.prototype.insert = function(extent, value) { + if (goog.DEBUG && this.readers_) { + throw new Error('Can not insert value while reading'); } - return node; + var item = [ + extent[0], + extent[1], + extent[2], + extent[3], + value + ]; + this.rbush_.insert(item); + // remember the object that was added to the internal rbush + goog.object.add(this.items_, goog.getUid(value).toString(), item); }; /** - * FIXME empty description for jsdoc + * Remove a value from the RBush. + * @param {T} value Value. + * @return {boolean} Removed. */ -ol.structs.RBush.prototype.clear = function() { - var node = this.root_; - node.extent = ol.extent.createOrUpdateEmpty(this.root_.extent); - node.height = 1; - node.children.length = 0; - node.value = null; - goog.object.clear(this.valueExtent_); -}; - - -/** - * @param {Array.>} path Path. - * @private - */ -ol.structs.RBush.prototype.condense_ = function(path) { - var i; - for (i = path.length - 1; i >= 0; --i) { - var node = path[i]; - if (node.children.length === 0) { - if (i > 0) { - goog.array.remove(path[i - 1].children, node); - } else { - this.clear(); - } - } else { - node.updateExtent(); - } +ol.structs.RBush.prototype.remove = function(value) { + if (goog.DEBUG && this.readers_) { + throw new Error('Can not remove value while reading'); } + var uid = goog.getUid(value).toString(); + goog.asserts.assert(goog.object.containsKey(this.items_, uid)); + + // get the object in which the value was wrapped when adding to the + // internal rbush. then use that object to do the removal. + var item = goog.object.get(this.items_, uid); + goog.object.remove(this.items_, uid); + return this.rbush_.remove(item) !== null; }; /** - * Calls a callback function with each node in the tree. Inside the callback, - * no tree modifications (insert, update, remove) can be made. + * Update the extent of a value in the RBush. + * @param {ol.Extent} extent Extent. + * @param {T} value Value. + */ +ol.structs.RBush.prototype.update = function(extent, value) { + this.remove(value); + this.insert(extent, value); +}; + + +/** + * Return all values in the RBush. + * @return {Array.} All. + */ +ol.structs.RBush.prototype.getAll = function() { + var items = this.rbush_.all(); + return goog.array.map(items, function(item) { + return item[4]; + }); +}; + + +/** + * Return all values in the given extent. + * @param {ol.Extent} extent Extent. + * @return {Array.} All in extent. + */ +ol.structs.RBush.prototype.getInExtent = function(extent) { + var items = this.rbush_.search(extent); + return goog.array.map(items, function(item) { + return item[4]; + }); +}; + + +/** + * Calls a callback function with each value in the tree. * If the callback returns a truthy value, this value is returned without * checking the rest of the tree. * @param {function(this: S, T): *} callback Callback. @@ -406,49 +134,18 @@ ol.structs.RBush.prototype.forEach = function(callback, opt_this) { if (goog.DEBUG) { ++this.readers_; try { - return this.forEach_(this.root_, callback, opt_this); + return this.forEach_(this.getAll(), callback, opt_this); } finally { --this.readers_; } } else { - return this.forEach_(this.root_, callback, opt_this); + return this.forEach_(this.getAll(), callback, opt_this); } }; /** - * @param {ol.structs.RBushNode.} node Node. - * @param {function(this: S, T): *} callback Callback. - * @param {S=} opt_this The object to use as `this` in `callback`. - * @private - * @return {*} Callback return value. - * @template S - */ -ol.structs.RBush.prototype.forEach_ = function(node, callback, opt_this) { - goog.asserts.assert(!node.isLeaf()); - /** @type {Array.>} */ - var toVisit = [node]; - var children, i, ii, result; - while (toVisit.length > 0) { - node = toVisit.pop(); - children = node.children; - if (node.height == 1) { - for (i = 0, ii = children.length; i < ii; ++i) { - result = callback.call(opt_this, children[i].value); - if (result) { - return result; - } - } - } else { - toVisit.push.apply(toVisit, children); - } - } -}; - - -/** - * Calls a callback function with each node in the provided extent. Inside the - * callback, no tree modifications (insert, update, remove) can be made. + * Calls a callback function with each value in the provided extent. * @param {ol.Extent} extent Extent. * @param {function(this: S, T): *} callback Callback. * @param {S=} opt_this The object to use as `this` in `callback`. @@ -460,81 +157,50 @@ ol.structs.RBush.prototype.forEachInExtent = if (goog.DEBUG) { ++this.readers_; try { - return this.forEachInExtent_(extent, callback, opt_this); + return this.forEach_(this.getInExtent(extent), callback, opt_this); } finally { --this.readers_; } } else { - return this.forEachInExtent_(extent, callback, opt_this); + return this.forEach_(this.getInExtent(extent), callback, opt_this); } }; /** - * @param {ol.Extent} extent Extent. + * @param {Array.} values Values. * @param {function(this: S, T): *} callback Callback. * @param {S=} opt_this The object to use as `this` in `callback`. * @private * @return {*} Callback return value. * @template S */ -ol.structs.RBush.prototype.forEachInExtent_ = - function(extent, callback, opt_this) { - /** @type {Array.>} */ - var toVisit = [this.root_]; +ol.structs.RBush.prototype.forEach_ = function(values, callback, opt_this) { var result; - while (toVisit.length > 0) { - var node = toVisit.pop(); - if (ol.extent.intersects(extent, node.extent)) { - if (node.isLeaf()) { - result = callback.call(opt_this, node.value); - if (result) { - return result; - } - } else if (ol.extent.containsExtent(extent, node.extent)) { - result = this.forEach_(node, callback, opt_this); - if (result) { - return result; - } - } else { - toVisit.push.apply(toVisit, node.children); - } + for (var i = 0, l = values.length; i < l; i++) { + result = callback.call(opt_this, values[i]); + if (result) { + return result; } } - return undefined; + return result; }; /** - * @return {Array.} All. + * @return {boolean} Is empty. */ -ol.structs.RBush.prototype.getAll = function() { - var values = []; - this.forEach( - /** - * @param {T} value Value. - */ - function(value) { - values.push(value); - }); - return values; +ol.structs.RBush.prototype.isEmpty = function() { + return goog.object.isEmpty(this.items_); }; /** - * @param {ol.Extent} extent Extent. - * @return {Array.} All in extent. + * Remove all values from the RBush. */ -ol.structs.RBush.prototype.getInExtent = function(extent) { - var values = []; - this.forEachInExtent(extent, - /** - * @param {T} value Value. - */ - function(value) { - values.push(value); - }); - return values; +ol.structs.RBush.prototype.clear = function() { + this.rbush_.clear(); + goog.object.clear(this.items_); }; @@ -543,160 +209,6 @@ ol.structs.RBush.prototype.getInExtent = function(extent) { * @return {ol.Extent} Extent. */ ol.structs.RBush.prototype.getExtent = function(opt_extent) { - return ol.extent.returnOrUpdate(this.root_.extent, opt_extent); -}; - - -/** - * @param {T} value Value. - * @private - * @return {string} Key. - */ -ol.structs.RBush.prototype.getKey_ = function(value) { - goog.asserts.assert(goog.isObject(value)); - return goog.getUid(value).toString(); -}; - - -/** - * @param {ol.Extent} extent Extent. - * @param {T} value Value. - */ -ol.structs.RBush.prototype.insert = function(extent, value) { - if (goog.DEBUG && this.readers_) { - throw new Error('cannot insert value while reading'); - } - var key = this.getKey_(value); - goog.asserts.assert(!this.valueExtent_.hasOwnProperty(key)); - this.insert_(extent, value, this.root_.height - 1); - this.valueExtent_[key] = ol.extent.clone(extent); -}; - - -/** - * @param {ol.Extent} extent Extent. - * @param {T} value Value. - * @param {number} level Level. - * @private - * @return {ol.structs.RBushNode.} Node. - */ -ol.structs.RBush.prototype.insert_ = function(extent, value, level) { - /** @type {Array.>} */ - var path = [this.root_]; - var node = this.chooseSubtree_(extent, this.root_, level, path); - node.children.push(new ol.structs.RBushNode(extent, 0, null, value)); - ol.extent.extend(node.extent, extent); - var i; - for (i = path.length - 1; i >= 0; --i) { - if (path[i].children.length > this.maxEntries_) { - this.split_(path, i); - } else { - break; - } - } - for (; i >= 0; --i) { - ol.extent.extend(path[i].extent, extent); - } - return node; -}; - - -/** - * @return {boolean} Is empty. - */ -ol.structs.RBush.prototype.isEmpty = function() { - return this.root_.children.length === 0; -}; - - -/** - * @param {T} value Value. - * @return {boolean} Removed. - */ -ol.structs.RBush.prototype.remove = function(value) { - if (goog.DEBUG && this.readers_) { - throw new Error('cannot remove value while reading'); - } - var key = this.getKey_(value); - goog.asserts.assert(this.valueExtent_.hasOwnProperty(key)); - var extent = this.valueExtent_[key]; - delete this.valueExtent_[key]; - return this.remove_(extent, value); -}; - - -/** - * @param {ol.Extent} extent Extent. - * @param {T} value Value. - * @private - * @return {boolean} Removed. - */ -ol.structs.RBush.prototype.remove_ = function(extent, value) { - var root = this.root_; - var path = [root]; - var removed = root.remove(extent, value, path); - if (removed) { - this.condense_(path); - } else { - goog.asserts.assert(path.length == 1); - goog.asserts.assert(path[0] === root); - } - return removed; -}; - - -/** - * @param {Array.>} path Path. - * @param {number} level Level. - * @private - */ -ol.structs.RBush.prototype.split_ = function(path, level) { - var node = path[level]; - this.chooseSplitAxis_(node); - var splitIndex = this.chooseSplitIndex_(node); - // FIXME too few arguments to splice here - var newChildren = node.children.splice(splitIndex); - var newNode = new ol.structs.RBushNode( - ol.extent.createEmpty(), node.height, newChildren, null); - node.updateExtent(); - newNode.updateExtent(); - if (level) { - path[level - 1].children.push(newNode); - } else { - this.splitRoot_(node, newNode); - } -}; - - -/** - * @param {ol.structs.RBushNode.} node1 Node 1. - * @param {ol.structs.RBushNode.} node2 Node 2. - * @private - */ -ol.structs.RBush.prototype.splitRoot_ = function(node1, node2) { - goog.asserts.assert(node1 === this.root_); - var height = node1.height + 1; - var extent = ol.extent.extend(node1.extent.slice(), node2.extent); - var children = [node1, node2]; - this.root_ = new ol.structs.RBushNode(extent, height, children, null); -}; - - -/** - * @param {ol.Extent} extent Extent. - * @param {T} value Value. - */ -ol.structs.RBush.prototype.update = function(extent, value) { - var key = this.getKey_(value); - var currentExtent = this.valueExtent_[key]; - goog.asserts.assert(goog.isDef(currentExtent)); - if (!ol.extent.equals(currentExtent, extent)) { - if (goog.DEBUG && this.readers_) { - throw new Error('cannot update extent while reading'); - } - var removed = this.remove_(currentExtent, value); - goog.asserts.assert(removed); - this.insert_(extent, value, this.root_.height - 1); - this.valueExtent_[key] = ol.extent.clone(extent, currentExtent); - } + // FIXME add getExtent() to rbush + return this.rbush_.data.bbox; }; diff --git a/tasks/build-ext.js b/tasks/build-ext.js new file mode 100644 index 0000000000..d7b4ae9f2e --- /dev/null +++ b/tasks/build-ext.js @@ -0,0 +1,97 @@ +var fs = require('fs'); +var path = require('path'); + +var async = require('async'); +var fse = require('fs-extra'); + +var pkg = require('../package.json'); + +var root = path.join(__dirname, '..'); +var buildDir = path.join(root, 'build', 'ol.ext'); + + +/** + * Get external module metadata. + * @return {Array.} Array of objects representing external modules. + */ +function getExternalModules() { + return pkg.ext.map(function(name) { + return { + name: name, + main: require.resolve(name) + }; + }); +} + + +/** + * Wrap a CommonJS module in Closure Library accessible code. + * @param {Object} mod Module metadata. + * @param {function(Error, string)} callback Called with any error and the + * wrapped module. + */ +function wrapModule(mod, callback) { + fs.readFile(mod.main, function(err, data) { + if (err) { + callback(err); + return; + } + var wrapped = 'goog.provide(\'ol.ext.' + mod.name + '\');\n' + + '/** @typedef {function(*)} */\n' + + 'ol.ext.' + mod.name + ';\n' + + '(function() {\n' + + 'var exports = {};\n' + + 'var module = {exports: exports};\n' + + '/**\n' + + ' * @fileoverview\n' + + ' * @suppress {accessControls, ambiguousFunctionDecl, ' + + 'checkDebuggerStatement, checkRegExp, checkTypes, checkVars, const, ' + + 'constantProperty, deprecated, duplicate, es5Strict, ' + + 'fileoverviewTags, missingProperties, nonStandardJsDocs, ' + + 'strictModuleDepCheck, suspiciousCode, undefinedNames, ' + + 'undefinedVars, unknownDefines, uselessCode, visibility}\n' + + ' */\n' + data.toString() + '\n' + + 'ol.ext.' + mod.name + ' = module.exports;\n' + + '})();\n'; + callback(null, wrapped); + }); +} + + +/** + * Build external modules. + * @param {Array.} modules External modules. + * @param {function(Error)} callback Called with any error. + */ +function buildModules(modules, callback) { + async.each(modules, function(mod, done) { + var output = path.join(buildDir, mod.name) + '.js'; + async.waterfall([ + wrapModule.bind(null, mod), + fse.outputFile.bind(fse, output) + ], done); + }, callback); +} + + +/** + * Build all external modules. + * @param {function(Error)} callback Called with any error. + */ +function main(callback) { + var modules = getExternalModules(); + buildModules(modules, callback); +} + +if (require.main === module) { + main(function(err) { + if (err) { + process.stderr.write(err.message + '\n'); + process.exit(1); + } else { + process.exit(0); + } + }); +} + +module.exports = main; diff --git a/tasks/build.js b/tasks/build.js index dd07d343cf..3f4fe2e2cf 100644 --- a/tasks/build.js +++ b/tasks/build.js @@ -127,7 +127,7 @@ function getDependencies(config, exports, callback) { }; } else { options = { - lib: ['src/**/*.js'], + lib: ['src/**/*.js', 'build/ol.ext/*.js'], cwd: root }; } diff --git a/tasks/install.js b/tasks/install.js new file mode 100644 index 0000000000..52cce80f8f --- /dev/null +++ b/tasks/install.js @@ -0,0 +1,17 @@ +var async = require('async'); + +var buildExt = require('./build-ext'); +var parseExamples = require('./parse-examples'); + +/** + * Parse examples and build external modules. + */ +async.waterfall([ + parseExamples, + buildExt +], function(err) { + if (err) { + process.stderr.write(err + '\n'); + process.exit(1); + } +}); diff --git a/tasks/parse-examples.js b/tasks/parse-examples.js index 09e86c1b64..8315da6716 100644 --- a/tasks/parse-examples.js +++ b/tasks/parse-examples.js @@ -137,14 +137,25 @@ function writeExampleList(exampleInfos, callback) { /** * List examples, parse them, and write example list. + * @param {function(Error)} callback Called with any error. */ -async.waterfall([ - listExamples, - parseExamples, - writeExampleList -], function(err) { - if (err) { - process.stderr.write(err + '\n'); - process.exit(1); - } -}); +function main(callback) { + async.waterfall([ + listExamples, + parseExamples, + writeExampleList + ], callback); +} + +if (require.main === module) { + main(function(err) { + if (err) { + process.stderr.write(err.message + '\n'); + process.exit(1); + } else { + process.exit(0); + } + }); +} + +module.exports = main; diff --git a/tasks/serve.js b/tasks/serve.js index 219ce69a5f..a7f792d51b 100644 --- a/tasks/serve.js +++ b/tasks/serve.js @@ -22,6 +22,7 @@ var createServer = exports.createServer = function(callback) { var manager = new closure.Manager({ lib: [ 'src/**/*.js', + 'build/ol.ext/*.js', 'test/spec/**/*.test.js' ], main: 'examples/*.js'