Showing preview only (1,285K chars total). Download the full file or copy to clipboard to get everything.
Repository: giscafer/mapshaper-plus
Branch: master
Commit: 49981ceefa0a
Files: 14
Total size: 1.1 MB
Directory structure:
gitextract_hdn3tnup/
├── README.md
├── codecs.js
├── deflate.js
├── elements.css
├── encode.js
├── index.html
├── manifest.js
├── mapshaper-gui.js
├── mapshaper.js
├── page.css
├── pako.deflate.js
├── pako.inflate.js
├── z-worker.js
└── zip.js
================================================
FILE CONTENTS
================================================
================================================
FILE: README.md
================================================
# mapshaper-plus
Generate geojson files for [Apache ECharts (incubating)](https://github.com/apache/incubator-echarts) Map,base on mapshaper
基于[mapshaper](https://github.com/mbloch/mapshaper)对geojson地图数据的坐标信息进行压缩编码,并提供可直接生成压缩编码后的echarts map数据格式
通过`mapshaper-plus`可以直接将`shp`格式数据转换为压缩后的echarts数据
## Demo
https://giscafer.github.io/mapshaper-plus/
## Description
**介绍** ——[mapshaper](https://github.com/mbloch/mapshaper)可以将多种数据格式(Shapefile, GeoJSON, TopoJSON
和 Zip files)导入后,对地图的编辑和导出(Shapefile, GeoJSON, TopoJSON, DSV, SVG),功能强大和简单易用。
`mapshaper-plus`是在`mapshaper`基础上拓展对地图坐标信息的压缩编码,很大程度上减小了文件的代码行数和字节大小:譬如一个贵州省的数据,原始的`geojson`数据会在`30M`左右,但在对坐标信息压缩编码后,仅为`1.4M`。
**背景** ——在做echarts图表统计时,需要自制地图数据,但官方没有提供一个平台可以直接将`shp文件`转化为压缩后的`json`或`js`格式的地图文件,而`mapshaper`导出的json数据没有压缩,数据量过大。
使用可以访问[mapshaper-plus在线demo](http://giscafer.github.io/mapshaper-plus/)
## Screenshot

## License
mapshaper is licensed under MPL 2.0. and mapshaper-plus is licensed under MIT.
> Blog [giscafer.com](http://giscafer.com) ·
> GitHub [@giscafer](https://github.com/giscafer) ·
> Weibo [@Nickbing Lao](https://weibo.com/laohoubin)
================================================
FILE: codecs.js
================================================
/// wrapper for pako (https://github.com/nodeca/pako)
/* globals pako */
(function(global) {
"use strict";
function Codec(isDeflater, options) {
var newOptions = { raw: true, chunkSize: 1024 * 1024 };
if (options && typeof options.level === 'number')
newOptions.level = options.level;
this._backEnd = isDeflater?
new pako.Deflate(newOptions) :
new pako.Inflate(newOptions);
this._chunks = [];
this._dataLength = 0;
this._backEnd.onData = this._onData.bind(this);
}
Codec.prototype._onData = function _onData(chunk) {
this._chunks.push(chunk);
this._dataLength += chunk.length;
};
Codec.prototype._fetchData = function _fetchData() {
var be = this._backEnd;
if (be.err !== 0)
throw new Error(be.msg);
var chunks = this._chunks;
var data;
if (chunks.length === 1)
data = chunks[0];
else if (chunks.length > 1) {
data = new Uint8Array(this._dataLength);
for (var i = 0, n = chunks.length, off = 0; i < n; i++) {
var chunk = chunks[i];
data.set(chunk, off);
off += chunk.length;
}
}
chunks.length = 0;
this._dataLength = 0;
return data;
};
Codec.prototype.append = function append(bytes, onprogress) {
this._backEnd.push(bytes, false);
return this._fetchData();
};
Codec.prototype.flush = function flush() {
this._backEnd.push(new Uint8Array(0), true);
return this._fetchData();
};
function Deflater(options) {
Codec.call(this, true, options);
}
Deflater.prototype = Object.create(Codec.prototype);
function Inflater() {
Codec.call(this, false);
}
Inflater.prototype = Object.create(Codec.prototype);
// 'zip' may not be defined in z-worker and some tests
var env = global.zip || global;
env.Deflater = env._pako_Deflater = Deflater;
env.Inflater = env._pako_Inflater = Inflater;
})(this);
================================================
FILE: deflate.js
================================================
/*
Copyright (c) 2013 Gildas Lormeau. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
3. The names of the authors may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* This program is based on JZlib 1.0.2 ymnk, JCraft,Inc.
* JZlib is based on zlib-1.1.3, so all credit should go authors
* Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
* and contributors of zlib.
*/
(function(global) {
"use strict";
// Global
var MAX_BITS = 15;
var D_CODES = 30;
var BL_CODES = 19;
var LENGTH_CODES = 29;
var LITERALS = 256;
var L_CODES = (LITERALS + 1 + LENGTH_CODES);
var HEAP_SIZE = (2 * L_CODES + 1);
var END_BLOCK = 256;
// Bit length codes must not exceed MAX_BL_BITS bits
var MAX_BL_BITS = 7;
// repeat previous bit length 3-6 times (2 bits of repeat count)
var REP_3_6 = 16;
// repeat a zero length 3-10 times (3 bits of repeat count)
var REPZ_3_10 = 17;
// repeat a zero length 11-138 times (7 bits of repeat count)
var REPZ_11_138 = 18;
// The lengths of the bit length codes are sent in order of decreasing
// probability, to avoid transmitting the lengths for unused bit
// length codes.
var Buf_size = 8 * 2;
// JZlib version : "1.0.2"
var Z_DEFAULT_COMPRESSION = -1;
// compression strategy
var Z_FILTERED = 1;
var Z_HUFFMAN_ONLY = 2;
var Z_DEFAULT_STRATEGY = 0;
var Z_NO_FLUSH = 0;
var Z_PARTIAL_FLUSH = 1;
var Z_FULL_FLUSH = 3;
var Z_FINISH = 4;
var Z_OK = 0;
var Z_STREAM_END = 1;
var Z_NEED_DICT = 2;
var Z_STREAM_ERROR = -2;
var Z_DATA_ERROR = -3;
var Z_BUF_ERROR = -5;
// Tree
// see definition of array dist_code below
var _dist_code = [ 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, 18, 18, 19, 19,
20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 29,
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 ];
function Tree() {
var that = this;
// dyn_tree; // the dynamic tree
// max_code; // largest code with non zero frequency
// stat_desc; // the corresponding static tree
// Compute the optimal bit lengths for a tree and update the total bit
// length
// for the current block.
// IN assertion: the fields freq and dad are set, heap[heap_max] and
// above are the tree nodes sorted by increasing frequency.
// OUT assertions: the field len is set to the optimal bit length, the
// array bl_count contains the frequencies for each bit length.
// The length opt_len is updated; static_len is also updated if stree is
// not null.
function gen_bitlen(s) {
var tree = that.dyn_tree;
var stree = that.stat_desc.static_tree;
var extra = that.stat_desc.extra_bits;
var base = that.stat_desc.extra_base;
var max_length = that.stat_desc.max_length;
var h; // heap index
var n, m; // iterate over the tree elements
var bits; // bit length
var xbits; // extra bits
var f; // frequency
var overflow = 0; // number of elements with bit length too large
for (bits = 0; bits <= MAX_BITS; bits++)
s.bl_count[bits] = 0;
// In a first pass, compute the optimal bit lengths (which may
// overflow in the case of the bit length tree).
tree[s.heap[s.heap_max] * 2 + 1] = 0; // root of the heap
for (h = s.heap_max + 1; h < HEAP_SIZE; h++) {
n = s.heap[h];
bits = tree[tree[n * 2 + 1] * 2 + 1] + 1;
if (bits > max_length) {
bits = max_length;
overflow++;
}
tree[n * 2 + 1] = bits;
// We overwrite tree[n*2+1] which is no longer needed
if (n > that.max_code)
continue; // not a leaf node
s.bl_count[bits]++;
xbits = 0;
if (n >= base)
xbits = extra[n - base];
f = tree[n * 2];
s.opt_len += f * (bits + xbits);
if (stree)
s.static_len += f * (stree[n * 2 + 1] + xbits);
}
if (overflow === 0)
return;
// This happens for example on obj2 and pic of the Calgary corpus
// Find the first bit length which could increase:
do {
bits = max_length - 1;
while (s.bl_count[bits] === 0)
bits--;
s.bl_count[bits]--; // move one leaf down the tree
s.bl_count[bits + 1] += 2; // move one overflow item as its brother
s.bl_count[max_length]--;
// The brother of the overflow item also moves one step up,
// but this does not affect bl_count[max_length]
overflow -= 2;
} while (overflow > 0);
for (bits = max_length; bits !== 0; bits--) {
n = s.bl_count[bits];
while (n !== 0) {
m = s.heap[--h];
if (m > that.max_code)
continue;
if (tree[m * 2 + 1] != bits) {
s.opt_len += (bits - tree[m * 2 + 1]) * tree[m * 2];
tree[m * 2 + 1] = bits;
}
n--;
}
}
}
// Reverse the first len bits of a code, using straightforward code (a
// faster
// method would use a table)
// IN assertion: 1 <= len <= 15
function bi_reverse(code, // the value to invert
len // its bit length
) {
var res = 0;
do {
res |= code & 1;
code >>>= 1;
res <<= 1;
} while (--len > 0);
return res >>> 1;
}
// Generate the codes for a given tree and bit counts (which need not be
// optimal).
// IN assertion: the array bl_count contains the bit length statistics for
// the given tree and the field len is set for all tree elements.
// OUT assertion: the field code is set for all tree elements of non
// zero code length.
function gen_codes(tree, // the tree to decorate
max_code, // largest code with non zero frequency
bl_count // number of codes at each bit length
) {
var next_code = []; // next code value for each
// bit length
var code = 0; // running code value
var bits; // bit index
var n; // code index
var len;
// The distribution counts are first used to generate the code values
// without bit reversal.
for (bits = 1; bits <= MAX_BITS; bits++) {
next_code[bits] = code = ((code + bl_count[bits - 1]) << 1);
}
// Check that the bit counts in bl_count are consistent. The last code
// must be all ones.
// Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1,
// "inconsistent bit counts");
// Tracev((stderr,"\ngen_codes: max_code %d ", max_code));
for (n = 0; n <= max_code; n++) {
len = tree[n * 2 + 1];
if (len === 0)
continue;
// Now reverse the bits
tree[n * 2] = bi_reverse(next_code[len]++, len);
}
}
// Construct one Huffman tree and assigns the code bit strings and lengths.
// Update the total bit length for the current block.
// IN assertion: the field freq is set for all tree elements.
// OUT assertions: the fields len and code are set to the optimal bit length
// and corresponding code. The length opt_len is updated; static_len is
// also updated if stree is not null. The field max_code is set.
that.build_tree = function(s) {
var tree = that.dyn_tree;
var stree = that.stat_desc.static_tree;
var elems = that.stat_desc.elems;
var n, m; // iterate over heap elements
var max_code = -1; // largest code with non zero frequency
var node; // new node being created
// Construct the initial heap, with least frequent element in
// heap[1]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
// heap[0] is not used.
s.heap_len = 0;
s.heap_max = HEAP_SIZE;
for (n = 0; n < elems; n++) {
if (tree[n * 2] !== 0) {
s.heap[++s.heap_len] = max_code = n;
s.depth[n] = 0;
} else {
tree[n * 2 + 1] = 0;
}
}
// The pkzip format requires that at least one distance code exists,
// and that at least one bit should be sent even if there is only one
// possible code. So to avoid special checks later on we force at least
// two codes of non zero frequency.
while (s.heap_len < 2) {
node = s.heap[++s.heap_len] = max_code < 2 ? ++max_code : 0;
tree[node * 2] = 1;
s.depth[node] = 0;
s.opt_len--;
if (stree)
s.static_len -= stree[node * 2 + 1];
// node is 0 or 1 so it does not have extra bits
}
that.max_code = max_code;
// The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,
// establish sub-heaps of increasing lengths:
for (n = Math.floor(s.heap_len / 2); n >= 1; n--)
s.pqdownheap(tree, n);
// Construct the Huffman tree by repeatedly combining the least two
// frequent nodes.
node = elems; // next internal node of the tree
do {
// n = node of least frequency
n = s.heap[1];
s.heap[1] = s.heap[s.heap_len--];
s.pqdownheap(tree, 1);
m = s.heap[1]; // m = node of next least frequency
s.heap[--s.heap_max] = n; // keep the nodes sorted by frequency
s.heap[--s.heap_max] = m;
// Create a new node father of n and m
tree[node * 2] = (tree[n * 2] + tree[m * 2]);
s.depth[node] = Math.max(s.depth[n], s.depth[m]) + 1;
tree[n * 2 + 1] = tree[m * 2 + 1] = node;
// and insert the new node in the heap
s.heap[1] = node++;
s.pqdownheap(tree, 1);
} while (s.heap_len >= 2);
s.heap[--s.heap_max] = s.heap[1];
// At this point, the fields freq and dad are set. We can now
// generate the bit lengths.
gen_bitlen(s);
// The field len is now set, we can generate the bit codes
gen_codes(tree, that.max_code, s.bl_count);
};
}
Tree._length_code = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16,
16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20,
20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 ];
Tree.base_length = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 0 ];
Tree.base_dist = [ 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384,
24576 ];
// Mapping from a distance to a distance code. dist is the distance - 1 and
// must not have side effects. _dist_code[256] and _dist_code[257] are never
// used.
Tree.d_code = function(dist) {
return ((dist) < 256 ? _dist_code[dist] : _dist_code[256 + ((dist) >>> 7)]);
};
// extra bits for each length code
Tree.extra_lbits = [ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 ];
// extra bits for each distance code
Tree.extra_dbits = [ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 ];
// extra bits for each bit length code
Tree.extra_blbits = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 7 ];
Tree.bl_order = [ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ];
// StaticTree
function StaticTree(static_tree, extra_bits, extra_base, elems, max_length) {
var that = this;
that.static_tree = static_tree;
that.extra_bits = extra_bits;
that.extra_base = extra_base;
that.elems = elems;
that.max_length = max_length;
}
StaticTree.static_ltree = [ 12, 8, 140, 8, 76, 8, 204, 8, 44, 8, 172, 8, 108, 8, 236, 8, 28, 8, 156, 8, 92, 8, 220, 8, 60, 8, 188, 8, 124, 8, 252, 8, 2, 8,
130, 8, 66, 8, 194, 8, 34, 8, 162, 8, 98, 8, 226, 8, 18, 8, 146, 8, 82, 8, 210, 8, 50, 8, 178, 8, 114, 8, 242, 8, 10, 8, 138, 8, 74, 8, 202, 8, 42,
8, 170, 8, 106, 8, 234, 8, 26, 8, 154, 8, 90, 8, 218, 8, 58, 8, 186, 8, 122, 8, 250, 8, 6, 8, 134, 8, 70, 8, 198, 8, 38, 8, 166, 8, 102, 8, 230, 8,
22, 8, 150, 8, 86, 8, 214, 8, 54, 8, 182, 8, 118, 8, 246, 8, 14, 8, 142, 8, 78, 8, 206, 8, 46, 8, 174, 8, 110, 8, 238, 8, 30, 8, 158, 8, 94, 8,
222, 8, 62, 8, 190, 8, 126, 8, 254, 8, 1, 8, 129, 8, 65, 8, 193, 8, 33, 8, 161, 8, 97, 8, 225, 8, 17, 8, 145, 8, 81, 8, 209, 8, 49, 8, 177, 8, 113,
8, 241, 8, 9, 8, 137, 8, 73, 8, 201, 8, 41, 8, 169, 8, 105, 8, 233, 8, 25, 8, 153, 8, 89, 8, 217, 8, 57, 8, 185, 8, 121, 8, 249, 8, 5, 8, 133, 8,
69, 8, 197, 8, 37, 8, 165, 8, 101, 8, 229, 8, 21, 8, 149, 8, 85, 8, 213, 8, 53, 8, 181, 8, 117, 8, 245, 8, 13, 8, 141, 8, 77, 8, 205, 8, 45, 8,
173, 8, 109, 8, 237, 8, 29, 8, 157, 8, 93, 8, 221, 8, 61, 8, 189, 8, 125, 8, 253, 8, 19, 9, 275, 9, 147, 9, 403, 9, 83, 9, 339, 9, 211, 9, 467, 9,
51, 9, 307, 9, 179, 9, 435, 9, 115, 9, 371, 9, 243, 9, 499, 9, 11, 9, 267, 9, 139, 9, 395, 9, 75, 9, 331, 9, 203, 9, 459, 9, 43, 9, 299, 9, 171, 9,
427, 9, 107, 9, 363, 9, 235, 9, 491, 9, 27, 9, 283, 9, 155, 9, 411, 9, 91, 9, 347, 9, 219, 9, 475, 9, 59, 9, 315, 9, 187, 9, 443, 9, 123, 9, 379,
9, 251, 9, 507, 9, 7, 9, 263, 9, 135, 9, 391, 9, 71, 9, 327, 9, 199, 9, 455, 9, 39, 9, 295, 9, 167, 9, 423, 9, 103, 9, 359, 9, 231, 9, 487, 9, 23,
9, 279, 9, 151, 9, 407, 9, 87, 9, 343, 9, 215, 9, 471, 9, 55, 9, 311, 9, 183, 9, 439, 9, 119, 9, 375, 9, 247, 9, 503, 9, 15, 9, 271, 9, 143, 9,
399, 9, 79, 9, 335, 9, 207, 9, 463, 9, 47, 9, 303, 9, 175, 9, 431, 9, 111, 9, 367, 9, 239, 9, 495, 9, 31, 9, 287, 9, 159, 9, 415, 9, 95, 9, 351, 9,
223, 9, 479, 9, 63, 9, 319, 9, 191, 9, 447, 9, 127, 9, 383, 9, 255, 9, 511, 9, 0, 7, 64, 7, 32, 7, 96, 7, 16, 7, 80, 7, 48, 7, 112, 7, 8, 7, 72, 7,
40, 7, 104, 7, 24, 7, 88, 7, 56, 7, 120, 7, 4, 7, 68, 7, 36, 7, 100, 7, 20, 7, 84, 7, 52, 7, 116, 7, 3, 8, 131, 8, 67, 8, 195, 8, 35, 8, 163, 8,
99, 8, 227, 8 ];
StaticTree.static_dtree = [ 0, 5, 16, 5, 8, 5, 24, 5, 4, 5, 20, 5, 12, 5, 28, 5, 2, 5, 18, 5, 10, 5, 26, 5, 6, 5, 22, 5, 14, 5, 30, 5, 1, 5, 17, 5, 9, 5,
25, 5, 5, 5, 21, 5, 13, 5, 29, 5, 3, 5, 19, 5, 11, 5, 27, 5, 7, 5, 23, 5 ];
StaticTree.static_l_desc = new StaticTree(StaticTree.static_ltree, Tree.extra_lbits, LITERALS + 1, L_CODES, MAX_BITS);
StaticTree.static_d_desc = new StaticTree(StaticTree.static_dtree, Tree.extra_dbits, 0, D_CODES, MAX_BITS);
StaticTree.static_bl_desc = new StaticTree(null, Tree.extra_blbits, 0, BL_CODES, MAX_BL_BITS);
// Deflate
var MAX_MEM_LEVEL = 9;
var DEF_MEM_LEVEL = 8;
function Config(good_length, max_lazy, nice_length, max_chain, func) {
var that = this;
that.good_length = good_length;
that.max_lazy = max_lazy;
that.nice_length = nice_length;
that.max_chain = max_chain;
that.func = func;
}
var STORED = 0;
var FAST = 1;
var SLOW = 2;
var config_table = [ new Config(0, 0, 0, 0, STORED), new Config(4, 4, 8, 4, FAST), new Config(4, 5, 16, 8, FAST), new Config(4, 6, 32, 32, FAST),
new Config(4, 4, 16, 16, SLOW), new Config(8, 16, 32, 32, SLOW), new Config(8, 16, 128, 128, SLOW), new Config(8, 32, 128, 256, SLOW),
new Config(32, 128, 258, 1024, SLOW), new Config(32, 258, 258, 4096, SLOW) ];
var z_errmsg = [ "need dictionary", // Z_NEED_DICT
// 2
"stream end", // Z_STREAM_END 1
"", // Z_OK 0
"", // Z_ERRNO (-1)
"stream error", // Z_STREAM_ERROR (-2)
"data error", // Z_DATA_ERROR (-3)
"", // Z_MEM_ERROR (-4)
"buffer error", // Z_BUF_ERROR (-5)
"",// Z_VERSION_ERROR (-6)
"" ];
// block not completed, need more input or more output
var NeedMore = 0;
// block flush performed
var BlockDone = 1;
// finish started, need only more output at next deflate
var FinishStarted = 2;
// finish done, accept no more input or output
var FinishDone = 3;
// preset dictionary flag in zlib header
var PRESET_DICT = 0x20;
var INIT_STATE = 42;
var BUSY_STATE = 113;
var FINISH_STATE = 666;
// The deflate compression method
var Z_DEFLATED = 8;
var STORED_BLOCK = 0;
var STATIC_TREES = 1;
var DYN_TREES = 2;
var MIN_MATCH = 3;
var MAX_MATCH = 258;
var MIN_LOOKAHEAD = (MAX_MATCH + MIN_MATCH + 1);
function smaller(tree, n, m, depth) {
var tn2 = tree[n * 2];
var tm2 = tree[m * 2];
return (tn2 < tm2 || (tn2 == tm2 && depth[n] <= depth[m]));
}
function Deflate() {
var that = this;
var strm; // pointer back to this zlib stream
var status; // as the name implies
// pending_buf; // output still pending
var pending_buf_size; // size of pending_buf
// pending_out; // next pending byte to output to the stream
// pending; // nb of bytes in the pending buffer
var method; // STORED (for zip only) or DEFLATED
var last_flush; // value of flush param for previous deflate call
var w_size; // LZ77 window size (32K by default)
var w_bits; // log2(w_size) (8..16)
var w_mask; // w_size - 1
var window;
// Sliding window. Input bytes are read into the second half of the window,
// and move to the first half later to keep a dictionary of at least wSize
// bytes. With this organization, matches are limited to a distance of
// wSize-MAX_MATCH bytes, but this ensures that IO is always
// performed with a length multiple of the block size. Also, it limits
// the window size to 64K, which is quite useful on MSDOS.
// To do: use the user input buffer as sliding window.
var window_size;
// Actual size of window: 2*wSize, except when the user input buffer
// is directly used as sliding window.
var prev;
// Link to older string with same hash index. To limit the size of this
// array to 64K, this link is maintained only for the last 32K strings.
// An index in this array is thus a window index modulo 32K.
var head; // Heads of the hash chains or NIL.
var ins_h; // hash index of string to be inserted
var hash_size; // number of elements in hash table
var hash_bits; // log2(hash_size)
var hash_mask; // hash_size-1
// Number of bits by which ins_h must be shifted at each input
// step. It must be such that after MIN_MATCH steps, the oldest
// byte no longer takes part in the hash key, that is:
// hash_shift * MIN_MATCH >= hash_bits
var hash_shift;
// Window position at the beginning of the current output block. Gets
// negative when the window is moved backwards.
var block_start;
var match_length; // length of best match
var prev_match; // previous match
var match_available; // set if previous match exists
var strstart; // start of string to insert
var match_start; // start of matching string
var lookahead; // number of valid bytes ahead in window
// Length of the best match at previous step. Matches not greater than this
// are discarded. This is used in the lazy match evaluation.
var prev_length;
// To speed up deflation, hash chains are never searched beyond this
// length. A higher limit improves compression ratio but degrades the speed.
var max_chain_length;
// Attempt to find a better match only when the current match is strictly
// smaller than this value. This mechanism is used only for compression
// levels >= 4.
var max_lazy_match;
// Insert new strings in the hash table only if the match length is not
// greater than this length. This saves time but degrades compression.
// max_insert_length is used only for compression levels <= 3.
var level; // compression level (1..9)
var strategy; // favor or force Huffman coding
// Use a faster search when the previous match is longer than this
var good_match;
// Stop searching when current match exceeds this
var nice_match;
var dyn_ltree; // literal and length tree
var dyn_dtree; // distance tree
var bl_tree; // Huffman tree for bit lengths
var l_desc = new Tree(); // desc for literal tree
var d_desc = new Tree(); // desc for distance tree
var bl_desc = new Tree(); // desc for bit length tree
// that.heap_len; // number of elements in the heap
// that.heap_max; // element of largest frequency
// The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.
// The same heap array is used to build all trees.
// Depth of each subtree used as tie breaker for trees of equal frequency
that.depth = [];
var l_buf; // index for literals or lengths */
// Size of match buffer for literals/lengths. There are 4 reasons for
// limiting lit_bufsize to 64K:
// - frequencies can be kept in 16 bit counters
// - if compression is not successful for the first block, all input
// data is still in the window so we can still emit a stored block even
// when input comes from standard input. (This can also be done for
// all blocks if lit_bufsize is not greater than 32K.)
// - if compression is not successful for a file smaller than 64K, we can
// even emit a stored file instead of a stored block (saving 5 bytes).
// This is applicable only for zip (not gzip or zlib).
// - creating new Huffman trees less frequently may not provide fast
// adaptation to changes in the input data statistics. (Take for
// example a binary file with poorly compressible code followed by
// a highly compressible string table.) Smaller buffer sizes give
// fast adaptation but have of course the overhead of transmitting
// trees more frequently.
// - I can't count above 4
var lit_bufsize;
var last_lit; // running index in l_buf
// Buffer for distances. To simplify the code, d_buf and l_buf have
// the same number of elements. To use different lengths, an extra flag
// array would be necessary.
var d_buf; // index of pendig_buf
// that.opt_len; // bit length of current block with optimal trees
// that.static_len; // bit length of current block with static trees
var matches; // number of string matches in current block
var last_eob_len; // bit length of EOB code for last block
// Output buffer. bits are inserted starting at the bottom (least
// significant bits).
var bi_buf;
// Number of valid bits in bi_buf. All bits above the last valid bit
// are always zero.
var bi_valid;
// number of codes at each bit length for an optimal tree
that.bl_count = [];
// heap used to build the Huffman trees
that.heap = [];
dyn_ltree = [];
dyn_dtree = [];
bl_tree = [];
function lm_init() {
var i;
window_size = 2 * w_size;
head[hash_size - 1] = 0;
for (i = 0; i < hash_size - 1; i++) {
head[i] = 0;
}
// Set the default configuration parameters:
max_lazy_match = config_table[level].max_lazy;
good_match = config_table[level].good_length;
nice_match = config_table[level].nice_length;
max_chain_length = config_table[level].max_chain;
strstart = 0;
block_start = 0;
lookahead = 0;
match_length = prev_length = MIN_MATCH - 1;
match_available = 0;
ins_h = 0;
}
function init_block() {
var i;
// Initialize the trees.
for (i = 0; i < L_CODES; i++)
dyn_ltree[i * 2] = 0;
for (i = 0; i < D_CODES; i++)
dyn_dtree[i * 2] = 0;
for (i = 0; i < BL_CODES; i++)
bl_tree[i * 2] = 0;
dyn_ltree[END_BLOCK * 2] = 1;
that.opt_len = that.static_len = 0;
last_lit = matches = 0;
}
// Initialize the tree data structures for a new zlib stream.
function tr_init() {
l_desc.dyn_tree = dyn_ltree;
l_desc.stat_desc = StaticTree.static_l_desc;
d_desc.dyn_tree = dyn_dtree;
d_desc.stat_desc = StaticTree.static_d_desc;
bl_desc.dyn_tree = bl_tree;
bl_desc.stat_desc = StaticTree.static_bl_desc;
bi_buf = 0;
bi_valid = 0;
last_eob_len = 8; // enough lookahead for inflate
// Initialize the first block of the first file:
init_block();
}
// Restore the heap property by moving down the tree starting at node k,
// exchanging a node with the smallest of its two sons if necessary,
// stopping
// when the heap property is re-established (each father smaller than its
// two sons).
that.pqdownheap = function(tree, // the tree to restore
k // node to move down
) {
var heap = that.heap;
var v = heap[k];
var j = k << 1; // left son of k
while (j <= that.heap_len) {
// Set j to the smallest of the two sons:
if (j < that.heap_len && smaller(tree, heap[j + 1], heap[j], that.depth)) {
j++;
}
// Exit if v is smaller than both sons
if (smaller(tree, v, heap[j], that.depth))
break;
// Exchange v with the smallest son
heap[k] = heap[j];
k = j;
// And continue down the tree, setting j to the left son of k
j <<= 1;
}
heap[k] = v;
};
// Scan a literal or distance tree to determine the frequencies of the codes
// in the bit length tree.
function scan_tree(tree,// the tree to be scanned
max_code // and its largest code of non zero frequency
) {
var n; // iterates over all tree elements
var prevlen = -1; // last emitted length
var curlen; // length of current code
var nextlen = tree[0 * 2 + 1]; // length of next code
var count = 0; // repeat count of the current code
var max_count = 7; // max repeat count
var min_count = 4; // min repeat count
if (nextlen === 0) {
max_count = 138;
min_count = 3;
}
tree[(max_code + 1) * 2 + 1] = 0xffff; // guard
for (n = 0; n <= max_code; n++) {
curlen = nextlen;
nextlen = tree[(n + 1) * 2 + 1];
if (++count < max_count && curlen == nextlen) {
continue;
} else if (count < min_count) {
bl_tree[curlen * 2] += count;
} else if (curlen !== 0) {
if (curlen != prevlen)
bl_tree[curlen * 2]++;
bl_tree[REP_3_6 * 2]++;
} else if (count <= 10) {
bl_tree[REPZ_3_10 * 2]++;
} else {
bl_tree[REPZ_11_138 * 2]++;
}
count = 0;
prevlen = curlen;
if (nextlen === 0) {
max_count = 138;
min_count = 3;
} else if (curlen == nextlen) {
max_count = 6;
min_count = 3;
} else {
max_count = 7;
min_count = 4;
}
}
}
// Construct the Huffman tree for the bit lengths and return the index in
// bl_order of the last bit length code to send.
function build_bl_tree() {
var max_blindex; // index of last bit length code of non zero freq
// Determine the bit length frequencies for literal and distance trees
scan_tree(dyn_ltree, l_desc.max_code);
scan_tree(dyn_dtree, d_desc.max_code);
// Build the bit length tree:
bl_desc.build_tree(that);
// opt_len now includes the length of the tree representations, except
// the lengths of the bit lengths codes and the 5+5+4 bits for the
// counts.
// Determine the number of bit length codes to send. The pkzip format
// requires that at least 4 bit length codes be sent. (appnote.txt says
// 3 but the actual value used is 4.)
for (max_blindex = BL_CODES - 1; max_blindex >= 3; max_blindex--) {
if (bl_tree[Tree.bl_order[max_blindex] * 2 + 1] !== 0)
break;
}
// Update opt_len to include the bit length tree and counts
that.opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4;
return max_blindex;
}
// Output a byte on the stream.
// IN assertion: there is enough room in pending_buf.
function put_byte(p) {
that.pending_buf[that.pending++] = p;
}
function put_short(w) {
put_byte(w & 0xff);
put_byte((w >>> 8) & 0xff);
}
function putShortMSB(b) {
put_byte((b >> 8) & 0xff);
put_byte((b & 0xff) & 0xff);
}
function send_bits(value, length) {
var val, len = length;
if (bi_valid > Buf_size - len) {
val = value;
// bi_buf |= (val << bi_valid);
bi_buf |= ((val << bi_valid) & 0xffff);
put_short(bi_buf);
bi_buf = val >>> (Buf_size - bi_valid);
bi_valid += len - Buf_size;
} else {
// bi_buf |= (value) << bi_valid;
bi_buf |= (((value) << bi_valid) & 0xffff);
bi_valid += len;
}
}
function send_code(c, tree) {
var c2 = c * 2;
send_bits(tree[c2] & 0xffff, tree[c2 + 1] & 0xffff);
}
// Send a literal or distance tree in compressed form, using the codes in
// bl_tree.
function send_tree(tree,// the tree to be sent
max_code // and its largest code of non zero frequency
) {
var n; // iterates over all tree elements
var prevlen = -1; // last emitted length
var curlen; // length of current code
var nextlen = tree[0 * 2 + 1]; // length of next code
var count = 0; // repeat count of the current code
var max_count = 7; // max repeat count
var min_count = 4; // min repeat count
if (nextlen === 0) {
max_count = 138;
min_count = 3;
}
for (n = 0; n <= max_code; n++) {
curlen = nextlen;
nextlen = tree[(n + 1) * 2 + 1];
if (++count < max_count && curlen == nextlen) {
continue;
} else if (count < min_count) {
do {
send_code(curlen, bl_tree);
} while (--count !== 0);
} else if (curlen !== 0) {
if (curlen != prevlen) {
send_code(curlen, bl_tree);
count--;
}
send_code(REP_3_6, bl_tree);
send_bits(count - 3, 2);
} else if (count <= 10) {
send_code(REPZ_3_10, bl_tree);
send_bits(count - 3, 3);
} else {
send_code(REPZ_11_138, bl_tree);
send_bits(count - 11, 7);
}
count = 0;
prevlen = curlen;
if (nextlen === 0) {
max_count = 138;
min_count = 3;
} else if (curlen == nextlen) {
max_count = 6;
min_count = 3;
} else {
max_count = 7;
min_count = 4;
}
}
}
// Send the header for a block using dynamic Huffman trees: the counts, the
// lengths of the bit length codes, the literal tree and the distance tree.
// IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4.
function send_all_trees(lcodes, dcodes, blcodes) {
var rank; // index in bl_order
send_bits(lcodes - 257, 5); // not +255 as stated in appnote.txt
send_bits(dcodes - 1, 5);
send_bits(blcodes - 4, 4); // not -3 as stated in appnote.txt
for (rank = 0; rank < blcodes; rank++) {
send_bits(bl_tree[Tree.bl_order[rank] * 2 + 1], 3);
}
send_tree(dyn_ltree, lcodes - 1); // literal tree
send_tree(dyn_dtree, dcodes - 1); // distance tree
}
// Flush the bit buffer, keeping at most 7 bits in it.
function bi_flush() {
if (bi_valid == 16) {
put_short(bi_buf);
bi_buf = 0;
bi_valid = 0;
} else if (bi_valid >= 8) {
put_byte(bi_buf & 0xff);
bi_buf >>>= 8;
bi_valid -= 8;
}
}
// Send one empty static block to give enough lookahead for inflate.
// This takes 10 bits, of which 7 may remain in the bit buffer.
// The current inflate code requires 9 bits of lookahead. If the
// last two codes for the previous block (real code plus EOB) were coded
// on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode
// the last real code. In this case we send two empty static blocks instead
// of one. (There are no problems if the previous block is stored or fixed.)
// To simplify the code, we assume the worst case of last real code encoded
// on one bit only.
function _tr_align() {
send_bits(STATIC_TREES << 1, 3);
send_code(END_BLOCK, StaticTree.static_ltree);
bi_flush();
// Of the 10 bits for the empty block, we have already sent
// (10 - bi_valid) bits. The lookahead for the last real code (before
// the EOB of the previous block) was thus at least one plus the length
// of the EOB plus what we have just sent of the empty static block.
if (1 + last_eob_len + 10 - bi_valid < 9) {
send_bits(STATIC_TREES << 1, 3);
send_code(END_BLOCK, StaticTree.static_ltree);
bi_flush();
}
last_eob_len = 7;
}
// Save the match info and tally the frequency counts. Return true if
// the current block must be flushed.
function _tr_tally(dist, // distance of matched string
lc // match length-MIN_MATCH or unmatched char (if dist==0)
) {
var out_length, in_length, dcode;
that.pending_buf[d_buf + last_lit * 2] = (dist >>> 8) & 0xff;
that.pending_buf[d_buf + last_lit * 2 + 1] = dist & 0xff;
that.pending_buf[l_buf + last_lit] = lc & 0xff;
last_lit++;
if (dist === 0) {
// lc is the unmatched char
dyn_ltree[lc * 2]++;
} else {
matches++;
// Here, lc is the match length - MIN_MATCH
dist--; // dist = match distance - 1
dyn_ltree[(Tree._length_code[lc] + LITERALS + 1) * 2]++;
dyn_dtree[Tree.d_code(dist) * 2]++;
}
if ((last_lit & 0x1fff) === 0 && level > 2) {
// Compute an upper bound for the compressed length
out_length = last_lit * 8;
in_length = strstart - block_start;
for (dcode = 0; dcode < D_CODES; dcode++) {
out_length += dyn_dtree[dcode * 2] * (5 + Tree.extra_dbits[dcode]);
}
out_length >>>= 3;
if ((matches < Math.floor(last_lit / 2)) && out_length < Math.floor(in_length / 2))
return true;
}
return (last_lit == lit_bufsize - 1);
// We avoid equality with lit_bufsize because of wraparound at 64K
// on 16 bit machines and because stored blocks are restricted to
// 64K-1 bytes.
}
// Send the block data compressed using the given Huffman trees
function compress_block(ltree, dtree) {
var dist; // distance of matched string
var lc; // match length or unmatched char (if dist === 0)
var lx = 0; // running index in l_buf
var code; // the code to send
var extra; // number of extra bits to send
if (last_lit !== 0) {
do {
dist = ((that.pending_buf[d_buf + lx * 2] << 8) & 0xff00) | (that.pending_buf[d_buf + lx * 2 + 1] & 0xff);
lc = (that.pending_buf[l_buf + lx]) & 0xff;
lx++;
if (dist === 0) {
send_code(lc, ltree); // send a literal byte
} else {
// Here, lc is the match length - MIN_MATCH
code = Tree._length_code[lc];
send_code(code + LITERALS + 1, ltree); // send the length
// code
extra = Tree.extra_lbits[code];
if (extra !== 0) {
lc -= Tree.base_length[code];
send_bits(lc, extra); // send the extra length bits
}
dist--; // dist is now the match distance - 1
code = Tree.d_code(dist);
send_code(code, dtree); // send the distance code
extra = Tree.extra_dbits[code];
if (extra !== 0) {
dist -= Tree.base_dist[code];
send_bits(dist, extra); // send the extra distance bits
}
} // literal or match pair ?
// Check that the overlay between pending_buf and d_buf+l_buf is
// ok:
} while (lx < last_lit);
}
send_code(END_BLOCK, ltree);
last_eob_len = ltree[END_BLOCK * 2 + 1];
}
// Flush the bit buffer and align the output on a byte boundary
function bi_windup() {
if (bi_valid > 8) {
put_short(bi_buf);
} else if (bi_valid > 0) {
put_byte(bi_buf & 0xff);
}
bi_buf = 0;
bi_valid = 0;
}
// Copy a stored block, storing first the length and its
// one's complement if requested.
function copy_block(buf, // the input data
len, // its length
header // true if block header must be written
) {
bi_windup(); // align on byte boundary
last_eob_len = 8; // enough lookahead for inflate
if (header) {
put_short(len);
put_short(~len);
}
that.pending_buf.set(window.subarray(buf, buf + len), that.pending);
that.pending += len;
}
// Send a stored block
function _tr_stored_block(buf, // input block
stored_len, // length of input block
eof // true if this is the last block for a file
) {
send_bits((STORED_BLOCK << 1) + (eof ? 1 : 0), 3); // send block type
copy_block(buf, stored_len, true); // with header
}
// Determine the best encoding for the current block: dynamic trees, static
// trees or store, and output the encoded block to the zip file.
function _tr_flush_block(buf, // input block, or NULL if too old
stored_len, // length of input block
eof // true if this is the last block for a file
) {
var opt_lenb, static_lenb;// opt_len and static_len in bytes
var max_blindex = 0; // index of last bit length code of non zero freq
// Build the Huffman trees unless a stored block is forced
if (level > 0) {
// Construct the literal and distance trees
l_desc.build_tree(that);
d_desc.build_tree(that);
// At this point, opt_len and static_len are the total bit lengths
// of
// the compressed block data, excluding the tree representations.
// Build the bit length tree for the above two trees, and get the
// index
// in bl_order of the last bit length code to send.
max_blindex = build_bl_tree();
// Determine the best encoding. Compute first the block length in
// bytes
opt_lenb = (that.opt_len + 3 + 7) >>> 3;
static_lenb = (that.static_len + 3 + 7) >>> 3;
if (static_lenb <= opt_lenb)
opt_lenb = static_lenb;
} else {
opt_lenb = static_lenb = stored_len + 5; // force a stored block
}
if ((stored_len + 4 <= opt_lenb) && buf != -1) {
// 4: two words for the lengths
// The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE.
// Otherwise we can't have processed more than WSIZE input bytes
// since
// the last block flush, because compression would have been
// successful. If LIT_BUFSIZE <= WSIZE, it is never too late to
// transform a block into a stored block.
_tr_stored_block(buf, stored_len, eof);
} else if (static_lenb == opt_lenb) {
send_bits((STATIC_TREES << 1) + (eof ? 1 : 0), 3);
compress_block(StaticTree.static_ltree, StaticTree.static_dtree);
} else {
send_bits((DYN_TREES << 1) + (eof ? 1 : 0), 3);
send_all_trees(l_desc.max_code + 1, d_desc.max_code + 1, max_blindex + 1);
compress_block(dyn_ltree, dyn_dtree);
}
// The above check is made mod 2^32, for files larger than 512 MB
// and uLong implemented on 32 bits.
init_block();
if (eof) {
bi_windup();
}
}
function flush_block_only(eof) {
_tr_flush_block(block_start >= 0 ? block_start : -1, strstart - block_start, eof);
block_start = strstart;
strm.flush_pending();
}
// Fill the window when the lookahead becomes insufficient.
// Updates strstart and lookahead.
//
// IN assertion: lookahead < MIN_LOOKAHEAD
// OUT assertions: strstart <= window_size-MIN_LOOKAHEAD
// At least one byte has been read, or avail_in === 0; reads are
// performed for at least two bytes (required for the zip translate_eol
// option -- not supported here).
function fill_window() {
var n, m;
var p;
var more; // Amount of free space at the end of the window.
do {
more = (window_size - lookahead - strstart);
// Deal with !@#$% 64K limit:
if (more === 0 && strstart === 0 && lookahead === 0) {
more = w_size;
} else if (more == -1) {
// Very unlikely, but possible on 16 bit machine if strstart ==
// 0
// and lookahead == 1 (input done one byte at time)
more--;
// If the window is almost full and there is insufficient
// lookahead,
// move the upper half to the lower one to make room in the
// upper half.
} else if (strstart >= w_size + w_size - MIN_LOOKAHEAD) {
window.set(window.subarray(w_size, w_size + w_size), 0);
match_start -= w_size;
strstart -= w_size; // we now have strstart >= MAX_DIST
block_start -= w_size;
// Slide the hash table (could be avoided with 32 bit values
// at the expense of memory usage). We slide even when level ==
// 0
// to keep the hash table consistent if we switch back to level
// > 0
// later. (Using level 0 permanently is not an optimal usage of
// zlib, so we don't care about this pathological case.)
n = hash_size;
p = n;
do {
m = (head[--p] & 0xffff);
head[p] = (m >= w_size ? m - w_size : 0);
} while (--n !== 0);
n = w_size;
p = n;
do {
m = (prev[--p] & 0xffff);
prev[p] = (m >= w_size ? m - w_size : 0);
// If n is not on any hash chain, prev[n] is garbage but
// its value will never be used.
} while (--n !== 0);
more += w_size;
}
if (strm.avail_in === 0)
return;
// If there was no sliding:
// strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 &&
// more == window_size - lookahead - strstart
// => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1)
// => more >= window_size - 2*WSIZE + 2
// In the BIG_MEM or MMAP case (not yet supported),
// window_size == input_size + MIN_LOOKAHEAD &&
// strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD.
// Otherwise, window_size == 2*WSIZE so more >= 2.
// If there was sliding, more >= WSIZE. So in all cases, more >= 2.
n = strm.read_buf(window, strstart + lookahead, more);
lookahead += n;
// Initialize the hash value now that we have some input:
if (lookahead >= MIN_MATCH) {
ins_h = window[strstart] & 0xff;
ins_h = (((ins_h) << hash_shift) ^ (window[strstart + 1] & 0xff)) & hash_mask;
}
// If the whole input has less than MIN_MATCH bytes, ins_h is
// garbage,
// but this is not important since only literal bytes will be
// emitted.
} while (lookahead < MIN_LOOKAHEAD && strm.avail_in !== 0);
}
// Copy without compression as much as possible from the input stream,
// return
// the current block state.
// This function does not insert new strings in the dictionary since
// uncompressible data is probably not useful. This function is used
// only for the level=0 compression option.
// NOTE: this function should be optimized to avoid extra copying from
// window to pending_buf.
function deflate_stored(flush) {
// Stored blocks are limited to 0xffff bytes, pending_buf is limited
// to pending_buf_size, and each stored block has a 5 byte header:
var max_block_size = 0xffff;
var max_start;
if (max_block_size > pending_buf_size - 5) {
max_block_size = pending_buf_size - 5;
}
// Copy as much as possible from input to output:
while (true) {
// Fill the window as much as possible:
if (lookahead <= 1) {
fill_window();
if (lookahead === 0 && flush == Z_NO_FLUSH)
return NeedMore;
if (lookahead === 0)
break; // flush the current block
}
strstart += lookahead;
lookahead = 0;
// Emit a stored block if pending_buf will be full:
max_start = block_start + max_block_size;
if (strstart === 0 || strstart >= max_start) {
// strstart === 0 is possible when wraparound on 16-bit machine
lookahead = (strstart - max_start);
strstart = max_start;
flush_block_only(false);
if (strm.avail_out === 0)
return NeedMore;
}
// Flush if we may have to slide, otherwise block_start may become
// negative and the data will be gone:
if (strstart - block_start >= w_size - MIN_LOOKAHEAD) {
flush_block_only(false);
if (strm.avail_out === 0)
return NeedMore;
}
}
flush_block_only(flush == Z_FINISH);
if (strm.avail_out === 0)
return (flush == Z_FINISH) ? FinishStarted : NeedMore;
return flush == Z_FINISH ? FinishDone : BlockDone;
}
function longest_match(cur_match) {
var chain_length = max_chain_length; // max hash chain length
var scan = strstart; // current string
var match; // matched string
var len; // length of current match
var best_len = prev_length; // best match length so far
var limit = strstart > (w_size - MIN_LOOKAHEAD) ? strstart - (w_size - MIN_LOOKAHEAD) : 0;
var _nice_match = nice_match;
// Stop when cur_match becomes <= limit. To simplify the code,
// we prevent matches with the string of window index 0.
var wmask = w_mask;
var strend = strstart + MAX_MATCH;
var scan_end1 = window[scan + best_len - 1];
var scan_end = window[scan + best_len];
// The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of
// 16.
// It is easy to get rid of this optimization if necessary.
// Do not waste too much time if we already have a good match:
if (prev_length >= good_match) {
chain_length >>= 2;
}
// Do not look for matches beyond the end of the input. This is
// necessary
// to make deflate deterministic.
if (_nice_match > lookahead)
_nice_match = lookahead;
do {
match = cur_match;
// Skip to next match if the match length cannot increase
// or if the match length is less than 2:
if (window[match + best_len] != scan_end || window[match + best_len - 1] != scan_end1 || window[match] != window[scan]
|| window[++match] != window[scan + 1])
continue;
// The check at best_len-1 can be removed because it will be made
// again later. (This heuristic is not always a win.)
// It is not necessary to compare scan[2] and match[2] since they
// are always equal when the other bytes match, given that
// the hash keys are equal and that HASH_BITS >= 8.
scan += 2;
match++;
// We check for insufficient lookahead only every 8th comparison;
// the 256th check will be made at strstart+258.
do {
} while (window[++scan] == window[++match] && window[++scan] == window[++match] && window[++scan] == window[++match]
&& window[++scan] == window[++match] && window[++scan] == window[++match] && window[++scan] == window[++match]
&& window[++scan] == window[++match] && window[++scan] == window[++match] && scan < strend);
len = MAX_MATCH - (strend - scan);
scan = strend - MAX_MATCH;
if (len > best_len) {
match_start = cur_match;
best_len = len;
if (len >= _nice_match)
break;
scan_end1 = window[scan + best_len - 1];
scan_end = window[scan + best_len];
}
} while ((cur_match = (prev[cur_match & wmask] & 0xffff)) > limit && --chain_length !== 0);
if (best_len <= lookahead)
return best_len;
return lookahead;
}
// Compress as much as possible from the input stream, return the current
// block state.
// This function does not perform lazy evaluation of matches and inserts
// new strings in the dictionary only for unmatched strings or for short
// matches. It is used only for the fast compression options.
function deflate_fast(flush) {
// short hash_head = 0; // head of the hash chain
var hash_head = 0; // head of the hash chain
var bflush; // set if current block must be flushed
while (true) {
// Make sure that we always have enough lookahead, except
// at the end of the input file. We need MAX_MATCH bytes
// for the next match, plus MIN_MATCH bytes to insert the
// string following the next match.
if (lookahead < MIN_LOOKAHEAD) {
fill_window();
if (lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) {
return NeedMore;
}
if (lookahead === 0)
break; // flush the current block
}
// Insert the string window[strstart .. strstart+2] in the
// dictionary, and set hash_head to the head of the hash chain:
if (lookahead >= MIN_MATCH) {
ins_h = (((ins_h) << hash_shift) ^ (window[(strstart) + (MIN_MATCH - 1)] & 0xff)) & hash_mask;
// prev[strstart&w_mask]=hash_head=head[ins_h];
hash_head = (head[ins_h] & 0xffff);
prev[strstart & w_mask] = head[ins_h];
head[ins_h] = strstart;
}
// Find the longest match, discarding those <= prev_length.
// At this point we have always match_length < MIN_MATCH
if (hash_head !== 0 && ((strstart - hash_head) & 0xffff) <= w_size - MIN_LOOKAHEAD) {
// To simplify the code, we prevent matches with the string
// of window index 0 (in particular we have to avoid a match
// of the string with itself at the start of the input file).
if (strategy != Z_HUFFMAN_ONLY) {
match_length = longest_match(hash_head);
}
// longest_match() sets match_start
}
if (match_length >= MIN_MATCH) {
// check_match(strstart, match_start, match_length);
bflush = _tr_tally(strstart - match_start, match_length - MIN_MATCH);
lookahead -= match_length;
// Insert new strings in the hash table only if the match length
// is not too large. This saves time but degrades compression.
if (match_length <= max_lazy_match && lookahead >= MIN_MATCH) {
match_length--; // string at strstart already in hash table
do {
strstart++;
ins_h = ((ins_h << hash_shift) ^ (window[(strstart) + (MIN_MATCH - 1)] & 0xff)) & hash_mask;
// prev[strstart&w_mask]=hash_head=head[ins_h];
hash_head = (head[ins_h] & 0xffff);
prev[strstart & w_mask] = head[ins_h];
head[ins_h] = strstart;
// strstart never exceeds WSIZE-MAX_MATCH, so there are
// always MIN_MATCH bytes ahead.
} while (--match_length !== 0);
strstart++;
} else {
strstart += match_length;
match_length = 0;
ins_h = window[strstart] & 0xff;
ins_h = (((ins_h) << hash_shift) ^ (window[strstart + 1] & 0xff)) & hash_mask;
// If lookahead < MIN_MATCH, ins_h is garbage, but it does
// not
// matter since it will be recomputed at next deflate call.
}
} else {
// No match, output a literal byte
bflush = _tr_tally(0, window[strstart] & 0xff);
lookahead--;
strstart++;
}
if (bflush) {
flush_block_only(false);
if (strm.avail_out === 0)
return NeedMore;
}
}
flush_block_only(flush == Z_FINISH);
if (strm.avail_out === 0) {
if (flush == Z_FINISH)
return FinishStarted;
else
return NeedMore;
}
return flush == Z_FINISH ? FinishDone : BlockDone;
}
// Same as above, but achieves better compression. We use a lazy
// evaluation for matches: a match is finally adopted only if there is
// no better match at the next window position.
function deflate_slow(flush) {
// short hash_head = 0; // head of hash chain
var hash_head = 0; // head of hash chain
var bflush; // set if current block must be flushed
var max_insert;
// Process the input block.
while (true) {
// Make sure that we always have enough lookahead, except
// at the end of the input file. We need MAX_MATCH bytes
// for the next match, plus MIN_MATCH bytes to insert the
// string following the next match.
if (lookahead < MIN_LOOKAHEAD) {
fill_window();
if (lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) {
return NeedMore;
}
if (lookahead === 0)
break; // flush the current block
}
// Insert the string window[strstart .. strstart+2] in the
// dictionary, and set hash_head to the head of the hash chain:
if (lookahead >= MIN_MATCH) {
ins_h = (((ins_h) << hash_shift) ^ (window[(strstart) + (MIN_MATCH - 1)] & 0xff)) & hash_mask;
// prev[strstart&w_mask]=hash_head=head[ins_h];
hash_head = (head[ins_h] & 0xffff);
prev[strstart & w_mask] = head[ins_h];
head[ins_h] = strstart;
}
// Find the longest match, discarding those <= prev_length.
prev_length = match_length;
prev_match = match_start;
match_length = MIN_MATCH - 1;
if (hash_head !== 0 && prev_length < max_lazy_match && ((strstart - hash_head) & 0xffff) <= w_size - MIN_LOOKAHEAD) {
// To simplify the code, we prevent matches with the string
// of window index 0 (in particular we have to avoid a match
// of the string with itself at the start of the input file).
if (strategy != Z_HUFFMAN_ONLY) {
match_length = longest_match(hash_head);
}
// longest_match() sets match_start
if (match_length <= 5 && (strategy == Z_FILTERED || (match_length == MIN_MATCH && strstart - match_start > 4096))) {
// If prev_match is also MIN_MATCH, match_start is garbage
// but we will ignore the current match anyway.
match_length = MIN_MATCH - 1;
}
}
// If there was a match at the previous step and the current
// match is not better, output the previous match:
if (prev_length >= MIN_MATCH && match_length <= prev_length) {
max_insert = strstart + lookahead - MIN_MATCH;
// Do not insert strings in hash table beyond this.
// check_match(strstart-1, prev_match, prev_length);
bflush = _tr_tally(strstart - 1 - prev_match, prev_length - MIN_MATCH);
// Insert in hash table all strings up to the end of the match.
// strstart-1 and strstart are already inserted. If there is not
// enough lookahead, the last two strings are not inserted in
// the hash table.
lookahead -= prev_length - 1;
prev_length -= 2;
do {
if (++strstart <= max_insert) {
ins_h = (((ins_h) << hash_shift) ^ (window[(strstart) + (MIN_MATCH - 1)] & 0xff)) & hash_mask;
// prev[strstart&w_mask]=hash_head=head[ins_h];
hash_head = (head[ins_h] & 0xffff);
prev[strstart & w_mask] = head[ins_h];
head[ins_h] = strstart;
}
} while (--prev_length !== 0);
match_available = 0;
match_length = MIN_MATCH - 1;
strstart++;
if (bflush) {
flush_block_only(false);
if (strm.avail_out === 0)
return NeedMore;
}
} else if (match_available !== 0) {
// If there was no match at the previous position, output a
// single literal. If there was a match but the current match
// is longer, truncate the previous match to a single literal.
bflush = _tr_tally(0, window[strstart - 1] & 0xff);
if (bflush) {
flush_block_only(false);
}
strstart++;
lookahead--;
if (strm.avail_out === 0)
return NeedMore;
} else {
// There is no previous match to compare with, wait for
// the next step to decide.
match_available = 1;
strstart++;
lookahead--;
}
}
if (match_available !== 0) {
bflush = _tr_tally(0, window[strstart - 1] & 0xff);
match_available = 0;
}
flush_block_only(flush == Z_FINISH);
if (strm.avail_out === 0) {
if (flush == Z_FINISH)
return FinishStarted;
else
return NeedMore;
}
return flush == Z_FINISH ? FinishDone : BlockDone;
}
function deflateReset(strm) {
strm.total_in = strm.total_out = 0;
strm.msg = null; //
that.pending = 0;
that.pending_out = 0;
status = BUSY_STATE;
last_flush = Z_NO_FLUSH;
tr_init();
lm_init();
return Z_OK;
}
that.deflateInit = function(strm, _level, bits, _method, memLevel, _strategy) {
if (!_method)
_method = Z_DEFLATED;
if (!memLevel)
memLevel = DEF_MEM_LEVEL;
if (!_strategy)
_strategy = Z_DEFAULT_STRATEGY;
// byte[] my_version=ZLIB_VERSION;
//
// if (!version || version[0] != my_version[0]
// || stream_size != sizeof(z_stream)) {
// return Z_VERSION_ERROR;
// }
strm.msg = null;
if (_level == Z_DEFAULT_COMPRESSION)
_level = 6;
if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || _method != Z_DEFLATED || bits < 9 || bits > 15 || _level < 0 || _level > 9 || _strategy < 0
|| _strategy > Z_HUFFMAN_ONLY) {
return Z_STREAM_ERROR;
}
strm.dstate = that;
w_bits = bits;
w_size = 1 << w_bits;
w_mask = w_size - 1;
hash_bits = memLevel + 7;
hash_size = 1 << hash_bits;
hash_mask = hash_size - 1;
hash_shift = Math.floor((hash_bits + MIN_MATCH - 1) / MIN_MATCH);
window = new Uint8Array(w_size * 2);
prev = [];
head = [];
lit_bufsize = 1 << (memLevel + 6); // 16K elements by default
// We overlay pending_buf and d_buf+l_buf. This works since the average
// output size for (length,distance) codes is <= 24 bits.
that.pending_buf = new Uint8Array(lit_bufsize * 4);
pending_buf_size = lit_bufsize * 4;
d_buf = Math.floor(lit_bufsize / 2);
l_buf = (1 + 2) * lit_bufsize;
level = _level;
strategy = _strategy;
method = _method & 0xff;
return deflateReset(strm);
};
that.deflateEnd = function() {
if (status != INIT_STATE && status != BUSY_STATE && status != FINISH_STATE) {
return Z_STREAM_ERROR;
}
// Deallocate in reverse order of allocations:
that.pending_buf = null;
head = null;
prev = null;
window = null;
// free
that.dstate = null;
return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK;
};
that.deflateParams = function(strm, _level, _strategy) {
var err = Z_OK;
if (_level == Z_DEFAULT_COMPRESSION) {
_level = 6;
}
if (_level < 0 || _level > 9 || _strategy < 0 || _strategy > Z_HUFFMAN_ONLY) {
return Z_STREAM_ERROR;
}
if (config_table[level].func != config_table[_level].func && strm.total_in !== 0) {
// Flush the last buffer:
err = strm.deflate(Z_PARTIAL_FLUSH);
}
if (level != _level) {
level = _level;
max_lazy_match = config_table[level].max_lazy;
good_match = config_table[level].good_length;
nice_match = config_table[level].nice_length;
max_chain_length = config_table[level].max_chain;
}
strategy = _strategy;
return err;
};
that.deflateSetDictionary = function(strm, dictionary, dictLength) {
var length = dictLength;
var n, index = 0;
if (!dictionary || status != INIT_STATE)
return Z_STREAM_ERROR;
if (length < MIN_MATCH)
return Z_OK;
if (length > w_size - MIN_LOOKAHEAD) {
length = w_size - MIN_LOOKAHEAD;
index = dictLength - length; // use the tail of the dictionary
}
window.set(dictionary.subarray(index, index + length), 0);
strstart = length;
block_start = length;
// Insert all strings in the hash table (except for the last two bytes).
// s->lookahead stays null, so s->ins_h will be recomputed at the next
// call of fill_window.
ins_h = window[0] & 0xff;
ins_h = (((ins_h) << hash_shift) ^ (window[1] & 0xff)) & hash_mask;
for (n = 0; n <= length - MIN_MATCH; n++) {
ins_h = (((ins_h) << hash_shift) ^ (window[(n) + (MIN_MATCH - 1)] & 0xff)) & hash_mask;
prev[n & w_mask] = head[ins_h];
head[ins_h] = n;
}
return Z_OK;
};
that.deflate = function(_strm, flush) {
var i, header, level_flags, old_flush, bstate;
if (flush > Z_FINISH || flush < 0) {
return Z_STREAM_ERROR;
}
if (!_strm.next_out || (!_strm.next_in && _strm.avail_in !== 0) || (status == FINISH_STATE && flush != Z_FINISH)) {
_strm.msg = z_errmsg[Z_NEED_DICT - (Z_STREAM_ERROR)];
return Z_STREAM_ERROR;
}
if (_strm.avail_out === 0) {
_strm.msg = z_errmsg[Z_NEED_DICT - (Z_BUF_ERROR)];
return Z_BUF_ERROR;
}
strm = _strm; // just in case
old_flush = last_flush;
last_flush = flush;
// Write the zlib header
if (status == INIT_STATE) {
header = (Z_DEFLATED + ((w_bits - 8) << 4)) << 8;
level_flags = ((level - 1) & 0xff) >> 1;
if (level_flags > 3)
level_flags = 3;
header |= (level_flags << 6);
if (strstart !== 0)
header |= PRESET_DICT;
header += 31 - (header % 31);
status = BUSY_STATE;
putShortMSB(header);
}
// Flush as much pending output as possible
if (that.pending !== 0) {
strm.flush_pending();
if (strm.avail_out === 0) {
// console.log(" avail_out==0");
// Since avail_out is 0, deflate will be called again with
// more output space, but possibly with both pending and
// avail_in equal to zero. There won't be anything to do,
// but this is not an error situation so make sure we
// return OK instead of BUF_ERROR at next call of deflate:
last_flush = -1;
return Z_OK;
}
// Make sure there is something to do and avoid duplicate
// consecutive
// flushes. For repeated and useless calls with Z_FINISH, we keep
// returning Z_STREAM_END instead of Z_BUFF_ERROR.
} else if (strm.avail_in === 0 && flush <= old_flush && flush != Z_FINISH) {
strm.msg = z_errmsg[Z_NEED_DICT - (Z_BUF_ERROR)];
return Z_BUF_ERROR;
}
// User must not provide more input after the first FINISH:
if (status == FINISH_STATE && strm.avail_in !== 0) {
_strm.msg = z_errmsg[Z_NEED_DICT - (Z_BUF_ERROR)];
return Z_BUF_ERROR;
}
// Start a new block or continue the current one.
if (strm.avail_in !== 0 || lookahead !== 0 || (flush != Z_NO_FLUSH && status != FINISH_STATE)) {
bstate = -1;
switch (config_table[level].func) {
case STORED:
bstate = deflate_stored(flush);
break;
case FAST:
bstate = deflate_fast(flush);
break;
case SLOW:
bstate = deflate_slow(flush);
break;
default:
}
if (bstate == FinishStarted || bstate == FinishDone) {
status = FINISH_STATE;
}
if (bstate == NeedMore || bstate == FinishStarted) {
if (strm.avail_out === 0) {
last_flush = -1; // avoid BUF_ERROR next call, see above
}
return Z_OK;
// If flush != Z_NO_FLUSH && avail_out === 0, the next call
// of deflate should use the same flush parameter to make sure
// that the flush is complete. So we don't have to output an
// empty block here, this will be done at next call. This also
// ensures that for a very small output buffer, we emit at most
// one empty block.
}
if (bstate == BlockDone) {
if (flush == Z_PARTIAL_FLUSH) {
_tr_align();
} else { // FULL_FLUSH or SYNC_FLUSH
_tr_stored_block(0, 0, false);
// For a full flush, this empty block will be recognized
// as a special marker by inflate_sync().
if (flush == Z_FULL_FLUSH) {
// state.head[s.hash_size-1]=0;
for (i = 0; i < hash_size/*-1*/; i++)
// forget history
head[i] = 0;
}
}
strm.flush_pending();
if (strm.avail_out === 0) {
last_flush = -1; // avoid BUF_ERROR at next call, see above
return Z_OK;
}
}
}
if (flush != Z_FINISH)
return Z_OK;
return Z_STREAM_END;
};
}
// ZStream
function ZStream() {
var that = this;
that.next_in_index = 0;
that.next_out_index = 0;
// that.next_in; // next input byte
that.avail_in = 0; // number of bytes available at next_in
that.total_in = 0; // total nb of input bytes read so far
// that.next_out; // next output byte should be put there
that.avail_out = 0; // remaining free space at next_out
that.total_out = 0; // total nb of bytes output so far
// that.msg;
// that.dstate;
}
ZStream.prototype = {
deflateInit : function(level, bits) {
var that = this;
that.dstate = new Deflate();
if (!bits)
bits = MAX_BITS;
return that.dstate.deflateInit(that, level, bits);
},
deflate : function(flush) {
var that = this;
if (!that.dstate) {
return Z_STREAM_ERROR;
}
return that.dstate.deflate(that, flush);
},
deflateEnd : function() {
var that = this;
if (!that.dstate)
return Z_STREAM_ERROR;
var ret = that.dstate.deflateEnd();
that.dstate = null;
return ret;
},
deflateParams : function(level, strategy) {
var that = this;
if (!that.dstate)
return Z_STREAM_ERROR;
return that.dstate.deflateParams(that, level, strategy);
},
deflateSetDictionary : function(dictionary, dictLength) {
var that = this;
if (!that.dstate)
return Z_STREAM_ERROR;
return that.dstate.deflateSetDictionary(that, dictionary, dictLength);
},
// Read a new buffer from the current input stream, update the
// total number of bytes read. All deflate() input goes through
// this function so some applications may wish to modify it to avoid
// allocating a large strm->next_in buffer and copying from it.
// (See also flush_pending()).
read_buf : function(buf, start, size) {
var that = this;
var len = that.avail_in;
if (len > size)
len = size;
if (len === 0)
return 0;
that.avail_in -= len;
buf.set(that.next_in.subarray(that.next_in_index, that.next_in_index + len), start);
that.next_in_index += len;
that.total_in += len;
return len;
},
// Flush as much pending output as possible. All deflate() output goes
// through this function so some applications may wish to modify it
// to avoid allocating a large strm->next_out buffer and copying into it.
// (See also read_buf()).
flush_pending : function() {
var that = this;
var len = that.dstate.pending;
if (len > that.avail_out)
len = that.avail_out;
if (len === 0)
return;
// if (that.dstate.pending_buf.length <= that.dstate.pending_out || that.next_out.length <= that.next_out_index
// || that.dstate.pending_buf.length < (that.dstate.pending_out + len) || that.next_out.length < (that.next_out_index +
// len)) {
// console.log(that.dstate.pending_buf.length + ", " + that.dstate.pending_out + ", " + that.next_out.length + ", " +
// that.next_out_index + ", " + len);
// console.log("avail_out=" + that.avail_out);
// }
that.next_out.set(that.dstate.pending_buf.subarray(that.dstate.pending_out, that.dstate.pending_out + len), that.next_out_index);
that.next_out_index += len;
that.dstate.pending_out += len;
that.total_out += len;
that.avail_out -= len;
that.dstate.pending -= len;
if (that.dstate.pending === 0) {
that.dstate.pending_out = 0;
}
}
};
// Deflater
function Deflater(options) {
var that = this;
var z = new ZStream();
var bufsize = 512;
var flush = Z_NO_FLUSH;
var buf = new Uint8Array(bufsize);
var level = options ? options.level : Z_DEFAULT_COMPRESSION;
if (typeof level == "undefined")
level = Z_DEFAULT_COMPRESSION;
z.deflateInit(level);
z.next_out = buf;
that.append = function(data, onprogress) {
var err, buffers = [], lastIndex = 0, bufferIndex = 0, bufferSize = 0, array;
if (!data.length)
return;
z.next_in_index = 0;
z.next_in = data;
z.avail_in = data.length;
do {
z.next_out_index = 0;
z.avail_out = bufsize;
err = z.deflate(flush);
if (err != Z_OK)
throw new Error("deflating: " + z.msg);
if (z.next_out_index)
if (z.next_out_index == bufsize)
buffers.push(new Uint8Array(buf));
else
buffers.push(new Uint8Array(buf.subarray(0, z.next_out_index)));
bufferSize += z.next_out_index;
if (onprogress && z.next_in_index > 0 && z.next_in_index != lastIndex) {
onprogress(z.next_in_index);
lastIndex = z.next_in_index;
}
} while (z.avail_in > 0 || z.avail_out === 0);
array = new Uint8Array(bufferSize);
buffers.forEach(function(chunk) {
array.set(chunk, bufferIndex);
bufferIndex += chunk.length;
});
return array;
};
that.flush = function() {
var err, buffers = [], bufferIndex = 0, bufferSize = 0, array;
do {
z.next_out_index = 0;
z.avail_out = bufsize;
err = z.deflate(Z_FINISH);
if (err != Z_STREAM_END && err != Z_OK)
throw new Error("deflating: " + z.msg);
if (bufsize - z.avail_out > 0)
buffers.push(new Uint8Array(buf.subarray(0, z.next_out_index)));
bufferSize += z.next_out_index;
} while (z.avail_in > 0 || z.avail_out === 0);
z.deflateEnd();
array = new Uint8Array(bufferSize);
buffers.forEach(function(chunk) {
array.set(chunk, bufferIndex);
bufferIndex += chunk.length;
});
return array;
};
}
// 'zip' may not be defined in z-worker and some tests
var env = global.zip || global;
env.Deflater = env._jzlib_Deflater = Deflater;
})(this);
================================================
FILE: elements.css
================================================
/* Hide Firefox's indicator when images are loading */
img:-moz-loading {
visibility: hidden;
}
/* ------ UTILITY ---------- */
.hidden {
display: none;
}
div.tip {
color: #555;
font-weight: normal;
text-align: left;
position: relative;
display: inline-block;
font-size: 13px;
line-height: 1.45;
background: #fff;
border: 1px solid #a2a2a2;
border-radius: 6px;
padding: 8px 11px 9px 11px;
bottom: 0px;
left: -50%;
margin-right: 9px;
white-space: pre;
box-shadow: 0 0px 10px rgba(0, 0, 0, 0.15);
}
/* tail */
.tip:after, .tip:before {
top: 100%;
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none;
}
/* tail fg */
.tip:after {
border-color: rgba(136, 183, 213, 0);
border-top-color: #fff;
border-width: 8px;
left: 50%;
margin-left: -8px;
}
/* tail bg */
.tip:before {
border-color: rgba(194, 225, 245, 0);
border-top-color: #999;
border-width: 9px;
left: 50%;
margin-left: -9px;
}
.tip-button {
float: right;
margin-top: 2px;
display:inline-block;
position: relative;
z-index: 500;
cursor: pointer;
text-align: center;
font-size: 13px;
line-height: 1;
font-weight: normal;
color: #799FCB;
}
.tip-button .tip-anchor {
bottom: 24px;
left: 8px;
}
.tip-anchor {
visibility: hidden;
position: absolute;
bottom: 0;
left: 0;
}
.tip-button:hover {
font-weight: bold;
color: #033D6D;
}
.tip-button:hover * {
visibility: visible;
}
.clicktext {
display:inline-block;
line-height: 1;
cursor: pointer;
border: none;
outline: none;
padding: 0;
margin: 0;
text-decoration:none;
text-indent: 0px;
}
.file-control {
position: absolute;
top: -1000px;
}
/* -------- BUTTONS ---------- */
.header-btn {
color: #fff;
border: none;
padding: 3px 7px 4px 7px;
border-radius: 3px;
margin-top: 5px;
}
.page-header .header-btn.disabled,
.page-header .header-btn.disabled:hover {
background-color: transparent;
}
.page-header .header-btn.active {
background-color: black;
}
.btn.active {
cursor: pointer;
}
.btn.selected,
.btn.selected:hover {
color: #aaa;
background: none;
}
.btn.inline-btn:hover:not(.selected) {
background-color: #FFFCDC;
}
.btn {
text-align: center;
padding: 4px 7px 5px 7px;
border-radius: 4px;
line-height: 1;
display: inline-block;
cursor: pointer;
}
.btn.disabled {
cursor: default;
}
.dialog-btn {
display: inline-block;
margin-bottom: 4px;
margin-top: 1px;
font-size: 13px;
color: white;
min-width: 28px;
}
.inline-btn {
margin: 0;
border: 1px solid #999;
padding: 1px 4px 3px 3px;
}
.text-btn {
cursor: pointer;
}
.text-btn.disabled {
cursor: auto;
color: #999;
}
================================================
FILE: encode.js
================================================
/**
* https://github.com/giscafer/mapshaper-plus
* 对坐标数据进行加密
* @author giscafer
* @version 1.0
* @date 2016-06-04T01:48:33+0800
* 参考:https://github.com/ecomfe/echarts/blob/8eeb7e5abe207d0536c62ce1f4ddecc6adfdf85e/src/util/mapData/rawData/encode.js
*/
!(function (name, definition) {
var hasDefine = typeof define === 'funciton',
hasExports = typeof module !== 'undefined' && module.exports;
if (hasDefine) {
//AMD/CMD
define(difinition);
} else if (hasExports) {
//Node.js
module.exports = definition();
} else {
this[name] = definition();
}
})('Encoder', function () {
function Encoder() { }
Encoder.prototype.convert2Echarts = function (rawStr, fileName, type) {
var results = "";
var json = JSON.parse(rawStr);
// Meta tag
json.UTF8Encoding = true;
var features = json.features;
// console.log(json);
if (features) {
features.forEach(function (feature) {
var encodeOffsets = feature.geometry.encodeOffsets = [];
var coordinates = feature.geometry.coordinates;
if (feature.geometry.type === 'Polygon') {
coordinates.forEach(function (coordinate, idx) {
coordinates[idx] = encodePolygon(
coordinate, encodeOffsets[idx] = []
);
});
} else if (feature.geometry.type === 'MultiPolygon') {
coordinates.forEach(function (polygon, idx1) {
encodeOffsets[idx1] = [];
polygon.forEach(function (coordinate, idx2) {
coordinates[idx1][idx2] = encodePolygon(
coordinate, encodeOffsets[idx1][idx2] = []
);
});
});
}
});
} else {
var geometries = json.geometries;
geometries.forEach(function (geometry) {
var encodeOffsets = geometry.encodeOffsets = [];
var coordinates = geometry.coordinates;
if (geometry.type === 'Polygon') {
coordinates.forEach(function (coordinate, idx) {
coordinates[idx] = encodePolygon(
coordinate, encodeOffsets[idx] = []
);
});
} else if (geometry.type === 'MultiPolygon') {
coordinates.forEach(function (polygon, idx1) {
encodeOffsets[idx1] = [];
polygon.forEach(function (coordinate, idx2) {
coordinates[idx1][idx2] = encodePolygon(
coordinate, encodeOffsets[idx1][idx2] = []
);
});
});
}
});
}
if (type === 'json') {
results = JSON.stringify(json);
} else {
results = addEchartsJsWrapper(JSON.stringify(json), fileName);
}
return results;
};
function encodePolygon(coordinate, encodeOffsets) {
var result = '';
var prevX = quantize(coordinate[0][0]);
var prevY = quantize(coordinate[0][1]);
// Store the origin offset
encodeOffsets[0] = prevX;
encodeOffsets[1] = prevY;
for (var i = 0; i < coordinate.length; i++) {
var point = coordinate[i];
result += encode(point[0], prevX);
result += encode(point[1], prevY);
prevX = quantize(point[0]);
prevY = quantize(point[1]);
}
return result;
}
function addAMDWrapper(jsonStr) {
return ['define(function() {',
' return ' + jsonStr + ';',
'});'].join('\n');
}
function addEchartsJsWrapper(jsonStr, fileName) {
return ['(function (root, factory) {',
" if (typeof define === 'function' && define.amd) {",
" define(['exports', 'echarts'], factory);",
" } else if (typeof exports === 'object' && typeof exports.nodeName !== 'string') {",
" factory(exports, require('echarts'));",
" } else {",
" factory({}, root.echarts);",
" }",
" }(this, function (exports, echarts) {",
" var log = function (msg) {",
" if (typeof console !== 'undefined') {",
" console && console.error && console.error(msg);",
" }",
" }",
" if (!echarts) {",
" log('ECharts is not Loaded');",
" return;",
" }",
" if (!echarts.registerMap) {",
" log('ECharts Map is not loaded')",
" return;",
" }",
" echarts.registerMap('" + fileName + "'," + jsonStr,
' )}));'].join('\n');
}
function encode(val, prev) {
// Quantization
val = quantize(val);
// var tmp = val;
// Delta
val = val - prev;
if (((val << 1) ^ (val >> 15)) + 64 === 8232) {
//WTF, 8232 will get syntax error in js code
val--;
}
// ZigZag
val = (val << 1) ^ (val >> 15);
// add offset and get unicode
return String.fromCharCode(val + 64);
// var tmp = {'tmp' : str};
// try{
// eval("(" + JSON.stringify(tmp) + ")");
// }catch(e) {
// console.log(val + 64);
// }
}
function quantize(val) {
return Math.ceil(val * 1024);
}
return new Encoder();
});
================================================
FILE: index.html
================================================
<!DOCTYPE html>
<html>
<head>
<title>mapshaper plus</title>
<meta name="Description" content="A tool for topologically aware shape simplification. Reads and writes Shapefile, GeoJSON and TopoJSON formats." />
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="stylesheet" href="page.css">
<link rel="stylesheet" href="elements.css">
<link rel="icon"
type="image/png"
href="images/icon.png">
</head>
<body class="theme2">
<div class="hidden">
<svg version="1.1" id="home-icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
y="0px" width="14px" height="19px" viewBox="0 0 14 16">
<g>
<polygon points="13,7 13,6 12,6 12,5 11,5 11,4 10,4 10,3 9,3 9,2 8,2 8,1 6,1 6,2 5,2 5,3 4,3 4,4 3,4 3,5 2,5
2,6 1,6 1,7 0,7 0,9 2,9 2,14 6,14 6,10 8,10 8,14 12,14 12,9 14,9 14,7"/>
</g>
</svg>
<svg version="1.1" id="zoom-in-icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
y="0px" width="14px" height="21px" viewBox="0 0 14 14">
<g>
<polygon points="13,5 9,5 9,1 5,1 5,5 1,5 1,9 5,9 5,13 9,13 9,9 13,9"/>
</g>
</svg>
<svg version="1.1" id="zoom-out-icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
y="0px" width="14px" height="16px" viewBox="0 -1 14 10">
<g>
<polygon points="1,1 13,1 13,5 1,5 1,1" />
</g>
</svg>
<svg version="1.1" id="info-icon2"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="13px" height="18px"
viewBox="-510 390 13 18" xml:space="preserve">
<circle fill="#30D4EF" cx="-503.4" cy="392.8" r="2.7"/>
<rect x="-508" y="405" fill="#30D4EF" width="10" height="3"/>
<rect x="-507" y="398" fill="#30D4EF" width="6" height="3"/>
<rect x="-505" y="400" fill="#30D4EF" width="4" height="6"/>
</svg>
</div>
<div id="coordinate-info" class="colored-text selectable"></div>
<a href="https://github.com/giscafer/mapshaper-plus" target="_blank"><div id="fork-me"></div></a>
<div class="page-header">
<div class="mapshaper-logo">map<span class="logo-highlight">shaper plus</span></div>
<div id="layer-control-btn"><span class="btn header-btn layer-name"></span></div>
<div id="simplify-control-wrapper"><div id="simplify-control"><div class="header-btn btn" id="simplify-settings-btn">Settings</div>
<div class="slider">
<div class="handle"><img src="images/slider_handle_v1.png" alt=""/></div>
<div class="track"></div>
</div>
<input type="text" value="label" class="clicktext" />
</div></div>
<div id="mode-buttons">
<span id="simplify-btn" class="header-btn btn">Simplify</span><span class="separator"></span><span id="console-btn" class="header-btn btn">Console</span><span class="separator"></span><span id="export-btn" class="header-btn btn">Export</span>
</div>
</div>
<div id="mshp-not-supported" class="main-area">
<div class="info-box">
<h3>Unfortunately, mapshaper can't run in <span class="unsupported-browser">this web browser</span></h3>
<div>For best results, try <a href="https://www.google.com/chrome/browser/desktop/">Google Chrome</a> or <a href="http://www.mozilla.org/en-US/firefox/new/">Mozilla Firefox</a>.</div>
</div>
</div>
<div id="layer-control" class="main-area popup-dialog">
<div class="info-box">
<div id="layer-menu">
<h3>Layers</h3>
<div class="layer-list"></div>
<div><div id="add-file-btn" class="dialog-btn btn">Add a file</div></div>
</div>
</div>
</div>
<div id="export-options" class="main-area popup-dialog">
<div class="info-box">
<h3>Export menu</h3>
<div style="height:3px"></div>
<div id="export-layers">
<h4>Layers</h4>
<div id="export-layer-list" class="option-menu"></div>
</div>
<h4>File format</h4>
<div id="export-formats" class="option-menu">
</div>
<div class="option-menu"><input type="text" class="advanced-options" placeholder="command line options" /><div class="tip-button">?<div class="tip-anchor">
<div class="tip">Enter options from the command line
interface. Examples: "bbox" "no-quantization"
"precision=0.001"</div></div></div></div>
<div class="cancel-btn btn dialog-btn">Cancel</div>
<div id="save-btn" class="btn dialog-btn">Export</div>
</div>
</div>
<div id="simplify-options" class="main-area popup-dialog">
<div class="info-box">
<h3>Simplification menu</h3>
<div class="option-menu">
<div><label for="import-retain-opt"><input type="checkbox" class="checkbox" id="import-retain-opt"/>prevent shape removal</label>
<div class="tip-button">?<div class="tip-anchor">
<div class="tip">Prevent small polygon features from
disappearing at high simplification. Keeps
the largest ring of multi-ring features.
</div></div></div></div>
<div id="planar-opt-wrapper"><label for="planar-opt"><input type="checkbox" class="checkbox" id="planar-opt"/>use planar geometry</label>
<div class="tip-button">?<div class="tip-anchor">
<div class="tip">Interpret x, y values as Cartesian coordinates
on a plane, rather than longitude, latitude
coordinates on a sphere.
</div></div></div></div>
</div>
<h4>Method</h4>
<div class="option-menu">
<div><label><input type="radio" name="method" value="dp" class="radio">Douglas-Peucker</label><div class="tip-button">?<div class="tip-anchor">
<div class="tip">Simplified lines remain within a set
distance of original lines. Good for
thinning dense points, but spikes
tend to form at high simplification.</div></div></div>
</div>
<div><label><input type="radio" name="method" value="visvalingam" class="radio">Visvalingam / effective area</label><div class="tip-button">?<div class="tip-anchor">
<div class="tip">Lines are simplified by iteratively
removing the point that forms
the least-area triangle with two
adjacent points.</div></div></div>
</div>
<div><label><input type="radio" name="method" value="weighted_visvalingam" class="radio">Visvalingam / weighted area</label><div class="tip-button">?<div class="tip-anchor">
<div class="tip">Points located at the vertex
of more acute angles are
preferentially removed, for
a smoother appearance.</div></div></div></div>
</div> <!-- option menu -->
<div>
<div class="cancel-btn btn dialog-btn">Cancel</div>
<div class="submit-btn btn dialog-btn">Apply</div>
</div>
</div> <!-- .info-box -->
</div> <!-- #simplify-options -->
<div id="import-options" class="main-area popup-dialog">
<div class="info-box">
<div id="import-intro">
<h3>Edit a file</h3>
<p>Drag and drop or <span class="inline-btn btn" id="file-selection-btn"><span class="label-text">select</span></span> one or more files
to import. Shapefile, GeoJSON, TopoJSON
and Zip files are supported.
</p>
</div>
<h4>Import options</h4>
<div class="option-menu">
<div><label for="repair-intersections-opt"><input type="checkbox" checked class="checkbox" id="repair-intersections-opt"/>detect line intersections</label>
<div class="tip-button">?<div class="tip-anchor">
<div class="tip">Detect line intersections, including
self-intersections, to help identify
topological errors in a dataset.</div></div></div></div>
<div><label for="snap-points-opt"><input type="checkbox" class="checkbox" id="snap-points-opt" />snap vertices</label>
<div class="tip-button">?<div class="tip-anchor">
<div class="tip">Fix topology errors by snapping
together points with nearly identical
coordinates. This option does not
apply to TopoJSON files.</div></div></div></div>
<div style="height:5px"></div>
<div><input type="text" class="advanced-options" placeholder="command line options" /><div class="tip-button">?<div class="tip-anchor">
<div class="tip">Enter options from the command line
interface. Examples: "no-topology"
"encoding=big5"</div></div></div></div>
</div>
<div id="dropped-file-list">
<h3>Files</h3>
<div class="file-list"></div>
</div>
<div id="import-buttons" class="hidden">
<div class="cancel-btn btn dialog-btn">Cancel</div>
<div class="add-btn btn dialog-btn">Add file</div>
<div class="submit-btn btn dialog-btn">Import</div>
</div>
</div> <!-- .info-box -->
</div> <!-- import-options -->
<!-- TODO: remove #mshp-main-page without causing the map to jitter when resized -->
<div id="mshp-main-page">
<div id="console" class="main-area">
<div id="console-window"><div id="console-buffer" class="selectable"></div></div>
</div>
<div id="mshp-main-map" class="main-area">
<div id="intersection-display">
<div id="intersection-count">0 line intersections</div>
<div id="repair-btn" class="text-btn colored-text">Repair</div>
</div>
<div id="map-layers"></div>
</div>
</div>
<script src="encode.js" type="text/javascript"></script>
<script src="zip.js" type="text/javascript"></script>
<script src="mapshaper.js" type="text/javascript"></script>
<script src="manifest.js" type="text/javascript"></script>
<script src="mapshaper-gui.js" type="text/javascript"></script>
</body>
</html>
================================================
FILE: manifest.js
================================================
/* replaced by a file manifest by mapshaper-gui server */
================================================
FILE: mapshaper-gui.js
================================================
(function(){
var api = mapshaper; // assuming mapshaper is in global scope
var utils = api.utils;
var gui = api.gui = {};
var cli = api.cli;
var geom = api.geom;
var MapShaper = api.internal;
var Bounds = api.internal.Bounds;
var APIError = api.internal.APIError;
var message = api.internal.message;
// Replace error function in mapshaper lib
var error = MapShaper.error = function() {
stop.apply(null, utils.toArray(arguments));
};
// replace stop function
var stop = MapShaper.stop = function() {
// Show a popup error message, then throw an error
var msg = gui.formatMessageArgs(arguments);
gui.alert(msg);
throw new Error(msg);
};
function Handler(type, target, callback, listener, priority) {
this.type = type;
this.callback = callback;
this.listener = listener || null;
this.priority = priority || 0;
this.target = target;
}
Handler.prototype.trigger = function(evt) {
if (!evt) {
evt = new EventData(this.type);
evt.target = this.target;
} else if (evt.target != this.target || evt.type != this.type) {
error("[Handler] event target/type have changed.");
}
this.callback.call(this.listener, evt);
}
function EventData(type, target, data) {
this.type = type;
this.target = target;
if (data) {
utils.defaults(this, data);
this.data = data;
}
}
EventData.prototype.stopPropagation = function() {
this.__stop__ = true;
};
// Base class for objects that dispatch events
function EventDispatcher() {}
// @obj (optional) data object, gets mixed into event
// @listener (optional) dispatch event only to this object
EventDispatcher.prototype.dispatchEvent = function(type, obj, listener) {
var evt;
// TODO: check for bugs if handlers are removed elsewhere while firing
var handlers = this._handlers;
if (handlers) {
for (var i = 0, len = handlers.length; i < len; i++) {
var handler = handlers[i];
if (handler.type == type && (!listener || listener == handler.listener)) {
if (!evt) {
evt = new EventData(type, this, obj);
}
else if (evt.__stop__) {
break;
}
handler.trigger(evt);
}
}
}
};
EventDispatcher.prototype.addEventListener =
EventDispatcher.prototype.on = function(type, callback, context, priority) {
context = context || this;
priority = priority || 0;
var handler = new Handler(type, this, callback, context, priority);
// Insert the new event in the array of handlers according to its priority.
var handlers = this._handlers || (this._handlers = []);
var i = handlers.length;
while (--i >= 0 && handlers[i].priority < handler.priority) {}
handlers.splice(i+1, 0, handler);
return this;
};
// Remove an event handler.
// @param {string} type Event type to match.
// @param {function(BoundEvent)} callback Event handler function to match.
// @param {*=} context Execution context of the event handler to match.
// @return {number} Returns number of handlers removed (expect 0 or 1).
EventDispatcher.prototype.removeEventListener = function(type, callback, context) {
context = context || this;
var count = this.removeEventListeners(type, callback, context);
return count;
};
// Remove event handlers; passing arguments can limit which listeners to remove
// Returns nmber of handlers removed.
EventDispatcher.prototype.removeEventListeners = function(type, callback, context) {
var handlers = this._handlers;
var newArr = [];
var count = 0;
for (var i = 0; handlers && i < handlers.length; i++) {
var evt = handlers[i];
if ((!type || type == evt.type) &&
(!callback || callback == evt.callback) &&
(!context || context == evt.listener)) {
count += 1;
}
else {
newArr.push(evt);
}
}
this._handlers = newArr;
return count;
};
EventDispatcher.prototype.countEventListeners = function(type) {
var handlers = this._handlers,
len = handlers && handlers.length || 0,
count = 0;
if (!type) return len;
for (var i = 0; i < len; i++) {
if (handlers[i].type === type) count++;
}
return count;
};
var Env = (function() {
var inNode = typeof module !== 'undefined' && !!module.exports;
var inBrowser = typeof window !== 'undefined' && !inNode;
var inPhantom = inBrowser && !!(window.phantom && window.phantom.exit);
var ieVersion = inBrowser && /MSIE ([0-9]+)/.exec(navigator.appVersion) && parseInt(RegExp.$1) || NaN;
return {
iPhone : inBrowser && !!(navigator.userAgent.match(/iPhone/i)),
iPad : inBrowser && !!(navigator.userAgent.match(/iPad/i)),
canvas: inBrowser && !!document.createElement('canvas').getContext,
inNode : inNode,
inPhantom : inPhantom,
inBrowser: inBrowser,
ieVersion: ieVersion,
ie: !isNaN(ieVersion)
};
})();
var Browser = {
getPageXY: function(el) {
var x = 0, y = 0;
if (el.getBoundingClientRect) {
var box = el.getBoundingClientRect();
x = box.left - Browser.pageXToViewportX(0);
y = box.top - Browser.pageYToViewportY(0);
}
else {
var fixed = Browser.elementIsFixed(el);
while (el) {
x += el.offsetLeft || 0;
y += el.offsetTop || 0;
el = el.offsetParent;
}
if (fixed) {
var offsX = -Browser.pageXToViewportX(0);
var offsY = -Browser.pageYToViewportY(0);
x += offsX;
y += offsY;
}
}
var obj = {x:x, y:y};
return obj;
},
elementIsFixed: function(el) {
// get top-level offsetParent that isn't body (cf. Firefox)
var body = document.body;
while (el && el != body) {
var parent = el;
el = el.offsetParent;
}
// Look for position:fixed in the computed style of the top offsetParent.
// var styleObj = parent && (parent.currentStyle || window.getComputedStyle && window.getComputedStyle(parent, '')) || {};
var styleObj = parent && Browser.getElementStyle(parent) || {};
return styleObj['position'] == 'fixed';
},
pageXToViewportX: function(x) {
return x - window.pageXOffset;
},
pageYToViewportY: function(y) {
return y - window.pageYOffset;
},
getElementStyle: function(el) {
return el.currentStyle || window.getComputedStyle && window.getComputedStyle(el, '') || {};
},
getClassNameRxp: function(cname) {
return new RegExp("(^|\\s)" + cname + "(\\s|$)");
},
hasClass: function(el, cname) {
var rxp = this.getClassNameRxp(cname);
return el && rxp.test(el.className);
},
addClass: function(el, cname) {
var classes = el.className;
if (!classes) {
classes = cname;
}
else if (!this.hasClass(el, cname)) {
classes = classes + ' ' + cname;
}
el.className = classes;
},
removeClass: function(el, cname) {
var rxp = this.getClassNameRxp(cname);
el.className = el.className.replace(rxp, "$2");
},
replaceClass: function(el, c1, c2) {
var r1 = this.getClassNameRxp(c1);
el.className = el.className.replace(r1, '$1' + c2 + '$2');
},
mergeCSS: function(s1, s2) {
var div = this._cssdiv;
if (!div) {
div = this._cssdiv = document.createElement('div');
}
div.style.cssText = s1 + ";" + s2; // extra ';' for ie, which may leave off final ';'
return div.style.cssText;
},
addCSS: function(el, css) {
el.style.cssText = Browser.mergeCSS(el.style.cssText, css);
},
// Return: HTML node reference or null
// Receive: node reference or id or "#" + id
getElement: function(ref) {
var el;
if (typeof ref == 'string') {
if (ref.charAt(0) == '#') {
ref = ref.substr(1);
}
if (ref == 'body') {
el = document.getElementsByTagName('body')[0];
}
else {
el = document.getElementById(ref);
}
}
else if (ref && ref.nodeType !== void 0) {
el = ref;
}
return el || null;
},
undraggable: function(el) {
el.ondragstart = function(){return false;};
el.draggable = false;
}
};
Browser.onload = function(handler) {
if (document.readyState == 'complete') {
handler();
} else {
window.addEventListener('load', handler);
}
};
// See https://github.com/janl/mustache.js/blob/master/mustache.js
utils.htmlEscape = (function() {
var entityMap = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": ''',
'/': '/'
};
return function(s) {
return String(s).replace(/[&<>"'\/]/g, function(s) {
return entityMap[s];
});
};
}());
var classSelectorRE = /^\.([\w-]+)$/,
idSelectorRE = /^#([\w-]+)$/,
tagSelectorRE = /^[\w-]+$/,
tagOrIdSelectorRE = /^#?[\w-]+$/;
function Elements(sel) {
if ((this instanceof Elements) == false) {
return new Elements(sel);
}
this.elements = [];
this.select(sel);
this.tmp = new El();
}
Elements.prototype = {
size: function() {
return this.elements.length;
},
select: function(sel) {
this.elements = Elements.__select(sel);
return this;
},
addClass: function(className) {
this.forEach(function(el) { el.addClass(className); });
return this;
},
removeClass: function(className) {
this.forEach(function(el) { el.removeClass(className); })
return this;
},
forEach: function(callback, ctx) {
var tmp = this.tmp;
for (var i=0, len=this.elements.length; i<len; i++) {
tmp.el = this.elements[i];
callback.call(ctx, tmp, i);
}
return this;
}
};
Elements.__select = function(selector, root) {
root = root || document;
var els;
if (classSelectorRE.test(selector)) {
els = Elements.__getElementsByClassName(RegExp.$1, root);
}
else if (tagSelectorRE.test(selector)) {
els = root.getElementsByTagName(selector);
}
else if (document.querySelectorAll) {
try {
els = root.querySelectorAll(selector)
} catch (e) {
error("Invalid selector:", selector);
}
} else {
error("This browser doesn't support CSS query selectors");
}
return utils.toArray(els);
}
Elements.__getElementsByClassName = function(cname, node) {
if (node.getElementsByClassName) {
return node.getElementsByClassName(cname);
}
var a = [];
var re = new RegExp('(^| )'+cname+'( |$)');
var els = node.getElementsByTagName("*");
for (var i=0, j=els.length; i<j; i++)
if (re.test(els[i].className)) a.push(els[i]);
return a;
};
// Converts dash-separated names (e.g. background-color) to camelCase (e.g. backgroundColor)
// Doesn't change names that are already camelCase
//
El.toCamelCase = function(str) {
var cc = str.replace(/-([a-z])/g, function (g) { return g[1].toUpperCase() });
return cc;
};
El.fromCamelCase = function(str) {
var dashed = str.replace(/([A-Z])/g, "-$1").toLowerCase();
return dashed;
};
El.setStyle = function(el, name, val) {
var jsName = El.toCamelCase(name);
if (el.style[jsName] == void 0) {
trace("[Element.setStyle()] css property:", jsName);
return;
}
var cssVal = val;
if (isFinite(val)) {
cssVal = String(val); // problem if converted to scientific notation
if (jsName != 'opacity' && jsName != 'zIndex') {
cssVal += "px";
}
}
el.style[jsName] = cssVal;
}
El.findAll = function(sel, root) {
return Elements.__select(sel, root);
};
function El(ref) {
if (!ref) error("Element() needs a reference");
if (ref instanceof El) {
return ref;
}
else if (this instanceof El === false) {
return new El(ref);
}
var node;
if (utils.isString(ref)) {
if (El.isHTML(ref)) {
var parent = El('div').html(ref).node();
node = parent.childNodes.length == 1 ? parent.childNodes[0] : parent;
} else if (tagOrIdSelectorRE.test(ref)) {
node = Browser.getElement(ref) || document.createElement(ref); // TODO: detect type of argument
} else {
node = Elements.__select(ref)[0];
}
} else if (ref.tagName) {
node = ref;
}
if (!node) error("Unmatched element selector:", ref);
this.el = node;
}
utils.inherit(El, EventDispatcher); //
El.removeAll = function(sel) {
var arr = Elements.__select(sel);
utils.forEach(arr, function(el) {
El(el).remove();
});
};
El.isHTML = function(str) {
return str && str[0] == '<'; // TODO: improve
};
utils.extend(El.prototype, {
clone: function() {
var el = this.el.cloneNode(true);
if (el.nodeName == 'SCRIPT') {
// Assume scripts are templates and convert to divs, so children
// can
el = El('div').addClass(el.className).html(el.innerHTML).node();
}
el.id = utils.getUniqueName();
this.el = el;
return this;
},
node: function() {
return this.el;
},
width: function() {
return this.el.offsetWidth;
},
height: function() {
return this.el.offsetHeight;
},
top: function() {
return this.el.offsetTop;
},
left: function() {
return this.el.offsetLeft;
},
// Apply inline css styles to this Element, either as string or object.
//
css: function(css, val) {
if (val != null) {
El.setStyle(this.el, css, val);
}
else if (utils.isString(css)) {
Browser.addCSS(this.el, css);
}
else if (utils.isObject(css)) {
utils.forEachProperty(css, function(val, key) {
El.setStyle(this.el, key, val);
}, this);
}
return this;
},
attr: function(obj, value) {
if (utils.isString(obj)) {
if (arguments.length == 1) {
return this.el.getAttribute(obj);
}
this.el[obj] = value;
}
else if (!value) {
Opts.copyAllParams(this.el, obj);
}
return this;
},
remove: function(sel) {
this.el.parentNode && this.el.parentNode.removeChild(this.el);
return this;
},
addClass: function(className) {
Browser.addClass(this.el, className);
return this;
},
removeClass: function(className) {
Browser.removeClass(this.el, className);
return this;
},
classed: function(className, b) {
this[b ? 'addClass' : 'removeClass'](className);
return this;
},
hasClass: function(className) {
return Browser.hasClass(this.el, className);
},
toggleClass: function(cname) {
if (this.hasClass(cname)) {
this.removeClass(cname);
} else {
this.addClass(cname);
}
},
computedStyle: function() {
return Browser.getElementStyle(this.el);
},
visible: function() {
if (this._hidden !== undefined) {
return !this._hidden;
}
var style = this.computedStyle();
return style.display != 'none' && style.visibility != 'hidden';
},
showCSS: function(css) {
if (!css) {
return this._showCSS || "display:block;";
}
this._showCSS = css;
return this;
},
hideCSS: function(css) {
if (!css) {
return this._hideCSS || "display:none;";
}
this._hideCSS = css;
return this;
},
hide: function(css) {
if (this.visible()) {
this.css(css || this.hideCSS());
this._hidden = true;
}
return this;
},
show: function(css) {
if (!this.visible()) {
this.css(css || this.showCSS());
this._hidden = false;
}
return this;
},
html: function(html) {
if (arguments.length == 0) {
return this.el.innerHTML;
} else {
this.el.innerHTML = html;
return this;
}
},
text: function(str) {
this.html(utils.htmlEscape(str));
return this;
},
// Shorthand for attr('id', <name>)
id: function(id) {
if (id) {
this.el.id = id;
return this;
}
return this.el.id;
},
findChild: function(sel) {
var node = Elements.__select(sel, this.el)[0];
if (!node) error("Unmatched selector:", sel);
return new El(node);
},
appendTo: function(ref) {
var parent = ref instanceof El ? ref.el : Browser.getElement(ref);
if (this._sibs) {
for (var i=0, len=this._sibs.length; i<len; i++) {
parent.appendChild(this._sibs[i]);
}
}
parent.appendChild(this.el);
return this;
},
nextSibling: function() {
return this.el.nextSibling ? new El(this.el.nextSibling) : null;
},
newSibling: function(tagName) {
var el = this.el,
sib = document.createElement(tagName),
e = new El(sib),
par = el.parentNode;
if (par) {
el.nextSibling ? par.insertBefore(sib, el.nextSibling) : par.appendChild(sib);
} else {
e._sibs = this._sibs || [];
e._sibs.push(el);
}
return e;
},
firstChild: function() {
var ch = this.el.firstChild;
while (ch.nodeType != 1) { // skip text nodes
ch = ch.nextSibling;
}
return new El(ch);
},
appendChild: function(ref) {
var el = El(ref);
this.el.appendChild(el.el);
return this;
},
newChild: function(tagName) {
var ch = document.createElement(tagName);
this.el.appendChild(ch);
return new El(ch);
},
// Traverse to parent node
parent: function() {
var p = this.el && this.el.parentNode;
return p ? new El(p) : null;
},
findParent: function(tagName) {
var p = this.el && this.el.parentNode;
if (tagName) {
tagName = tagName.toUpperCase();
while (p && p.tagName != tagName) {
p = p.parentNode;
}
}
return p ? new El(p) : null;
},
// Remove all children of this element
//
empty: function() {
this.el.innerHTML = '';
return this;
}
});
// use DOM handler for certain events
// TODO: find a better way distinguising DOM events and other events registered on El
// e.g. different methods
//
//El.prototype.__domevents = utils.arrayToIndex("click,mousedown,mousemove,mouseup".split(','));
El.prototype.__on = El.prototype.on;
El.prototype.on = function(type, func, ctx) {
if (ctx) {
error("[El#on()] Third argument no longer supported.");
}
if (this.constructor == El) {
this.el.addEventListener(type, func);
} else {
this.__on.apply(this, arguments);
}
return this;
};
El.prototype.__removeEventListener = El.prototype.removeEventListener;
El.prototype.removeEventListener = function(type, func) {
if (this.constructor == El) {
this.el.removeEventListener(type, func);
} else {
this.__removeEventListener.apply(this, arguments);
}
return this;
};
function ElementPosition(ref) {
var self = this,
el = El(ref),
pageX = 0,
pageY = 0,
width = 0,
height = 0;
el.on('mouseover', update);
window.onorientationchange && window.addEventListener('orientationchange', update);
window.addEventListener('scroll', update);
window.addEventListener('resize', update);
// trigger an update, e.g. when map container is resized
this.update = function() {
update();
};
this.resize = function(w, h) {
el.css('width', w).css('height', h);
update();
};
this.width = function() { return width };
this.height = function() { return height };
this.position = function() {
return {
element: el.node(),
pageX: pageX,
pageY: pageY,
width: width,
height: height
};
};
function update() {
var div = el.node(),
xy = Browser.getPageXY(div),
w = div.clientWidth,
h = div.clientHeight,
x = xy.x,
y = xy.y,
resized = w != width || h != height,
moved = x != pageX || y != pageY;
if (resized || moved) {
pageX = x, pageY = y, width = w, height = h;
self.dispatchEvent('change', self.position());
if (resized) {
self.dispatchEvent('resize', self.position());
}
}
}
update();
}
utils.inherit(ElementPosition, EventDispatcher);
function getTimerFunction() {
return typeof requestAnimationFrame == 'function' ?
requestAnimationFrame : function(cb) {setTimeout(cb, 25);};
}
function Timer() {
var self = this,
running = false,
busy = false,
tickTime, startTime, duration;
this.start = function(ms) {
var now = +new Date();
duration = ms || Infinity;
startTime = now;
running = true;
if (!busy) startTick(now);
};
this.stop = function() {
running = false;
};
function startTick(now) {
busy = true;
tickTime = now;
getTimerFunction()(onTick);
}
function onTick() {
var now = +new Date(),
elapsed = now - startTime,
pct = Math.min((elapsed + 10) / duration, 1),
done = pct >= 1;
if (!running) { // interrupted
busy = false;
return;
}
if (done) running = false;
self.dispatchEvent('tick', {
elapsed: elapsed,
pct: pct,
done: done,
time: now,
tickTime: now - tickTime
});
busy = false;
if (running) startTick(now);
}
}
utils.inherit(Timer, EventDispatcher);
function Tween(ease) {
var self = this,
timer = new Timer(),
start, end;
timer.on('tick', onTick);
this.start = function(a, b, duration) {
start = a;
end = b;
timer.start(duration || 500);
};
function onTick(e) {
var pct = ease ? ease(e.pct) : e.pct,
val = end * pct + start * (1 - pct);
self.dispatchEvent('change', {value: val});
}
}
utils.inherit(Tween, EventDispatcher);
Tween.sineInOut = function(n) {
return 0.5 - Math.cos(n * Math.PI) / 2;
};
Tween.quadraticOut = function(n) {
return 1 - Math.pow((1 - n), 2);
};
// @mouse: MouseArea object
function MouseWheel(mouse) {
var self = this,
prevWheelTime = 0,
currDirection = 0,
timer = new Timer().addEventListener('tick', onTick),
sustainTime = 60,
fadeTime = 80;
if (window.onmousewheel !== undefined) { // ie, webkit
window.addEventListener('mousewheel', handleWheel);
} else { // firefox
window.addEventListener('DOMMouseScroll', handleWheel);
}
function handleWheel(evt) {
var direction;
if (evt.wheelDelta) {
direction = evt.wheelDelta > 0 ? 1 : -1;
} else if (evt.detail) {
direction = evt.detail > 0 ? -1 : 1;
}
if (!mouse.isOver() || !direction) return;
evt.preventDefault();
prevWheelTime = +new Date();
if (!currDirection) {
self.dispatchEvent('mousewheelstart');
}
currDirection = direction;
timer.start(sustainTime + fadeTime);
}
function onTick(evt) {
var elapsed = evt.time - prevWheelTime,
fadeElapsed = elapsed - sustainTime,
scale = evt.tickTime / 25,
obj;
if (evt.done) {
currDirection = 0;
} else {
if (fadeElapsed > 0) {
// Decelerate if the timer fires during 'fade time' (for smoother zooming)
scale *= Tween.quadraticOut((fadeTime - fadeElapsed) / fadeTime);
}
obj = utils.extend({direction: currDirection, multiplier: scale}, mouse.mouseData());
self.dispatchEvent('mousewheel', obj);
}
}
}
utils.inherit(MouseWheel, EventDispatcher);
function MouseArea(element) {
var pos = new ElementPosition(element),
_areaPos = pos.position(),
_self = this,
_dragging = false,
_isOver = false,
_prevEvt,
// _moveEvt,
_downEvt;
pos.on('change', function() {_areaPos = pos.position()});
// TODO: think about touch events
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mousedown', onMouseDown);
document.addEventListener('mouseup', onMouseUp);
element.addEventListener('mouseover', onAreaEnter);
element.addEventListener('mousemove', onAreaEnter);
element.addEventListener('mouseout', onAreaOut);
element.addEventListener('mousedown', onAreaDown);
element.addEventListener('dblclick', onAreaDblClick);
function onAreaDown(e) {
e.preventDefault(); // prevent text selection cursor on drag
}
function onAreaEnter() {
if (!_isOver) {
_isOver = true;
_self.dispatchEvent('enter');
}
}
function onAreaOut(e) {
_isOver = false;
_self.dispatchEvent('leave');
}
function onMouseUp(e) {
var evt = procMouseEvent(e),
elapsed, dx, dy;
if (_dragging) {
_dragging = false;
_self.dispatchEvent('dragend', evt);
}
if (_downEvt) {
elapsed = evt.time - _downEvt.time;
dx = evt.pageX - _downEvt.pageX;
dy = evt.pageY - _downEvt.pageY;
if (_isOver && elapsed < 500 && Math.sqrt(dx * dx + dy * dy) < 6) {
_self.dispatchEvent('click', evt);
}
_downEvt = null;
}
}
function onMouseDown(e) {
if (e.button != 2 && e.which != 3) { // ignore right-click
_downEvt = procMouseEvent(e);
}
}
function onMouseMove(e) {
var evt = procMouseEvent(e);
if (!_dragging && _downEvt && _downEvt.hover) {
_dragging = true;
_self.dispatchEvent('dragstart', evt);
}
if (_dragging) {
var obj = {
dragX: evt.pageX - _downEvt.pageX,
dragY: evt.pageY - _downEvt.pageY
};
_self.dispatchEvent('drag', utils.extend(obj, evt));
} else {
_self.dispatchEvent('hover', evt);
}
}
function onAreaDblClick(e) {
if (_isOver) _self.dispatchEvent('dblclick', procMouseEvent(e));
}
function procMouseEvent(e) {
var pageX = e.pageX,
pageY = e.pageY,
prev = _prevEvt;
_prevEvt = {
shiftKey: e.shiftKey,
time: +new Date,
pageX: pageX,
pageY: pageY,
hover: _isOver,
x: pageX - _areaPos.pageX,
y: pageY - _areaPos.pageY,
dx: prev ? pageX - prev.pageX : 0,
dy: prev ? pageY - prev.pageY : 0
};
return _prevEvt;
}
this.isOver = function() {
return _isOver;
}
this.isDown = function() {
return !!_downEvt;
}
this.mouseData = function() {
return utils.extend({}, _prevEvt);
}
}
utils.inherit(MouseArea, EventDispatcher);
function ErrorMessages(model) {
var el;
model.addMode('alert', function() {}, turnOff);
function turnOff() {
if (el) {
el.remove();
el = null;
}
}
return function(str) {
var infoBox;
if (el) return;
el = El('div').appendTo('body').addClass('error-wrapper');
infoBox = El('div').appendTo(el).addClass('error-box info-box');
El('p').addClass('error-message').appendTo(infoBox).html(str);
El('div').addClass("btn dialog-btn").appendTo(infoBox).html('close').on('click', model.clearMode);
model.enterMode('alert');
};
}
api.enableLogging();
gui.browserIsSupported = function() {
return typeof ArrayBuffer != 'undefined' &&
typeof Blob != 'undefined' && typeof File != 'undefined';
};
gui.formatMessageArgs = function(args) {
// remove cli annotation (if present)
return MapShaper.formatLogArgs(args).replace(/^\[[^\]]+\] ?/, '');
};
gui.handleDirectEvent = function(cb) {
return function(e) {
if (e.target == this) cb();
};
};
gui.getInputElement = function() {
var el = document.activeElement;
return (el && (el.tagName == 'INPUT' || el.contentEditable == 'true')) ? el : null;
};
gui.selectElement = function(el) {
var range = document.createRange(),
sel = getSelection();
range.selectNodeContents(el);
sel.removeAllRanges();
sel.addRange(range);
};
gui.blurActiveElement = function() {
var el = gui.getInputElement();
if (el) el.blur();
};
// Filter out delayed click events, e.g. so users can highlight and copy text
gui.onClick = function(el, cb) {
var time;
el.on('mousedown', function() {
time = +new Date();
});
el.on('mouseup', function(e) {
if (+new Date() - time < 300) cb(e);
});
};
// TODO: switch all ClickText to ClickText2
// @ref Reference to an element containing a text node
function ClickText2(ref) {
var self = this;
var selected = false;
var el = El(ref).on('mousedown', init);
function init() {
el.removeEventListener('mousedown', init);
el.attr('contentEditable', true)
.attr('spellcheck', false)
.attr('autocorrect', false)
.on('focus', function(e) {
el.addClass('editing');
selected = false;
}).on('blur', function(e) {
el.removeClass('editing');
self.dispatchEvent('change');
getSelection().removeAllRanges();
}).on('keydown', function(e) {
if (e.keyCode == 13) { // enter
e.stopPropagation();
e.preventDefault();
this.blur();
}
}).on('click', function(e) {
if (!selected && getSelection().isCollapsed) {
gui.selectElement(el.node());
}
selected = true;
e.stopPropagation();
});
}
this.value = function(str) {
if (utils.isString(str)) {
el.node().textContent = str;
} else {
return el.node().textContent;
}
};
}
utils.inherit(ClickText2, EventDispatcher);
// @ref reference to a text input element
function ClickText(ref) {
var _el = El(ref);
var _self = this;
var _max = Infinity,
_min = -Infinity,
_formatter = function(v) {return String(v);},
_validator = function(v) {return !isNaN(v);},
_parser = function(s) {return parseFloat(s);},
_value = 0;
_el.on('blur', onblur);
_el.on('keydown', onpress);
function onpress(e) {
if (e.keyCode == 27) { // esc
_self.value(_value); // reset input field to current value
_el.el.blur();
} else if (e.keyCode == 13) { // enter
_el.el.blur();
}
}
// Validate input contents.
// Update internal value and fire 'change' if valid
//
function onblur() {
var val = _parser(_el.el.value);
if (val === _value) {
// return;
}
if (_validator(val)) {
_self.value(val);
_self.dispatchEvent('change', {value:_self.value()});
} else {
_self.value(_value);
_self.dispatchEvent('error'); // TODO: improve
}
}
this.bounds = function(min, max) {
_min = min;
_max = max;
return this;
};
this.validator = function(f) {
_validator = f;
return this;
};
this.formatter = function(f) {
_formatter = f;
return this;
};
this.parser = function(f) {
_parser = f;
return this;
};
this.value = function(arg) {
if (arg == void 0) {
// var valStr = this.el.value;
// return _parser ? _parser(valStr) : parseFloat(valStr);
return _value;
}
var val = utils.clamp(arg, _min, _max);
if (!_validator(val)) {
error("ClickText#value() invalid value:", arg);
} else {
_value = val;
}
_el.el.value = _formatter(val);
return this;
};
}
utils.inherit(ClickText, EventDispatcher);
function Checkbox(ref) {
var _el = El(ref);
}
utils.inherit(Checkbox, EventDispatcher);
function SimpleButton(ref) {
var _el = El(ref),
_self = this,
_active = !_el.hasClass('disabled');
_el.on('click', function(e) {
if (_active) _self.dispatchEvent('click');
return false;
});
this.active = function(a) {
if (a === void 0) return _active;
if (a !== _active) {
_active = a;
_el.toggleClass('disabled');
}
return this;
};
}
utils.inherit(SimpleButton, EventDispatcher);
function ModeButton(el, name, model) {
var btn = El(el),
active = false;
model.on('mode', function(e) {
active = e.name == name;
if (active) {
btn.addClass('active');
} else {
btn.removeClass('active');
}
});
btn.on('click', function() {
model.enterMode(active ? null : name);
});
}
function draggable(ref) {
var xdown, ydown;
var el = El(ref),
dragging = false,
obj = new EventDispatcher();
Browser.undraggable(el.node());
el.on('mousedown', function(e) {
xdown = e.pageX;
ydown = e.pageY;
window.addEventListener('mousemove', onmove);
window.addEventListener('mouseup', onrelease);
});
function onrelease(e) {
window.removeEventListener('mousemove', onmove);
window.removeEventListener('mouseup', onrelease);
if (dragging) {
dragging = false;
obj.dispatchEvent('dragend');
}
}
function onmove(e) {
if (!dragging) {
dragging = true;
obj.dispatchEvent('dragstart');
}
obj.dispatchEvent('drag', {dx: e.pageX - xdown, dy: e.pageY - ydown});
}
return obj;
}
function Slider(ref, opts) {
var _el = El(ref);
var _self = this;
var defaults = {
space: 7
};
opts = utils.extend(defaults, opts);
var _pct = 0;
var _track,
_handle,
_handleLeft = opts.space;
function size() {
return _track ? _track.width() - opts.space * 2 : 0;
}
this.track = function(ref) {
if (ref && !_track) {
_track = El(ref);
_handleLeft = _track.el.offsetLeft + opts.space;
updateHandlePos();
}
return _track;
};
this.handle = function(ref) {
var startX;
if (ref && !_handle) {
_handle = El(ref);
draggable(_handle)
.on('drag', function(e) {
setHandlePos(startX + e.dx, true);
})
.on('dragstart', function(e) {
startX = position();
_self.dispatchEvent('start');
})
.on('dragend', function(e) {
_self.dispatchEvent('end');
});
updateHandlePos();
}
return _handle;
};
function position() {
return Math.round(_pct * size());
}
this.pct = function(pct) {
if (pct >= 0 && pct <= 1) {
_pct = pct;
updateHandlePos();
}
return _pct;
};
function setHandlePos(x, fire) {
x = utils.clamp(x, 0, size());
var pct = x / size();
if (pct != _pct) {
_pct = pct;
_handle.css('left', _handleLeft + x);
_self.dispatchEvent('change', {pct: _pct});
}
}
function updateHandlePos() {
var x = _handleLeft + Math.round(position());
if (_handle) _handle.css('left', x);
}
}
utils.inherit(Slider, EventDispatcher);
var SimplifyControl = function(model) {
var control = new EventDispatcher();
var _value = 1;
var el = El('#simplify-control-wrapper');
var menu = El('#simplify-options');
var slider, text;
new SimpleButton('#simplify-options .submit-btn').on('click', onSubmit);
new SimpleButton('#simplify-options .cancel-btn').on('click', function() {
if (el.visible()) {
// cancel just hides menu if slider is visible
menu.hide();
} else {
model.clearMode();
}
});
new SimpleButton('#simplify-settings-btn').on('click', function() {
if (menu.visible()) {
menu.hide();
} else {
initMenu();
}
});
new ModeButton('#simplify-btn', 'simplify', model);
model.addMode('simplify', turnOn, turnOff);
model.on('select', function() {
if (model.getMode() == 'simplify') model.clearMode();
});
// exit simplify mode when user clicks off the visible part of the menu
menu.on('click', gui.handleDirectEvent(model.clearMode));
slider = new Slider("#simplify-control .slider");
slider.handle("#simplify-control .handle");
slider.track("#simplify-control .track");
slider.on('change', function(e) {
var pct = fromSliderPct(e.pct);
text.value(pct);
onchange(pct);
});
slider.on('start', function(e) {
control.dispatchEvent('simplify-start');
}).on('end', function(e) {
control.dispatchEvent('simplify-end');
});
text = new ClickText("#simplify-control .clicktext");
text.bounds(0, 1);
text.formatter(function(val) {
if (isNaN(val)) return '-';
var pct = val * 100;
var decimals = 0;
if (pct <= 0) decimals = 1;
else if (pct < 0.001) decimals = 4;
else if (pct < 0.01) decimals = 3;
else if (pct < 1) decimals = 2;
else if (pct < 100) decimals = 1;
return utils.formatNumber(pct, decimals) + "%";
});
text.parser(function(s) {
return parseFloat(s) / 100;
});
text.value(0);
text.on('change', function(e) {
var pct = e.value;
slider.pct(toSliderPct(pct));
control.dispatchEvent('simplify-start');
onchange(pct);
control.dispatchEvent('simplify-end');
});
function turnOn() {
var target = model.getEditingLayer();
if (!MapShaper.layerHasPaths(target.layer)) {
gui.alert("This layer can not be simplified");
return;
}
if (target.dataset.arcs.getVertexData().zz) {
// TODO: try to avoid calculating pct (slow);
showSlider(); // need to show slider before setting; TODO: fix
control.value(target.dataset.arcs.getRetainedPct());
} else {
initMenu();
}
}
function initMenu() {
var dataset = model.getEditingLayer().dataset;
var showPlanarOpt = !dataset.arcs.isPlanar();
var opts = MapShaper.getStandardSimplifyOpts(dataset, dataset.info && dataset.info.simplify);
El('#planar-opt-wrapper').node().style.display = showPlanarOpt ? 'block' : 'none';
El('#planar-opt').node().checked = !opts.spherical;
El("#import-retain-opt").node().checked = opts.keep_shapes;
El("#simplify-options input[value=" + opts.method + "]").node().checked = true;
menu.show();
}
function turnOff() {
menu.hide();
control.reset();
}
function onSubmit() {
var dataset = model.getEditingLayer().dataset;
var showMsg = dataset.arcs && dataset.arcs.getPointCount() > 1e6;
var delay = 0;
if (showMsg) {
delay = 35;
gui.showProgressMessage('Calculating');
}
menu.hide();
setTimeout(function() {
var opts = getSimplifyOptions();
mapshaper.simplify(dataset, opts);
model.updated({
// use presimplify flag if no vertices are removed
// (to trigger map redraw without recalculating intersections)
presimplify: opts.pct == 1,
simplify: opts.pct < 1
});
showSlider();
gui.clearProgressMessage();
}, delay);
}
function showSlider() {
el.show();
El('body').addClass('simplify'); // for resizing, hiding layer label, etc.
}
function getSimplifyOptions() {
var method = El('#simplify-options input[name=method]:checked').attr('value') || null;
return {
method: method,
pct: _value,
no_repair: true,
keep_shapes: !!El("#import-retain-opt").node().checked,
planar: !!El('#planar-opt').node().checked
};
}
function toSliderPct(p) {
p = Math.sqrt(p);
var pct = 1 - p;
return pct;
}
function fromSliderPct(p) {
var pct = 1 - p;
return pct * pct;
}
function onchange(val) {
if (_value != val) {
_value = val;
control.dispatchEvent('change', {value:val});
}
}
control.reset = function() {
control.value(1);
el.hide();
menu.hide();
El('body').removeClass('simplify');
};
control.value = function(val) {
if (!isNaN(val)) {
// TODO: validate
_value = val;
slider.pct(toSliderPct(val));
text.value(val);
}
return _value;
};
control.value(_value);
return control;
};
// Assume zip.js is loaded and zip is defined globally
zip.workerScripts = {
// deflater: ['z-worker.js', 'deflate.js'], // use zip.js deflater
// TODO: find out why it was necessary to rename pako_deflate.min.js
deflater: ['z-worker.js', 'pako.deflate.js', 'codecs.js'],
inflater: ['z-worker.js', 'pako.inflate.js', 'codecs.js']
};
// @file: Zip file
// @cb: function(err, <files>)
//
gui.readZipFile = function(file, cb) {
var _files = [];
zip.createReader(new zip.BlobReader(file), importZipContent, onError);
function onError(err) {
cb(err);
}
function onDone() {
cb(null, _files);
}
function importZipContent(reader) {
var _entries;
reader.getEntries(readEntries);
function readEntries(entries) {
_entries = entries || [];
readNext();
}
function readNext() {
if (_entries.length > 0) {
readEntry(_entries.pop());
} else {
reader.close();
onDone();
}
}
function readEntry(entry) {
var filename = entry.filename,
isValid = !entry.directory && gui.isReadableFileType(filename) &&
!/^__MACOSX/.test(filename); // ignore "resource-force" files
if (isValid) {
entry.getData(new zip.BlobWriter(), function(file) {
file.name = filename; // Give the Blob a name, like a File object
_files.push(file);
readNext();
});
} else {
readNext();
}
}
}
};
gui.showProgressMessage = function(msg) {
if (!gui.progressMessage) {
gui.progressMessage = El('div').id('progress-message')
.appendTo('body');
}
El('<div>').text(msg).appendTo(gui.progressMessage.empty().show());
};
gui.clearProgressMessage = function() {
if (gui.progressMessage) gui.progressMessage.hide();
};
gui.parseFreeformOptions = function(raw, cmd) {
var str = raw.trim(),
parsed;
if (!str) {
return {};
}
if (!/^-/.test(str)) {
str = '-' + cmd + ' ' + str;
}
parsed = MapShaper.parseCommands(str);
if (!parsed.length || parsed[0].name != cmd) {
stop("Unable to parse command line options");
}
return parsed[0].options;
};
// tests if filename is a type that can be used
gui.isReadableFileType = function(filename) {
var ext = utils.getFileExtension(filename).toLowerCase();
return !!MapShaper.guessInputFileType(filename) || MapShaper.couldBeDsvFile(filename) ||
MapShaper.isZipFile(filename);
};
// @cb function(<FileList>)
function DropControl(cb) {
var el = El('body');
el.on('dragleave', ondrag);
el.on('dragover', ondrag);
el.on('drop', ondrop);
function ondrag(e) {
// blocking drag events enables drop event
e.preventDefault();
}
function ondrop(e) {
e.preventDefault();
cb(e.dataTransfer.files);
}
}
// @el DOM element for select button
// @cb function(<FileList>)
function FileChooser(el, cb) {
var btn = El(el).on('click', function() {
input.el.click();
});
var input = El('form')
.addClass('file-control').appendTo('body')
.newChild('input')
.attr('type', 'file')
.attr('multiple', 'multiple')
.on('change', onchange);
function onchange(e) {
var files = e.target.files;
// files may be undefined (e.g. if user presses 'cancel' after a file has been selected)
if (files) {
// disable the button while files are being processed
btn.addClass('selected');
input.attr('disabled', true);
cb(files);
btn.removeClass('selected');
input.attr('disabled', false);
}
}
}
function ImportControl(model) {
new SimpleButton('#import-buttons .submit-btn').on('click', submitFiles);
new SimpleButton('#import-buttons .cancel-btn').on('click', model.clearMode);
var importCount = 0;
var queuedFiles = [];
model.addMode('import', turnOn, turnOff);
new DropControl(receiveFiles);
new FileChooser('#file-selection-btn', receiveFiles);
new FileChooser('#import-buttons .add-btn', receiveFiles);
new FileChooser('#add-file-btn', receiveFiles);
model.enterMode('import');
model.on('mode', function(e) {
// re-open import opts if leaving alert or console modes and nothing has been imported yet
if (!e.name && importCount === 0) {
model.enterMode('import');
}
});
function findMatchingShp(filename) {
var shpName = utils.replaceFileExtension(filename, 'shp');
return model.getDatasets().filter(function(d) {
return shpName == d.info.input_files[0];
});
}
function turnOn() {
if (mapshaper.manifest) {
downloadFiles(mapshaper.manifest);
mapshaper.manifest = null;
} else {
El('#import-options').show();
}
}
function close() {
El('#fork-me').hide();
El('#import-intro').hide(); // only show intro before first import
El('#import-buttons').show();
El('#import-options').hide();
}
function turnOff() {
gui.clearProgressMessage();
clearFiles();
close();
}
function clearFiles() {
queuedFiles = [];
El('#dropped-file-list .file-list').empty();
El('#dropped-file-list').hide();
}
function addFiles(files) {
var index = {};
queuedFiles = queuedFiles.concat(files).reduce(function(memo, f) {
// filter out unreadable types and dupes
if (gui.isReadableFileType(f.name) && f.name in index === false) {
index[f.name] = true;
memo.push(f);
}
return memo;
}, []);
// sort alphabetically by filename
queuedFiles.sort(function(a, b) {
return a.name > b.name ? 1 : -1;
});
}
function showQueuedFiles() {
var list = El('#dropped-file-list .file-list').empty();
El('#dropped-file-list').show();
queuedFiles.forEach(function(f) {
El('<p>').text(f.name).appendTo(El("#dropped-file-list .file-list"));
});
}
function receiveFiles(files) {
var prevSize = queuedFiles.length;
addFiles(utils.toArray(files));
if (queuedFiles.length === 0) return;
model.enterMode('import');
if (importCount === 0 && prevSize === 0 && containsImmediateFile(queuedFiles)) {
// if the first batch of files will be imported, process right away
submitFiles();
} else {
showQueuedFiles();
El('#import-buttons').show();
}
}
// Check if an array of File objects contains a file that should be imported right away
function containsImmediateFile(files) {
return utils.some(files, function(f) {
var type = MapShaper.guessInputFileType(f.name);
return type == 'shp' || type == 'json';
});
}
function submitFiles() {
close();
readNext();
}
function readNext() {
if (queuedFiles.length > 0) {
readFile(queuedFiles.pop()); // read in rev. alphabetic order, so .shp comes before .dbf
} else {
model.clearMode();
}
}
function getImportOpts() {
var freeform = El('#import-options .advanced-options').node().value,
opts = gui.parseFreeformOptions(freeform, 'i');
opts.no_repair = !El("#repair-intersections-opt").node().checked;
opts.auto_snap = !!El("#snap-points-opt").node().checked;
return opts;
}
function loadFile(file, cb) {
var reader = new FileReader(),
isBinary = MapShaper.isBinaryFile(file.name);
// no callback on error -- fix?
reader.onload = function(e) {
cb(null, reader.result);
};
if (isBinary) {
reader.readAsArrayBuffer(file);
} else {
// TODO: improve to handle encodings, etc.
reader.readAsText(file, 'UTF-8');
}
}
// @file a File object
function readFile(file) {
if (MapShaper.isZipFile(file.name)) {
readZipFile(file);
} else {
loadFile(file, function(err, content) {
if (err) {
readNext();
} else {
readFileContent(file.name, content);
}
});
}
}
function readFileContent(name, content) {
var type = MapShaper.guessInputType(name, content),
importOpts = getImportOpts(),
matches = findMatchingShp(name),
dataset, lyr;
// TODO: refactor
if (type == 'dbf' && matches.length > 0) {
// find an imported .shp layer that is missing attribute data
// (if multiple matches, try to use the most recently imported one)
dataset = matches.reduce(function(memo, d) {
if (!d.layers[0].data) {
memo = d;
}
return memo;
}, null);
if (dataset) {
lyr = dataset.layers[0];
lyr.data = new MapShaper.ShapefileTable(content, importOpts.encoding);
if (lyr.shapes && lyr.data.size() != lyr.shapes.length) {
stop("Different number of records in .shp and .dbf files");
}
if (!lyr.geometry_type) {
// kludge: trigger display of table cells if .shp has null geometry
model.updated(null, lyr, dataset);
}
readNext();
return;
}
}
if (type == 'prj') {
// assumes that .shp has been imported first
matches.forEach(function(d) {
if (!d.info.output_prj && !d.info.input_prj) {
d.info.input_prj = content;
}
});
readNext();
return;
}
importFileContent(type, name, content, importOpts);
}
function importFileContent(type, path, content, importOpts) {
var size = content.byteLength || content.length, // ArrayBuffer or string
showMsg = size > 4e7, // don't show message if dataset is small
delay = 0;
importOpts.files = [path]; // TODO: try to remove this
if (showMsg) {
gui.showProgressMessage('Importing');
delay = 35;
}
setTimeout(function() {
var dataset = MapShaper.importFileContent(content, path, importOpts);
dataset.info.no_repair = importOpts.no_repair;
model.addDataset(dataset);
importCount++;
readNext();
}, delay);
}
function readZipFile(file) {
gui.showProgressMessage('Importing');
setTimeout(function() {
gui.readZipFile(file, function(err, files) {
if (err) {
console.log("Zip file loading failed:");
throw err;
}
// don't try to import .txt files from zip files
// (these would be parsed as dsv and throw errows)
files = files.filter(function(f) {
return !/\.txt$/i.test(f.name);
});
addFiles(files);
readNext();
});
}, 35);
}
function downloadFiles(paths, opts) {
paths = paths.filter(function(f) {
return gui.isReadableFileType(f);
});
utils.reduceAsync(paths, [], downloadNextFile, function(err, files) {
if (err || !files.length) {
model.clearMode();
} else {
addFiles(files);
submitFiles();
}
});
}
function downloadNextFile(memo, filepath, next) {
var req = new XMLHttpRequest();
req.responseType = 'blob';
req.addEventListener('load', function(e) {
var blob = req.response;
blob.name = filepath;
memo.push(blob);
next(null, memo);
});
req.addEventListener('error', function(e) {
next('error');
});
req.open('GET', '/data/' + filepath);
req.send();
}
}
// Export buttons and their behavior
var ExportControl = function(model) {
var downloadSupport = typeof URL != 'undefined' && URL.createObjectURL &&
typeof document.createElement("a").download != "undefined" ||
!!window.navigator.msSaveBlob;
var unsupportedMsg = "Exporting is not supported in this browser";
var menu = El('#export-options').on('click', gui.handleDirectEvent(model.clearMode));
var datasets = []; // array of exportable layers grouped by dataset
var anchor, blobUrl;
new SimpleButton('#export-options .cancel-btn').on('click', model.clearMode);
if (!downloadSupport) {
El('#export-btn').on('click', function() {
gui.alert(unsupportedMsg);
});
MapShaper.writeFiles = function() {
error(unsupportedMsg);
};
} else {
anchor = menu.newChild('a').attr('href', '#').node();
initExportButton();
model.addMode('export', turnOn, turnOff);
new ModeButton('#export-btn', 'export', model);
MapShaper.writeFiles = function(files, opts, done) {
if (!utils.isArray(files) || files.length === 0) {
done("Nothing to export");
}else if (opts.isEchartsType) {
var content="";
var fileFormat=".json";
var filename=files[0].filename.replace('.json','');
if(opts.isEchartsType==='json'){
fileFormat=".json";
content=Encoder.convert2Echarts(files[0].content, filename,'json');
}else{
fileFormat=".js";
content=Encoder.convert2Echarts(files[0].content, filename,'js');
}
saveBlob(filename+fileFormat, new Blob([content]), done);
}else if (files.length == 1) {
saveBlob(files[0].filename, new Blob([files[0].content]), done);
} else {
filename = utils.getCommonFileBase(utils.pluck(files, 'filename')) || "output";
saveZipFile(filename + ".zip", files, done);
}
};
}
function initLayerMenu() {
// init layer menu with current editing layer selected
var list = El('#export-layer-list').empty();
var template = '<label><input type="checkbox" checked> %s</label>';
var datasets = model.getDatasets().map(initDataset);
var hideLayers = datasets.length == 1 && datasets[0].layers.length < 2;
El('#export-layers').css('display', hideLayers ? 'none' : 'block');
return datasets;
function initDataset(dataset) {
var layers = dataset.layers.map(function(lyr) {
var html = utils.format(template, lyr.name || '[unnamed layer]');
var box = El('div').html(html).appendTo(list).findChild('input').node();
return {
checkbox: box,
layer: lyr
};
});
return {
dataset: dataset,
layers: layers
};
}
}
function getInputFormats() {
return model.getDatasets().reduce(function(memo, d) {
var fmt = d.info && d.info.input_format;
if (fmt) memo.push(fmt);
return memo;
}, []);
}
function getDefaultExportFormat() {
var dataset = model.getEditingLayer().dataset;
return dataset.info && dataset.info.input_format || 'geojson';
}
function initFormatMenu() {
var defaults = ['shapefile', 'geojson', 'topojson', 'dsv','svg','echartsmapjson','echartsmapjs'];
var formats = utils.uniq(defaults.concat(getInputFormats()));
var items = formats.map(function(fmt) {
return utils.format('<div><label><input type="radio" name="format" value="%s"' +
' class="radio">%s</label></div>', fmt, MapShaper.getFormatName(fmt));
});
El('#export-formats').html(items.join('\n'));
El('#export-formats input[value="' + getDefaultExportFormat() + '"]').node().checked = true;
}
function turnOn() {
datasets = initLayerMenu();
initFormatMenu();
menu.show();
}
function turnOff() {
menu.hide();
}
function getSelectedFormat() {
return El('#export-formats input:checked').node().value;
}
function getSelectedLayers() {
var selections = datasets.reduce(function(memo, obj) {
var dataset = obj.dataset;
var selection = obj.layers.reduce(reduceLayer, []);
if (selection.length > 0) {
memo.push(utils.defaults({layers: selection}, dataset));
}
return memo;
}, []);
function reduceLayer(memo, obj) {
if (obj.checkbox.checked) {
// shallow-copy layer, so uniqified filenames do not affect original layers
memo.push(utils.extend({}, obj.layer));
}
return memo;
}
return selections;
}
function initExportButton() {
new SimpleButton('#save-btn').on('click', function() {
gui.showProgressMessage('Exporting');
model.clearMode();
setTimeout(function() {
exportMenuSelection(function(err) {
if (err) {
if (utils.isString(err)) {
gui.alert(err);
} else {
// stack seems to change if Error is logged directly
console.error(err.stack);
gui.alert("Export failed for an unknown reason");
}
}
// hide message after a delay, so it doesn't just flash for an instant.
setTimeout(gui.clearProgressMessage, err ? 0 : 400);
});
}, 20);
});
}
// @done function(string|Error|null)
function exportMenuSelection(done) {
var opts, files, datasets;
try {
opts = gui.parseFreeformOptions(El('#export-options .advanced-options').node().value, 'o');
if (!opts.format) opts.format = getSelectedFormat();
if(opts.format==='echartsmapjson'){
opts.format='geojson';
opts.isEchartsType='json';
}else if(opts.format==='echartsmapjs'){
opts.format='geojson';
opts.isEchartsType='js';
}
// ignoring command line "target" option
datasets = getSelectedLayers();
if (isMultiLayerFormat(opts.format)) {
// merge multiple datasets into one for export as SVG or TopoJSON
if (datasets.length > 1) {
datasets = [MapShaper.mergeDatasetsForExport(datasets)];
if (opts.format == 'topojson') {
// Build topology, in case user has loaded several
// files derived from the same source, with matching coordinates
// (Downsides: useless work if geometry is unrelated;
// could create many small arcs if layers are partially related)
api.buildTopology(datasets[0]);
}
// KLUDGE let exporter know that cloning is not needed
// (because shape data was deep-copied during merge)
opts.cloned = true;
}
} else {
MapShaper.assignUniqueLayerNames2(datasets);
}
files = datasets.reduce(function(memo, dataset) {
var output = MapShaper.exportFileContent(dataset, opts);
return memo.concat(output);
}, []);
// multiple output files will be zipped, need unique names
MapShaper.assignUniqueFileNames(files);
} catch(e) {
return done(e);
}
MapShaper.writeFiles(files, opts, done);
}
function isMultiLayerFormat(fmt) {
return fmt == 'svg' || fmt == 'topojson';
}
function saveBlob(filename, blob, done) {
if (window.navigator.msSaveBlob) {
window.navigator.msSaveBlob(blob, filename);
done();
}
try {
// revoke previous download url, if any. TODO: do this when download completes (how?)
if (blobUrl) URL.revokeObjectURL(blobUrl);
blobUrl = URL.createObjectURL(blob);
} catch(e) {
done("Mapshaper can't export files from this browser. Try switching to Chrome or Firefox.");
return;
}
// TODO: handle errors
anchor.href = blobUrl;
anchor.download = filename;
var clickEvent = document.createEvent("MouseEvent");
clickEvent.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false,
false, false, false, 0, null);
anchor.dispatchEvent(clickEvent);
done();
}
function saveZipFile(zipfileName, files, done) {
var toAdd = files;
try {
zip.createWriter(new zip.BlobWriter("application/zip"), addFile, zipError);
} catch(e) {
// TODO: show proper error message, not alert
done("This browser doesn't support Zip file creation.");
}
function zipError(msg) {
var str = "Error creating Zip file";
if (msg) {
str += ": " + (msg.message || msg);
}
done(str);
}
function addFile(archive) {
if (toAdd.length === 0) {
archive.close(function(blob) {
saveBlob(zipfileName, blob, done);
});
} else {
var obj = toAdd.pop(),
blob = new Blob([obj.content]);
archive.add(obj.filename, new zip.BlobReader(blob), function() {addFile(archive);});
}
}
}
};
function RepairControl(model, map) {
var el = El("#intersection-display"),
readout = el.findChild("#intersection-count"),
btn = el.findChild("#repair-btn"),
_self = this,
_dataset, _currXX;
model.on('update', function(e) {
if (e.flags.simplify || e.flags.proj || e.flags.arc_count) {
// these changes require nulling out any cached intersection data and recalculating
if (_dataset) {
_dataset.info.intersections = null;
_dataset = null;
_self.hide();
}
delayedUpdate();
} else if (e.flags.select) {
_self.hide();
if (!e.flags.import) {
// Don't recalculate if a dataset was just imported -- another layer may be
// selected right away.
reset();
delayedUpdate();
}
}
});
model.on('mode', function(e) {
if (e.prev == 'import') {
// update if import just finished and a new dataset is being edited
delayedUpdate();
}
});
btn.on('click', function() {
var fixed = MapShaper.repairIntersections(_dataset.arcs, _currXX);
showIntersections(fixed);
btn.addClass('disabled');
model.updated({repair: true});
});
this.hide = function() {
el.hide();
map.setHighlightLayer(null);
};
// Detect and display intersections for current level of arc simplification
this.update = function() {
var XX, showBtn, pct;
if (!_dataset) return;
if (_dataset.arcs.getRetainedInterval() > 0) {
// TODO: cache these intersections
XX = MapShaper.findSegmentIntersections(_dataset.arcs);
showBtn = XX.length > 0;
} else { // no simplification
XX = _dataset.info.intersections;
if (!XX) {
// cache intersections at 0 simplification, to avoid recalculating
// every time the simplification slider is set to 100% or the layer is selected at 100%
XX = _dataset.info.intersections = MapShaper.findSegmentIntersections(_dataset.arcs);
}
showBtn = false;
}
el.show();
showIntersections(XX);
btn.classed('disabled', !showBtn);
};
function delayedUpdate() {
setTimeout(function() {
var e = model.getEditingLayer();
if (e.dataset && e.dataset != _dataset && !e.dataset.info.no_repair &&
MapShaper.layerHasPaths(e.layer)) {
_dataset = e.dataset;
_self.update();
}
}, 10);
}
function reset() {
_dataset = null;
_currXX = null;
_self.hide();
}
function showIntersections(XX) {
var n = XX.length, pointLyr;
_currXX = XX;
if (n > 0) {
pointLyr = {geometry_type: 'point', shapes: [MapShaper.getIntersectionPoints(XX)]};
map.setHighlightLayer(pointLyr, {layers:[pointLyr]});
readout.text(utils.format("%s line intersection%s", n, utils.pluralSuffix(n)));
} else {
map.setHighlightLayer(null);
readout.text('');
}
}
}
utils.inherit(RepairControl, EventDispatcher);
function LayerControl(model) {
var el = El("#layer-control").on('click', gui.handleDirectEvent(model.clearMode));
var buttonLabel = El('#layer-control-btn .layer-name');
var isOpen = false;
new ModeButton('#layer-control-btn .header-btn', 'layer_menu', model);
model.addMode('layer_menu', turnOn, turnOff);
model.on('update', function(e) {
updateBtn();
if (isOpen) render();
});
function turnOn() {
isOpen = true;
render();
el.show();
}
function turnOff() {
isOpen = false;
el.hide();
}
function updateBtn() {
var name = model.getEditingLayer().layer.name || "[unnamed layer]";
buttonLabel.html(name + " ▼");
}
function render() {
var list = El('#layer-control .layer-list');
if (isOpen) {
list.hide().empty();
model.forEachLayer(function(lyr, dataset) {
list.appendChild(renderLayer(lyr, dataset));
});
list.show();
}
}
function describeLyr(lyr) {
var n = MapShaper.getFeatureCount(lyr),
str, type;
if (lyr.data && !lyr.shapes) {
type = 'data record';
} else if (lyr.geometry_type) {
type = lyr.geometry_type + ' feature';
}
if (type) {
str = utils.format('%,d %s%s', n, type, utils.pluralSuffix(n));
} else {
str = "[empty]";
}
return str;
}
function describeSrc(lyr, dataset) {
var file = dataset.info.input_files[0] || '';
if (utils.endsWith(file, '.shp') && !lyr.data && lyr == dataset.layers[0]) {
file += " (missing .dbf)";
}
return file;
}
function getDisplayName(name) {
return name || '[unnamed]';
}
function renderLayer(lyr, dataset) {
var editLyr = model.getEditingLayer().layer;
var entry = El('div').addClass('layer-item').classed('active', lyr == editLyr);
var html = rowHTML('name', '<span class="layer-name colored-text dot-underline">' + getDisplayName(lyr.name) + '</span>');
html += rowHTML('source file', describeSrc(lyr, dataset));
html += rowHTML('contents', describeLyr(lyr));
html += '<img src="images/close.png">';
entry.html(html);
// init delete button
entry.findChild('img').on('mouseup', function(e) {
e.stopPropagation();
deleteLayer(lyr, dataset);
});
// init name editor
new ClickText2(entry.findChild('.layer-name'))
.on('change', function(e) {
var str = cleanLayerName(this.value());
this.value(getDisplayName(str));
lyr.name = str;
updateBtn();
});
// init click-to-select
gui.onClick(entry, function() {
if (!gui.getInputElement()) { // don't select if user is typing
model.clearMode();
if (lyr != editLyr) {
model.updated({select: true}, lyr, dataset);
}
}
});
return entry;
}
function deleteLayer(lyr, dataset) {
var otherLyr = model.findAnotherLayer(lyr);
if (otherLyr) {
turnOff(); // avoid rendering twice
if (model.getEditingLayer().layer == lyr) {
// switch to a different layer if deleted layer was selected
model.selectLayer(otherLyr.layer, otherLyr.dataset);
}
model.deleteLayer(lyr, dataset);
turnOn();
} else {
// refresh browser if deleted layer was the last layer
window.location.href = window.location.href.toString();
}
}
function cleanLayerName(raw) {
return raw.replace(/[\n\t/\\]/g, '')
.replace(/^[\.\s]+/, '').replace(/[\.\s]+$/, '');
}
function rowHTML(c1, c2) {
return utils.format('<div class="row"><div class="col1">%s</div>' +
'<div class="col2">%s</div></div>', c1, c2);
}
}
// These functions could be called when validating i/o options; TODO: avoid this
cli.isFile =
cli.isDirectory = function(name) {return false;};
cli.validateOutputDir = function() {};
// Replaces functions for reading from files with functions that try to match
// already-loaded datasets.
//
function ImportFileProxy(model) {
// Try to match an imported dataset or layer.
// TODO: think about handling import options
function find(src) {
var datasets = model.getDatasets();
var retn = datasets.reduce(function(memo, d) {
var lyr;
if (memo) return memo; // already found a match
// try to match import filename of this dataset
if (d.info.input_files[0] == src) return d;
// try to match name of a layer in this dataset
lyr = utils.find(d.layers, function(lyr) {return lyr.name == src;});
return lyr ? MapShaper.isolateLayer(lyr, d) : null;
}, null);
if (!retn) stop("Missing data layer [" + src + "]");
return retn;
}
api.importFile = function(src, opts) {
var dataset = find(src);
// Aeturn a copy with layers duplicated, so changes won't affect original layers
// This makes an (unsafe) assumption that the dataset arcs won't be changed...
// need to rethink this.
return utils.defaults({
layers: dataset.layers.map(MapShaper.copyLayer)
}, dataset);
};
api.importDataTable = function(src, opts) {
var dataset = find(src);
return dataset.layers[0].data;
};
}
gui.getPixelRatio = function() {
var deviceRatio = window.devicePixelRatio || window.webkitDevicePixelRatio || 1;
return deviceRatio > 1 ? 2 : 1;
};
function DisplayCanvas() {
var _self = El('canvas'),
_canvas = _self.node(),
_ctx = _canvas.getContext('2d'),
_ext;
_self.prep = function(extent) {
var w = extent.width(),
h = extent.height(),
pixRatio = gui.getPixelRatio();
_ctx.clearRect(0, 0, _canvas.width, _canvas.height);
_canvas.width = w * pixRatio;
_canvas.height = h * pixRatio;
_self.classed('retina', pixRatio == 2);
_self.show();
_ext = extent;
};
_self.drawPathShapes = function(shapes, arcs, style) {
var start = getPathStart(style, _ext),
draw = getShapePencil(arcs, _ext),
end = getPathEnd(style);
for (var i=0, n=shapes.length; i<n; i++) {
start(_ctx, i);
draw(shapes[i], _ctx);
end(_ctx);
}
};
_self.drawSquareDots = function(shapes, style) {
var t = getScaledTransform(_ext),
pixRatio = gui.getPixelRatio(),
size = (style.dotSize || 3) * pixRatio,
styler = style.styler || null,
shp, p;
_ctx.fillStyle = style.dotColor || "black";
// TODO: don't try to draw offscreen points
for (var i=0, n=shapes.length; i<n; i++) {
if (styler !== null) {
styler(style, i);
size = style.dotSize * pixRatio;
_ctx.fillStyle = style.dotColor;
}
shp = shapes[i];
for (var j=0, m=shp ? shp.length : 0; j<m; j++) {
if (!shp) continue;
p = shp[j];
drawSquare(p[0] * t.mx + t.bx, p[1] * t.my + t.by, size, _ctx);
}
}
};
_self.drawPoints = function(shapes, style) {
var t = getScaledTransform(_ext),
pixRatio = gui.getPixelRatio(),
start = getPathStart(style, _ext),
end = getPathEnd(style),
shp, p;
for (var i=0, n=shapes.length; i<n; i++) {
shp = shapes[i];
start(_ctx, i);
if (!shp || style.radius > 0 === false) continue;
for (var j=0, m=shp ? shp.length : 0; j<m; j++) {
p = shp[j];
drawCircle(p[0] * t.mx + t.bx, p[1] * t.my + t.by, style.radius * pixRatio, _ctx);
}
end(_ctx);
}
};
_self.drawArcs = function(arcs, flags, style) {
var darkStyle = {strokeWidth: style.strokeWidth, strokeColor: style.strokeColors[1]},
lightStyle = {strokeWidth: style.strokeWidth, strokeColor: style.strokeColors[0]};
setArcVisibility(flags, arcs);
drawFlaggedArcs(2, flags, lightStyle, arcs);
drawFlaggedArcs(3, flags, darkStyle, arcs);
};
function setArcVisibility(flags, arcs) {
var minPathLen = 0.5 * _ext.getPixelSize(),
geoBounds = _ext.getBounds(),
geoBBox = geoBounds.toArray(),
allIn = geoBounds.contains(arcs.getBounds()),
visible;
// don't continue dropping paths if user zooms out farther than full extent
if (_ext.scale() < 1) minPathLen *= _ext.scale();
for (var i=0, n=arcs.size(); i<n; i++) {
visible = !arcs.arcIsSmaller(i, minPathLen) && (allIn ||
arcs.arcIntersectsBBox(i, geoBBox));
// mark visible arcs by setting second flag bit to 1
flags[i] = (flags[i] & 1) | (visible ? 2 : 0);
}
}
function drawFlaggedArcs(flag, flags, style, arcs) {
var start = getPathStart(style, _ext),
end = getPathEnd(style),
t = getScaledTransform(_ext),
ctx = _ctx,
n = 25, // render paths in batches of this size (an optimization)
count = 0;
start(ctx);
for (i=0, n=arcs.size(); i<n; i++) {
if (flags[i] != flag) continue;
if (++count % n === 0) {
end(ctx);
start(ctx);
}
drawPath(arcs.getArcIter(i), t, ctx);
}
end(ctx);
}
return _self;
}
function getScaledTransform(ext) {
return ext.getTransform(gui.getPixelRatio());
}
function drawCircle(x, y, radius, ctx) {
if (radius > 0) {
ctx.moveTo(x + radius, y);
ctx.arc(x, y, radius, 0, Math.PI * 2, true);
}
}
function drawSquare(x, y, size, ctx) {
if (size > 0) {
var offs = size / 2;
x = Math.round(x - offs);
y = Math.round(y - offs);
ctx.fillRect(x, y, size, size);
}
}
function drawPath(vec, t, ctx) {
var minLen = gui.getPixelRatio() > 1 ? 1 : 0.6,
x, y, xp, yp;
if (!vec.hasNext()) return;
x = xp = vec.x * t.mx + t.bx;
y = yp = vec.y * t.my + t.by;
ctx.moveTo(x, y);
while (vec.hasNext()) {
x = vec.x * t.mx + t.bx;
y = vec.y * t.my + t.by;
if (Math.abs(x - xp) > minLen || Math.abs(y - yp) > minLen) {
ctx.lineTo(x, y);
xp = x;
yp = y;
}
}
if (x != xp || y != yp) {
ctx.lineTo(x, y);
}
}
function getShapePencil(arcs, ext) {
var t = getScaledTransform(ext);
return function(shp, ctx) {
var iter = new MapShaper.ShapeIter(arcs);
if (!shp) return;
for (var i=0; i<shp.length; i++) {
iter.init(shp[i]);
drawPath(iter, t, ctx);
}
};
}
function getPathStart(style, ext) {
var mapScale = ext.scale(),
styler = style.styler || null,
pixRatio = gui.getPixelRatio(),
lineScale = 1;
// vary line width according to zoom ratio; for performance and clarity,
// don't start thickening lines until zoomed quite far in.
if (mapScale < 1) {
lineScale *= Math.pow(mapScale, 0.6);
} else if (mapScale > 60) {
lineScale *= Math.pow(mapScale - 59, 0.18);
}
return function(ctx, i) {
var strokeWidth;
ctx.beginPath();
if (styler) {
styler(style, i);
}
if (style.opacity >= 0) {
ctx.globalAlpha = style.opacity;
}
if (style.strokeWidth > 0) {
strokeWidth = style.strokeWidth;
if (pixRatio > 1) {
// bump up thin lines on retina, but not to more than 1px (too slow)
strokeWidth = strokeWidth < 1 ? 1 : strokeWidth * pixRatio;
}
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
ctx.lineWidth = strokeWidth * lineScale;
ctx.strokeStyle = style.strokeColor;
}
if (style.fillColor) {
ctx.fillStyle = style.fillColor;
}
};
}
function getPathEnd(style) {
return function(ctx) {
if (style.fillColor) ctx.fill();
if (style.strokeWidth > 0) ctx.stroke();
if (style.opacity >= 0) ctx.globalAlpha = 1;
ctx.closePath();
};
}
// A wrapper for ArcCollection that filters paths to speed up rendering.
//
function FilteredArcCollection(unfilteredArcs) {
var sortedThresholds,
filteredArcs,
filteredSegLen;
init();
function init() {
var size = unfilteredArcs.getPointCount(),
cutoff = 5e5,
nth;
sortedThresholds = filteredArcs = null;
if (!!unfilteredArcs.getVertexData().zz) {
// If we have simplification data...
// Sort simplification thresholds for all non-endpoint vertices
// for quick conversion of simplification percentage to threshold value.
// For large datasets, use every nth point, for faster sorting.
nth = Math.ceil(size / cutoff);
sortedThresholds = unfilteredArcs.getRemovableThresholds(nth);
utils.quicksort(sortedThresholds, false);
// For large datasets, create a filtered copy of the data for faster rendering
if (size > cutoff) {
filteredArcs = initFilteredArcs(unfilteredArcs, sortedThresholds);
filteredSegLen = MapShaper.getAvgSegment(filteredArcs);
}
} else {
if (size > cutoff) {
// generate filtered arcs when no simplification data is present
filteredSegLen = MapShaper.getAvgSegment(unfilteredArcs) * 4;
filteredArcs = MapShaper.simplifyArcsFast(unfilteredArcs, filteredSegLen);
}
}
}
// Use simplification data to create a low-detail copy of arcs, for faster
// rendering when zoomed-out.
function initFilteredArcs(arcs, sortedThresholds) {
var filterPct = 0.08;
var currInterval = arcs.getRetainedInterval();
var filterZ = sortedThresholds[Math.floor(filterPct * sortedThresholds.length)];
var filteredArcs = arcs.setRetainedInterval(filterZ).getFilteredCopy();
arcs.setRetainedInterval(currInterval); // reset current simplification
return filteredArcs;
}
this.getArcCollection = function(ext) {
refreshFilteredArcs();
// Use a filtered version of arcs at small scales
var unitsPerPixel = 1/ext.getTransform().mx,
useFiltering = filteredArcs && unitsPerPixel > filteredSegLen * 1.5;
return useFiltering ? filteredArcs : unfilteredArcs;
};
function refreshFilteredArcs() {
if (filteredArcs) {
if (filteredArcs.size() != unfilteredArcs.size()) {
init();
}
filteredArcs.setRetainedInterval(unfilteredArcs.getRetainedInterval());
}
}
this.size = function() {return unfilteredArcs.size();};
this.setRetainedPct = function(pct) {
if (sortedThresholds) {
var z = sortedThresholds[Math.floor(pct * sortedThresholds.length)];
z = MapShaper.clampIntervalByPct(z, pct);
// this.setRetainedInterval(z);
unfilteredArcs.setRetainedInterval(z);
} else {
unfilteredArcs.setRetainedPct(pct);
}
};
}
gui.getDisplayLayerForTable = function(table) {
var n = table.size(),
cellWidth = 12,
cellHeight = 5,
gutter = 6,
arcs = [],
shapes = [],
lyr = {shapes: shapes},
data = {layer: lyr},
aspectRatio = 1.1,
usePoints = false,
x, y, col, row, blockSize;
if (n > 10000) {
usePoints = true;
gutter = 0;
cellWidth = 4;
cellHeight = 4;
aspectRatio = 1.45;
} else if (n > 5000) {
cellWidth = 5;
gutter = 3;
aspectRatio = 1.45;
} else if (n > 1000) {
gutter = 3;
cellWidth = 8;
aspectRatio = 1.3;
}
if (n < 25) {
blockSize = n;
} else {
blockSize = Math.sqrt(n * (cellWidth + gutter) / cellHeight / aspectRatio) | 0;
}
for (var i=0; i<n; i++) {
row = i % blockSize;
col = Math.floor(i / blockSize);
x = col * (cellWidth + gutter);
y = cellHeight * (blockSize - row);
if (usePoints) {
shapes.push([[x, y]]);
} else {
arcs.push(getArc(x, y, cellWidth, cellHeight));
shapes.push([[i]]);
}
}
if (usePoints) {
lyr.geometry_type = 'point';
} else {
data.arcs = new MapShaper.ArcCollection(arcs);
lyr.geometry_type = 'polygon';
}
lyr.data = table;
function getArc(x, y, w, h) {
return [[x, y], [x + w, y], [x + w, y - h], [x, y - h], [x, y]];
}
return data;
};
function DisplayLayer(lyr, dataset, ext) {
var _displayBounds;
init();
this.setStyle = function(o) {
lyr.display.style = o;
};
this.getBounds = function() {
return _displayBounds;
};
this.setRetainedPct = function(pct) {
var arcs = dataset.filteredArcs || dataset.arcs;
if (arcs) {
arcs.setRetainedPct(pct);
}
};
this.updateStyle = function(style) {
var o = this.getDisplayLayer();
// arc style
if (o.dataset.arcs) {
lyr.display.arcFlags = new Uint8Array(o.dataset.arcs.size());
if (MapShaper.layerHasPaths(o.layer)) {
initArcFlags(o.layer.shapes, lyr.display.arcFlags);
}
}
};
// @ext map extent
this.getDisplayLayer = function() {
var arcs = lyr.display.arcs,
layer = lyr.display.layer || lyr;
if (!arcs) {
// use filtered arcs if available & map extent is known
arcs = dataset.filteredArcs ?
dataset.filteredArcs.getArcCollection(ext) : dataset.arcs;
}
return {
layer: layer,
dataset: {arcs: arcs},
geographic: layer == lyr // false if using table-only shapes
};
};
this.draw = function(canv, style) {
style = style || lyr.display.style;
if (style.type == 'outline') {
this.drawStructure(canv, style);
} else {
this.drawShapes(canv, style);
}
};
this.drawStructure = function(canv, style) {
var obj = this.getDisplayLayer(ext);
var arcs = obj.dataset.arcs;
if (arcs && lyr.display.arcFlags) {
canv.drawArcs(arcs, lyr.display.arcFlags, style);
}
if (obj.layer.geometry_type == 'point') {
canv.drawSquareDots(obj.layer.shapes, style);
}
};
this.drawShapes = function(canv, style) {
var obj = this.getDisplayLayer(ext);
var lyr = style.ids ? filterLayer(obj.layer, style.ids) : obj.layer;
if (lyr.geometry_type == 'point') {
if (style.type == 'styled') {
canv.drawPoints(lyr.shapes, style);
} else {
canv.drawSquareDots(lyr.shapes, style);
}
} else {
canv.drawPathShapes(lyr.shapes, obj.dataset.arcs, style);
}
};
function filterLayer(lyr, ids) {
if (lyr.shapes) {
shapes = ids.map(function(id) {
return lyr.shapes[id];
});
return utils.defaults({shapes: shapes}, lyr);
}
return lyr;
}
function initArcFlags(shapes, arr) {
// Arcs belonging to at least one path are flagged 1, others 0
MapShaper.countArcsInShapes(shapes, arr);
for (var i=0, n=arr.length; i<n; i++) {
arr[i] = arr[i] === 0 ? 0 : 1;
}
}
function init() {
var display = lyr.display = lyr.display || {};
// init filtered arcs, if needed
if (MapShaper.layerHasPaths(lyr) && !dataset.filteredArcs) {
dataset.filteredArcs = new FilteredArcCollection(dataset.arcs);
}
// init table shapes, if needed
if (lyr.data && !lyr.geometry_type) {
if (!display.layer || display.layer.shapes.length != lyr.data.size()) {
utils.extend(display, gui.getDisplayLayerForTable(lyr.data));
}
} else if (display.layer) {
delete display.layer;
delete display.arcs;
}
_displayBounds = getDisplayBounds(display.layer || lyr, display.arcs || dataset.arcs);
}
}
function getDisplayBounds(lyr, arcs) {
var arcBounds = arcs ? arcs.getBounds() : new Bounds(),
bounds = arcBounds, // default display extent: all arcs in the dataset
lyrBounds;
if (lyr.geometry_type == 'point') {
lyrBounds = MapShaper.getLayerBounds(lyr);
if (lyrBounds && lyrBounds.hasBounds()) {
if (lyrBounds.area() > 0 || arcBounds.area() === 0) {
bounds = lyrBounds;
}
// if a point layer has no extent (e.g. contains only a single point),
// then use arc bounds (if present), to match any path layers in the dataset.
}
}
// If a layer has collapsed, inflate it by a default amount
if (bounds.width() === 0) {
bounds.xmin = (bounds.centerX() || 0) - 1;
bounds.xmax = bounds.xmin + 2;
}
if (bounds.height() === 0) {
bounds.ymin = (bounds.centerY() || 0) - 1;
bounds.ymax = bounds.ymin + 2;
}
return bounds;
}
function HighlightBox(el) {
var stroke = 2,
box = El('div').addClass('zoom-box').appendTo(el).hide();
this.show = function(x1, y1, x2, y2) {
var w = Math.abs(x1 - x2),
h = Math.abs(y1 - y2);
box.css({
top: Math.min(y1, y2),
left: Math.min(x1, x2),
width: Math.max(w - stroke * 2, 1),
height: Math.max(h - stroke * 2, 1)
});
box.show();
};
this.hide = function() {
box.hide();
};
}
gui.addSidebarButton = function(iconId) {
var btn = El('div').addClass('nav-btn')
.on('dblclick', function(e) {e.stopPropagation();}); // block dblclick zoom
btn.appendChild(iconId);
btn.appendTo('#nav-buttons');
return btn;
};
function MapNav(root, ext, mouse) {
var wheel = new MouseWheel(mouse),
zoomBox = new HighlightBox('body'),
buttons = El('div').id('nav-buttons').appendTo(root),
zoomTween = new Tween(Tween.sineInOut),
shiftDrag = false,
zoomScale = 2.5,
dragStartEvt, _fx, _fy; // zoom foci, [0,1]
gui.addSidebarButton("#home-icon").on('click', function() {ext.reset();});
gui.addSidebarButton("#zoom-in-icon").on('click', zoomIn);
gui.addSidebarButton("#zoom-out-icon").on('click', zoomOut);
zoomTween.on('change', function(e) {
ext.rescale(e.value, _fx, _fy);
});
mouse.on('dblclick', function(e) {
zoomByPct(zoomScale, e.x / ext.width(), e.y / ext.height());
});
mouse.on('dragstart', function(e) {
shiftDrag = !!e.shiftKey;
if (shiftDrag) {
dragStartEvt = e;
}
});
mouse.on('drag', function(e) {
if (shiftDrag) {
zoomBox.show(e.pageX, e.pageY, dragStartEvt.pageX, dragStartEvt.pageY);
} else {
ext.pan(e.dx, e.dy);
}
});
mouse.on('dragend', function(e) {
var bounds;
if (shiftDrag) {
shiftDrag = false;
bounds = new Bounds(e.x, e.y, dragStartEvt.x, dragStartEvt.y);
zoomBox.hide();
if (bounds.width() > 5 && bounds.height() > 5) {
zoomToBox(bounds);
}
}
});
wheel.on('mousewheel', function(e) {
var k = 1 + (0.11 * e.multiplier),
delta = e.direction > 0 ? k : 1 / k;
ext.rescale(ext.scale() * delta, e.x / ext.width(), e.y / ext.height());
});
function zoomIn() {
zoomByPct(zoomScale, 0.5, 0.5);
}
function zoomOut() {
zoomByPct(1/zoomScale, 0.5, 0.5);
}
// @box Bounds with pixels from t,l corner of map area.
function zoomToBox(box) {
var pct = Math.max(box.width() / ext.width(), box.height() / ext.height()),
fx = box.centerX() / ext.width() * (1 + pct) - pct / 2,
fy = box.centerY() / ext.height() * (1 + pct) - pct / 2;
zoomByPct(1 / pct, fx, fy);
}
// @pct Change in scale (2 = 2x zoom)
// @fx, @fy zoom focus, [0, 1]
function zoomByPct(pct, fx, fy) {
_fx = fx;
_fy = fy;
zoomTween.start(ext.scale(), ext.scale() * pct, 400);
}
}
function MapExtent(el) {
var _position = new ElementPosition(el),
_scale = 1,
_cx,
_cy,
_contentBounds;
_position.on('resize', function() {
this.dispatchEvent('change');
this.dispatchEvent('navigate');
this.dispatchEvent('resize');
}, this);
this.reset = function(force) {
this.recenter(_contentBounds.centerX(), _contentBounds.centerY(), 1, force);
};
this.recenter = function(cx, cy, scale, force) {
if (!scale) scale = _scale;
if (force || !(cx == _cx && cy == _cy && scale == _scale)) {
_cx = cx;
_cy = cy;
_scale = scale;
this.dispatchEvent('change');
this.dispatchEvent('navigate');
}
};
this.pan = function(xpix, ypix) {
var t = this.getTransform();
this.recenter(_cx - xpix / t.mx, _cy - ypix / t.my);
};
// Zoom to @scale (a multiple of the map's full scale)
// @xpct, @ypct: optional focus, [0-1]...
this.rescale = function(scale, xpct, ypct) {
if (arguments.length < 3) {
xpct = 0.5;
ypct = 0.5;
}
var b = this.getBounds(),
fx = b.xmin + xpct * b.width(),
fy = b.ymax - ypct * b.height(),
dx = b.centerX() - fx,
dy = b.centerY() - fy,
ds = _scale / scale,
dx2 = dx * ds,
dy2 = dy * ds,
cx = fx + dx2,
cy = fy + dy2;
this.recenter(cx, cy, scale);
};
this.resize = _position.resize;
this.width = _position.width;
this.height = _position.height;
this.position = _position.position;
// get zoom factor (1 == full extent, 2 == 2x zoom, etc.)
this.scale = function() {
return _scale;
};
this.getPixelSize = function() {
return 1 / this.getTransform().mx;
};
// Get params for converting geographic coords to pixel coords
this.getTransform = function(pixScale) {
// get transform (y-flipped);
var viewBounds = new Bounds(0, 0, _position.width(), _position.height());
if (pixScale) {
viewBounds.xmax *= pixScale;
viewBounds.ymax *= pixScale;
}
return this.getBounds().getTransform(viewBounds, true);
};
this.getBounds = function() {
if (!_contentBounds) return new Bounds();
return centerAlign(calcBounds(_cx, _cy, _scale));
};
// Update the extent of 'full' zoom without navigating the current view
this.setBounds = function(b) {
var prev = _contentBounds;
_contentBounds = b;
if (prev) {
_scale = _scale * centerAlign(b).width() / centerAlign(prev).width();
} else {
_cx = b.centerX();
_cy = b.centerY();
}
};
function getPadding(size) {
return size * 0.020 + 4;
}
function calcBounds(cx, cy, scale) {
var w = _contentBounds.width() / scale,
h = _contentBounds.height() / scale;
return new Bounds(cx - w/2, cy - h/2, cx + w/2, cy + h/2);
}
// Receive: Geographic bounds of content to be centered in the map
// Return: Geographic bounds of map window centered on @_contentBounds,
// with padding applied
function centerAlign(_contentBounds) {
var bounds = _contentBounds.clone(),
wpix = _position.width(),
hpix = _position.height(),
xmarg = getPadding(wpix),
ymarg = getPadding(hpix),
xpad, ypad;
wpix -= 2 * xmarg;
hpix -= 2 * ymarg;
if (wpix <= 0 || hpix <= 0) {
return new Bounds(0, 0, 0, 0);
}
bounds.fillOut(wpix / hpix);
xpad = bounds.width() / wpix * xmarg;
ypad = bounds.height() / hpix * ymarg;
bounds.padBounds(xpad, ypad, xpad, ypad);
return bounds;
}
}
utils.inherit(MapExtent, EventDispatcher);
function HitControl(ext, mouse) {
var self = new EventDispatcher();
var prevHits = [];
var active = false;
var tests = {
polygon: polygonTest,
polyline: polylineTest,
point: pointTest
};
var coords = El('#coordinate-info').hide();
var lyr, target, test;
ext.on('change', function() {
// shapes may change along with map scale
target = lyr ? lyr.getDisplayLayer() : null;
});
self.setLayer = function(o) {
lyr = o;
target = o.getDisplayLayer();
test = tests[target.layer.geometry_type];
coords.hide();
};
self.start = function() {
active = true;
};
self.stop = function() {
if (active) {
hover([]);
coords.text('').hide();
active = false;
}
};
mouse.on('click', function(e) {
if (!active || !target) return;
trigger('click', prevHits);
gui.selectElement(coords.node());
});
// DISABLING: This causes problems when hovering over the info panel
// Deselect hover shape when pointer leaves hover area
//mouse.on('leave', function(e) {
// hover(-1);
//});
mouse.on('hover', function(e) {
var p, decimals;
if (!active || !target) return;
p = ext.getTransform().invert().transform(e.x, e.y);
if (target.geographic) {
// update coordinate readout if displaying geographic shapes
decimals = getCoordPrecision(ext.getBounds());
coords.text(p[0].toFixed(decimals) + ', ' + p[1].toFixed(decimals)).show();
}
if (test && e.hover) {
hover(test(p[0], p[1]));
}
});
// Convert pixel distance to distance in coordinate units.
function getHitBuffer(pix) {
var dist = pix / ext.getTransform().mx,
scale = ext.scale();
if (scale < 1) dist *= scale; // reduce hit threshold when zoomed out
return dist;
}
function getCoordPrecision(bounds) {
var min = Math.min(Math.abs(bounds.xmax), Math.abs(bounds.ymax)),
decimals = Math.ceil(Math.log(min) / Math.LN10);
return Math.max(0, 7 - decimals);
}
function polygonTest(x, y) {
var dist = getHitBuffer(5),
cands = findHitCandidates(x, y, dist),
hits = [],
cand, hitId;
for (var i=0; i<cands.length; i++) {
cand = cands[i];
if (geom.testPointInPolygon(x, y, cand.shape, target.dataset.arcs)) {
hits.push(cand.id);
}
}
if (cands.length > 0 && hits.length === 0) {
// secondary detection: proximity, if not inside a polygon
hits = findNearestCandidates(x, y, dist, cands, target.dataset.arcs);
}
return hits;
}
function polylineTest(x, y) {
var dist = getHitBuffer(15),
cands = findHitCandidates(x, y, dist);
return findNearestCandidates(x, y, dist, cands, target.dataset.arcs);
}
function findNearestCandidates(x, y, dist, cands, arcs) {
var hits = [],
cand, candDist;
for (var i=0; i<cands.length; i++) {
cand = cands[i];
candDist = geom.getPointToShapeDistance(x, y, cand.shape, arcs);
if (candDist < dist) {
hits = [cand.id];
dist = candDist;
} else if (candDist == dist) {
hits.push(cand.id);
}
}
return hits;
}
function pointTest(x, y) {
var dist = getHitBuffer(25),
limitSq = dist * dist,
hits = [];
MapShaper.forEachPoint(target.layer.shapes, function(p, id) {
var distSq = geom.distanceSq(x, y, p[0], p[1]);
if (distSq < limitSq) {
hits = [id];
limitSq = distSq;
} else if (distSq == limitSq) {
hits.push(id);
}
});
return hits;
}
function getProperties(id) {
return target.layer.data ? target.layer.data.getRecordAt(id) : {};
}
function sameIds(a, b) {
if (a.length != b.length) return false;
for (var i=0; i<a.length; i++) {
if (a[i] !== b[i]) return false;
}
return true;
}
function trigger(event, hits) {
self.dispatchEvent(event, {
ids: hits,
id: hits.length > 0 ? hits[0] : -1
});
}
function hover(hits) {
if (!sameIds(hits, prevHits)) {
prevHits = hits;
El('#map-layers').classed('hover', hits.length > 0);
trigger('hover', hits);
}
}
function findHitCandidates(x, y, dist) {
var arcs = target.dataset.arcs,
index = {},
cands = [],
bbox = [];
target.layer.shapes.forEach(function(shp, shpId) {
var cand;
for (var i = 0, n = shp && shp.length; i < n; i++) {
arcs.getSimpleShapeBounds2(shp[i], bbox);
if (x + dist < bbox[0] || x - dist > bbox[2] ||
y + dist < bbox[1] || y - dist > bbox[3]) {
continue; // bbox non-intersection
}
cand = index[shpId];
if (!cand) {
cand = index[shpId] = {shape: [], id: shpId};
cands.push(cand);
}
cand.shape.push(shp[i]);
}
});
return cands;
}
return self;
}
function Popup() {
var parent = El('#mshp-main-map');
var el = El('div').addClass('popup').appendTo(parent).hide();
// var head = El('div').addClass('popup-head').appendTo(el).text('Feature 1 of 5 next prev');
var content = El('div').addClass('popup-content').appendTo(el);
this.show = function(rec, table, editable) {
var maxHeight = parent.node().clientHeight - 36;
this.hide(); // clean up if panel is already open
render(content, rec, table, editable);
el.show();
if (content.node().clientHeight > maxHeight) {
content.css('height:' + maxHeight + 'px');
}
};
this.hide = function() {
// make sure any pending edits are made before re-rendering popup
// TODO: only blur popup fields
gui.blurActiveElement();
content.empty();
content.node().removeAttribute('style'); // remove inline height
el.hide();
};
function render(el, rec, table, editable) {
var tableEl = El('table').addClass('selectable'),
rows = 0;
utils.forEachProperty(rec, function(v, k) {
var type = MapShaper.getFieldType(v, k, table);
renderRow(tableEl, rec, k, type, editable);
rows++;
});
if (rows > 0) {
tableEl.appendTo(el);
} else {
el.html('<div class="note">This layer is missing attribute data.</div>');
}
}
function renderRow(table, rec, key, type, editable) {
var rowHtml = '<td class="field-name">%s</td><td><span class="value">%s</span> </td>';
var val = rec[key];
var cell = El('tr')
.appendTo(table)
.html(utils.format(rowHtml, key, utils.htmlEscape(val)))
.findChild('.value');
setFieldClass(cell, val, type);
if (editable) {
editItem(cell, rec, key, type);
}
}
function setFieldClass(el, val, type) {
var isNum = type ? type == 'number' : utils.isNumber(val);
var isNully = val === undefined || val === null || val !== val;
var isEmpty = val === '';
el.classed('num-field', isNum);
el.classed('null-value', isNully);
el.classed('empty', isEmpty);
}
function editItem(el, rec, key, type) {
var input = new ClickText2(el),
strval = String(rec[key]),
parser = MapShaper.getInputParser(type);
el.parent().addClass('editable-cell');
el.addClass('colored-text dot-underline');
input.on('change', function(e) {
var val2 = parser(input.value()),
strval2 = String(val2);
if (strval == strval2) {
// contents unchanged
} else if (val2 === null) {
// invalid value; revert to previous value
input.value(strval);
} else {
// field content has changed;
strval = strval2;
rec[key] = val2;
input.value(strval);
setFieldClass(el, val2, type);
}
});
}
}
MapShaper.inputParsers = {
string: function(raw) {
return raw;
},
number: function(raw) {
var val = Number(raw);
if (raw == 'NaN') {
val = NaN;
} else if (isNaN(val)) {
val = null;
}
return val;
},
boolean: function(raw) {
var val = null;
if (raw == 'true') {
val = true;
} else if (raw == 'false') {
val = false;
}
return val;
},
multiple: function(raw) {
var val = Number(raw);
return isNaN(val) ? raw : val;
}
};
MapShaper.getInputParser = function(type) {
return MapShaper.inputParsers[type || 'multiple'];
};
MapShaper.getValueType = function(val) {
var type = null;
if (utils.isString(val)) {
type = 'string';
} else if (utils.isNumber(val)) {
type = 'number';
} else if (utils.isBoolean(val)) {
type = 'boolean';
}
return type;
};
MapShaper.getColumnType = function(key, table) {
var records = table.getRecords(),
type = null;
for (var i=0, n=records.length; i<n; i++) {
type = MapShaper.getValueType(records[i][key]);
if (type) break;
}
return type;
};
MapShaper.getFieldType = function(val, key, table) {
// if a field has a null value, look at entire column to identify type
return MapShaper.getValueType(val) || MapShaper.getColumnType(key, table);
};
function InspectionControl(model, hit) {
var _popup = new Popup();
var _inspecting = false;
var _pinned = false;
var _highId = -1;
var _selectionIds = null;
var btn = gui.addSidebarButton("#info-icon2").on('click', function() {
if (_inspecting) turnOff(); else turnOn();
});
var _self = new EventDispatcher();
var _shapes, _lyr;
_self.updateLayer = function(o) {
var shapes = o.getDisplayLayer().layer.shapes;
if (_inspecting) {
// kludge: check if shapes have changed
if (_shapes == shapes) {
// kludge: re-display the inspector, in case data changed
inspect(_highId, _pinned);
} else {
_selectionIds = null;
inspect(-1, false);
}
}
hit.setLayer(o);
_shapes = shapes;
_lyr = o;
};
// replace cli inspect command
api.inspect = function(lyr, arcs, opts) {
var ids;
if (lyr != model.getEditingLayer().layer) {
error("Only the active layer can be targeted");
}
ids = MapShaper.selectFeatures(lyr, arcs, opts);
if (ids.length === 0) {
message("No features were selected");
return;
}
_selectionIds = ids;
turnOn();
inspect(ids[0], true);
};
document.addEventListener('keydown', function(e) {
var kc = e.keyCode, n, id;
if (!_inspecting) return;
// esc key closes (unless in an editing mode)
if (e.keyCode == 27 && _inspecting && !model.getMode()) {
turnOff();
// arrow keys advance pinned feature unless user is editing text.
} else if ((kc == 37 || kc == 39) && _pinned && !gui.getInputElement()) {
n = MapShaper.getFeatureCount(_lyr.getDisplayLayer().layer);
if (n > 1) {
if (kc == 37) {
id = (_highId + n - 1) % n;
} else {
id = (_highId + 1) % n;
}
inspect(id, true);
e.stopPropagation();
}
}
}, !!'capture'); // preempt the layer control's arrow key handler
hit.on('click', function(e) {
var id = e.id;
var pin = false;
if (_pinned && id == _highId) {
// clicking on pinned shape: unpin
} else if (!_pinned && id > -1) {
// clicking on unpinned shape while unpinned: pin
pin = true;
} else if (_pinned && id > -1) {
// clicking on unpinned shape while pinned: pin new shape
pin = true;
} else if (!_pinned && id == -1) {
// clicking off the layer while pinned: unpin and deselect
}
inspect(id, pin, e.ids);
});
hit.on('hover', function(e) {
var id = e.id;
if (!_inspecting || _pinned) return;
inspect(id, false, e.ids);
});
function showInspector(id, editable) {
var o = _lyr.getDisplayLayer();
var table = o.layer.data || null;
var rec = table ? table.getRecordAt(id) : {};
_popup.show(rec, table, editable);
}
// @id Id of a feature in the active layer, or -1
function inspect(id, pin, ids) {
if (!_inspecting) return;
if (id > -1) {
showInspector(id, pin);
} else {
_popup.hide();
}
_highId = id;
_pinned = pin;
_self.dispatchEvent('change', {
selection_ids: _selectionIds || [],
hover_ids: ids || [],
id: id,
pinned: pin
});
}
function turnOn() {
btn.addClass('selected');
_inspecting = true;
hit.start();
}
function turnOff() {
btn.removeClass('selected');
hit.stop();
_selectionIds = null;
inspect(-1); // clear the map
_inspecting = false;
}
return _self;
}
var MapStyle = (function() {
var darkStroke = "#334",
lightStroke = "#b2d83a",
pink = "#f74b80", // dark
pink2 = "rgba(239, 0, 86, 0.16)", // "#ffd9e7", // medium
gold = "#efc100",
black = "black",
selectionFill = "rgba(237, 214, 0, 0.12)",
hoverFill = "rgba(255, 117, 165, 0.18)",
outlineStyle = {
type: 'outline',
strokeColors: [lightStroke, darkStroke],
strokeWidth: 0.7,
dotColor: "#223"
},
highStyle = {
dotColor: "#F24400"
},
hoverStyles = {
polygon: {
fillColor: hoverFill,
strokeColor: black,
strokeWidth: 1.2
}, point: {
dotColor: black,
dotSize: 8
}, polyline: {
strokeColor: black,
strokeWidth: 2.5
}
},
selectionStyles = {
polygon: {
fillColor: selectionFill,
strokeColor: gold,
strokeWidth: 1
}, point: {
dotColor: gold,
dotSize: 6
}, polyline: {
strokeColor: gold,
strokeWidth: 1.8
}
},
selectionHoverStyles = {
polygon: {
fillColor: selectionFill,
strokeColor: black,
strokeWidth: 1.2
}, point: {
dotColor: black,
dotSize: 6
}, polyline: {
strokeColor: black,
strokeWidth: 2.5
}
},
pinnedStyles = {
polygon: {
fillColor: pink2,
strokeColor: pink,
strokeWidth: 1.8
}, point: {
dotColor: pink,
dotSize: 8
}, polyline: {
strokeColor: pink,
strokeWidth: 3
}
};
return {
getHighlightStyle: function() {
return highStyle;
},
getActiveStyle: function(lyr) {
var style;
if (MapShaper.layerHasSvgDisplayStyle(lyr)) {
style = MapShaper.getSvgDisplayStyle(lyr);
} else {
style = utils.extend({}, outlineStyle);
style.dotSize = calcDotSize(MapShaper.countPointsInLayer(lyr));
}
return style;
},
getOverlayStyle: getOverlayStyle
};
function calcDotSize(n) {
return n < 20 && 5 || n < 500 && 4 || n < 50000 && 3 || 2;
}
function getOverlayStyle(lyr, o) {
var type = lyr.geometry_type;
var topId = o.id;
var ids = [];
var styles = [];
var styler = function(o, i) {
utils.extend(o, styles[i]);
};
var overlayStyle = {
styler: styler
};
// first layer: selected feature(s)
o.selection_ids.forEach(function(i) {
// skip features in a higher layer
if (i == topId || o.hover_ids.indexOf(i) > -1) return;
ids.push(i);
styles.push(selectionStyles[type]);
});
// second layer: hover feature(s)
o.hover_ids.forEach(function(i) {
var style;
if (i == topId) return;
style = o.selection_ids.indexOf(i) > -1 ? selectionHoverStyles[type] : hoverStyles[type];
ids.push(i);
styles.push(style);
});
// top layer: highlighted feature
if (topId > -1) {
var isPinned = o.pinned;
var inSelection = o.selection_ids.indexOf(topId) > -1;
var style;
if (isPinned) {
style = pinnedStyles[type];
} else if (inSelection) {
style = selectionHoverStyles[type]; // TODO: differentiate from other hover ids
} else {
style = hoverStyles[type]; // TODO: differentiate from other hover ids
}
ids.push(topId);
styles.push(style);
}
if (MapShaper.layerHasSvgDisplayStyle(lyr)) {
if (type == 'point') {
overlayStyle = MapShaper.wrapHoverStyle(MapShaper.getSvgDisplayStyle(lyr), overlayStyle);
}
overlayStyle.type = 'styled';
}
overlayStyle.ids = ids;
return ids.length > 0 ? overlayStyle : null;
}
}());
// Modify style to use scaled circle instead of dot symbol
MapShaper.wrapHoverStyle = function(style, hoverStyle) {
var styler = function(obj, i) {
var dotColor;
style.styler(obj, i);
if (hoverStyle.styler) {
hoverStyle.styler(obj, i);
}
dotColor = obj.dotColor;
if (obj.radius && dotColor) {
obj.radius += 1.5;
obj.fillColor = dotColor;
obj.strokeColor = dotColor;
obj.opacity = 1;
}
};
return {styler: styler};
};
MapShaper.getSvgDisplayStyle = function(lyr) {
var records = lyr.data.getRecords(),
fields = MapShaper.getSvgStyleFields(lyr),
index = MapShaper.svgStyles;
var styler = function(style, i) {
var f, key, val;
for (var j=0; j<fields.length; j++) {
f = fields[j];
key = index[f];
val = records[i][f];
if (val == 'none') {
val = 'transparent'; // canvas equivalent
}
style[key] = val;
}
// TODO: make sure canvas rendering matches svg output
if (('strokeWidth' in style) && !style.strokeColor) {
style.strokeColor = 'black';
} else if (!('strokeWidth' in style) && style.strokeColor) {
style.strokeWidth = 1;
}
if (('radius' in style) && !style.strokeColor && !style.fillColor &&
lyr.geometry_type == 'point') {
style.fillColor = 'black';
}
};
return {styler: styler, type: 'styled'};
};
MapShaper.getBoundsOverlap = function(bb1, bb2) {
var area = 0;
if (bb1.intersects(bb2)) {
area = (Math.min(bb1.xmax, bb2.xmax) - Math.max(bb1.xmin, bb2.xmin)) *
(Math.min(bb1.ymax, bb2.ymax) - Math.max(bb1.ymin, bb2.ymin));
}
return area;
};
// Test if map should be re-framed to show updated layer
gui.mapNeedsReset = function(newBounds, prevBounds, mapBounds) {
if (!prevBounds) return true;
if (prevBounds.xmin === 0 || newBounds.xmin === 0) return true; // kludge to handle tables
// TODO: consider similarity of prev and next bounds
//var overlapPct = 2 * MapShaper.getBoundsOverlap(newBounds, prevBounds) /
// (newBounds.area() + prevBounds.area());
var boundsChanged = !prevBounds.equals(newBounds);
var intersects = newBounds.intersects(mapBounds);
// TODO: compare only intersecting portion of layer with map bounds
var areaRatio = newBounds.area() / mapBounds.area();
if (!boundsChanged) return false; // don't reset if layer extent hasn't changed
if (!intersects) return true; // reset if layer is out-of-view
return areaRatio > 500 || areaRatio < 0.05; // reset if layer is not at a viewable scale
};
function MshpMap(model) {
var _root = El('#mshp-main-map'),
_layers = El('#map-layers'),
_ext = new MapExtent(_layers),
_mouse = new MouseArea(_layers.node()),
_nav = new MapNav(_root, _ext, _mouse),
_inspector = new InspectionControl(model, new HitControl(_ext, _mouse));
var _activeCanv = new DisplayCanvas().appendTo(_layers), // data layer shapes
_overlayCanv = new DisplayCanvas().appendTo(_layers), // hover and selection shapes
_annotationCanv = new DisplayCanvas().appendTo(_layers), // used for line intersections
_annotationLyr, _annotationStyle,
_activeLyr, _activeStyle, _overlayStyle;
_ext.on('change', drawLayers);
_inspector.on('change', function(e) {
var lyr = _activeLyr.getDisplayLayer().layer;
_overlayStyle = MapStyle.getOverlayStyle(lyr, e);
drawLayer(_activeLyr, _overlayCanv, _overlayStyle);
});
model.on('select', function(e) {
_annotationStyle = null;
_overlayStyle = null;
});
model.on('update', function(e) {
var prevBounds = _activeLyr ?_activeLyr.getBounds() : null,
needReset = false;
if (arcsMayHaveChanged(e.flags)) {
// regenerate filtered arcs when simplification thresholds are calculated
// or arcs are updated
delete e.dataset.filteredArcs;
// reset simplification after projection (thresholds have changed)
// TODO: preserve simplification pct (need to record pct before change)
if (e.flags.proj
gitextract_hdn3tnup/ ├── README.md ├── codecs.js ├── deflate.js ├── elements.css ├── encode.js ├── index.html ├── manifest.js ├── mapshaper-gui.js ├── mapshaper.js ├── page.css ├── pako.deflate.js ├── pako.inflate.js ├── z-worker.js └── zip.js
SYMBOL INDEX (604 symbols across 9 files)
FILE: codecs.js
function Codec (line 7) | function Codec(isDeflater, options) {
function Deflater (line 51) | function Deflater(options) {
function Inflater (line 55) | function Inflater() {
FILE: deflate.js
function Tree (line 108) | function Tree() {
function StaticTree (line 372) | function StaticTree(static_tree, extra_bits, extra_base, elems, max_leng...
function Config (line 412) | function Config(good_length, max_lazy, nice_length, max_chain, func) {
function smaller (line 470) | function smaller(tree, n, m, depth) {
function Deflate (line 476) | function Deflate() {
function ZStream (line 1883) | function ZStream() {
function Deflater (line 1992) | function Deflater(options) {
FILE: encode.js
function Encoder (line 22) | function Encoder() { }
function encodePolygon (line 83) | function encodePolygon(coordinate, encodeOffsets) {
function addAMDWrapper (line 104) | function addAMDWrapper(jsonStr) {
function addEchartsJsWrapper (line 110) | function addEchartsJsWrapper(jsonStr, fileName) {
function encode (line 137) | function encode(val, prev) {
function quantize (line 160) | function quantize(val) {
FILE: mapshaper-gui.js
function Handler (line 27) | function Handler(type, target, callback, listener, priority) {
function EventData (line 45) | function EventData(type, target, data) {
function EventDispatcher (line 59) | function EventDispatcher() {}
function Elements (line 319) | function Elements(sel) {
function El (line 424) | function El(ref) {
function ElementPosition (line 737) | function ElementPosition(ref) {
function getTimerFunction (line 795) | function getTimerFunction() {
function Timer (line 800) | function Timer() {
function Tween (line 848) | function Tween(ease) {
function MouseWheel (line 880) | function MouseWheel(mouse) {
function MouseArea (line 932) | function MouseArea(element) {
function ErrorMessages (line 1051) | function ErrorMessages(model) {
function ClickText2 (line 1129) | function ClickText2(ref) {
function ClickText (line 1173) | function ClickText(ref) {
function Checkbox (line 1253) | function Checkbox(ref) {
function SimpleButton (line 1259) | function SimpleButton(ref) {
function ModeButton (line 1284) | function ModeButton(el, name, model) {
function draggable (line 1304) | function draggable(ref) {
function Slider (line 1336) | function Slider(ref, opts) {
function turnOn (line 1488) | function turnOn() {
function initMenu (line 1503) | function initMenu() {
function turnOff (line 1514) | function turnOff() {
function onSubmit (line 1519) | function onSubmit() {
function showSlider (line 1542) | function showSlider() {
function getSimplifyOptions (line 1547) | function getSimplifyOptions() {
function toSliderPct (line 1558) | function toSliderPct(p) {
function fromSliderPct (line 1564) | function fromSliderPct(p) {
function onchange (line 1569) | function onchange(val) {
function onError (line 1613) | function onError(err) {
function onDone (line 1617) | function onDone() {
function importZipContent (line 1621) | function importZipContent(reader) {
function DropControl (line 1701) | function DropControl(cb) {
function FileChooser (line 1718) | function FileChooser(el, cb) {
function ImportControl (line 1743) | function ImportControl(model) {
function initLayerMenu (line 2060) | function initLayerMenu() {
function getInputFormats (line 2085) | function getInputFormats() {
function getDefaultExportFormat (line 2093) | function getDefaultExportFormat() {
function initFormatMenu (line 2098) | function initFormatMenu() {
function turnOn (line 2109) | function turnOn() {
function turnOff (line 2115) | function turnOff() {
function getSelectedFormat (line 2119) | function getSelectedFormat() {
function getSelectedLayers (line 2123) | function getSelectedLayers() {
function initExportButton (line 2143) | function initExportButton() {
function exportMenuSelection (line 2166) | function exportMenuSelection(done) {
function isMultiLayerFormat (line 2210) | function isMultiLayerFormat(fmt) {
function saveBlob (line 2214) | function saveBlob(filename, blob, done) {
function saveZipFile (line 2238) | function saveZipFile(zipfileName, files, done) {
function RepairControl (line 2272) | function RepairControl(model, map) {
function LayerControl (line 2376) | function LayerControl(model) {
function ImportFileProxy (line 2515) | function ImportFileProxy(model) {
function DisplayCanvas (line 2557) | function DisplayCanvas() {
function getScaledTransform (line 2675) | function getScaledTransform(ext) {
function drawCircle (line 2679) | function drawCircle(x, y, radius, ctx) {
function drawSquare (line 2686) | function drawSquare(x, y, size, ctx) {
function drawPath (line 2695) | function drawPath(vec, t, ctx) {
function getShapePencil (line 2716) | function getShapePencil(arcs, ext) {
function getPathStart (line 2728) | function getPathStart(style, ext) {
function getPathEnd (line 2768) | function getPathEnd(style) {
function FilteredArcCollection (line 2782) | function FilteredArcCollection(unfilteredArcs) {
function getArc (line 2917) | function getArc(x, y, w, h) {
function DisplayLayer (line 2927) | function DisplayLayer(lyr, dataset, ext) {
function getDisplayBounds (line 3048) | function getDisplayBounds(lyr, arcs) {
function HighlightBox (line 3079) | function HighlightBox(el) {
function MapNav (line 3109) | function MapNav(root, ext, mouse) {
function MapExtent (line 3192) | function MapExtent(el) {
function HitControl (line 3325) | function HitControl(ext, mouse) {
function Popup (line 3515) | function Popup() {
function InspectionControl (line 3664) | function InspectionControl(model, hit) {
function calcDotSize (line 3888) | function calcDotSize(n) {
function getOverlayStyle (line 3892) | function getOverlayStyle(lyr, o) {
function MshpMap (line 4023) | function MshpMap(model) {
function Console (line 4133) | function Console(model) {
function Model (line 4516) | function Model() {
FILE: mapshaper.js
function s (line 1) | function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&re...
function Transform (line 496) | function Transform() {
function Bounds (line 529) | function Bounds() {
function BinArray (line 995) | function BinArray(buf, le) {
function formatValue (line 1312) | function formatValue(val, matches) {
function error (line 1492) | function error() {
function stop (line 1497) | function stop() {
function APIError (line 1501) | function APIError(msg) {
function message (line 1507) | function message() {
function verbose (line 1511) | function verbose() {
function trace (line 1517) | function trace() {
function absArcId (line 1523) | function absArcId(arcId) {
function degreesToMeters (line 1650) | function degreesToMeters(deg) {
function distance3D (line 1654) | function distance3D(ax, ay, az, bx, by, bz) {
function distanceSq (line 1661) | function distanceSq(ax, ay, bx, by) {
function distance2D (line 1667) | function distance2D(ax, ay, bx, by) {
function distanceSq3D (line 1673) | function distanceSq3D(ax, ay, az, bx, by, bz) {
function getRoundingFunction (line 1680) | function getRoundingFunction(inc) {
function nearestPoint (line 1696) | function nearestPoint(x, y, x0, y0) {
function lineIntersection (line 1710) | function lineIntersection(s1p1x, s1p1y, s1p2x, s1p2y, s2p1x, s2p1y, s2p2...
function crossIntersection (line 1721) | function crossIntersection(s1p1x, s1p1y, s1p2x, s1p2y, s2p1x, s2p1y, s2p...
function segmentHit (line 1744) | function segmentHit(s1p1x, s1p1y, s1p2x, s1p2y, s2p1x, s2p1y, s2p2x, s2p...
function inside (line 1751) | function inside(x, minX, maxX) {
function sortSeg (line 1755) | function sortSeg(x1, y1, x2, y2) {
function collinearIntersection (line 1761) | function collinearIntersection(s1p1x, s1p1y, s1p2x, s1p2y, s2p1x, s2p1y,...
function endpointHit (line 1792) | function endpointHit(s1p1x, s1p1y, s1p2x, s1p2y, s2p1x, s2p1y, s2p2x, s2...
function segmentIntersection (line 1802) | function segmentIntersection(s1p1x, s1p1y, s1p2x, s1p2y, s2p1x, s2p1y, s...
function determinant2D (line 1819) | function determinant2D(a, b, c, d) {
function orient2D (line 1824) | function orient2D(x0, y0, x1, y1, x2, y2) {
function innerAngle2 (line 1829) | function innerAngle2(ax, ay, bx, by, cx, cy) {
function standardAngle (line 1858) | function standardAngle(a) {
function signedAngle (line 1869) | function signedAngle(ax, ay, bx, by, cx, cy) {
function bearing (line 1884) | function bearing(lng1, lat1, lng2, lat2) {
function signedAngleSph (line 1897) | function signedAngleSph(alng, alat, blng, blat, clng, clat) {
function convLngLatToSph (line 1928) | function convLngLatToSph(xsrc, ysrc, xbuf, ybuf, zbuf) {
function xyzToLngLat (line 1938) | function xyzToLngLat(x, y, z, p) {
function lngLatToXYZ (line 1946) | function lngLatToXYZ(lng, lat, p) {
function sphericalDistance (line 1957) | function sphericalDistance(lam1, phi1, lam2, phi2) {
function greatCircleDistance (line 1969) | function greatCircleDistance(lng1, lat1, lng2, lat2) {
function innerAngle (line 1976) | function innerAngle(ax, ay, bx, by, cx, cy) {
function innerAngle3D (line 1995) | function innerAngle3D(ax, ay, az, bx, by, bz, cx, cy, cz) {
function triangleArea (line 2014) | function triangleArea(ax, ay, bx, by, cx, cy) {
function detSq (line 2019) | function detSq(ax, ay, bx, by, cx, cy) {
function cosine (line 2024) | function cosine(ax, ay, bx, by, cx, cy) {
function cosine3D (line 2035) | function cosine3D(ax, ay, az, bx, by, bz, cx, cy, cz) {
function triangleArea3D (line 2046) | function triangleArea3D(ax, ay, az, bx, by, bz, cx, cy, cz) {
function apexDistSq (line 2056) | function apexDistSq(ab2, bc2, ac2) {
function pointSegDistSq (line 2074) | function pointSegDistSq(ax, ay, bx, by, cx, cy) {
function pointSegDistSq3D (line 2081) | function pointSegDistSq3D(ax, ay, az, bx, by, bz, cx, cy, cz) {
function mergeBounds (line 2121) | function mergeBounds(a, b) {
function containsBounds (line 2128) | function containsBounds(a, b) {
function boundsArea (line 2132) | function boundsArea(b) {
function ArcIter (line 2178) | function ArcIter(xx, yy) {
function FilteredArcIter (line 2214) | function FilteredArcIter(xx, yy, zz) {
function ShapeIter (line 2257) | function ShapeIter(arcs) {
function ArcCollection (line 2311) | function ArcCollection() {
function swap (line 3507) | function swap(i, j) {
function getUint32Array (line 3588) | function getUint32Array(count) {
function multiStripeId (line 3608) | function multiStripeId(y) {
function singleStripeId (line 3612) | function singleStripeId(y) {return 0;}
function getEndpointIds (line 3776) | function getEndpointIds(id1, id2, p) {
function addSegLen (line 4106) | function addSegLen(i, j, xx, yy) {
function PolygonIndex (line 4124) | function PolygonIndex(shape, arcs, opts) {
function PathIndex (line 4250) | function PathIndex(shapes, arcs) {
function NodeCollection (line 4420) | function NodeCollection(arcs, filter) {
function contains (line 4602) | function contains(arr, el) {
function dividePathAtNode (line 4612) | function dividePathAtNode(path, enterId) {
function dividePath (line 4642) | function dividePath(path) {
function getNextArc (line 4798) | function getNextArc(prevId) {
function chooseRoute (line 4916) | function chooseRoute(id1, angle1, id2, angle2, prevId) {
function routeIsActive (line 4927) | function routeIsActive(arcId) {
function useRoute (line 4932) | function useRoute(arcId) {
function bitsToString (line 4952) | function bitsToString(bits) {
function cleanPolygon (line 5124) | function cleanPolygon(shp) {
function remapPathIds (line 5209) | function remapPathIds(ids) {
function remapArcId (line 5218) | function remapArcId(id, ids) {
function getSegmentIntersection (line 5339) | function getSegmentIntersection(x, y, ids) {
function DbfReader (line 5689) | function DbfReader(src, encodingArg) {
function DataTable (line 6240) | function DataTable(obj) {
function addGetters (line 6797) | function addGetters(obj, getters) {
function FeatureExpressionContext (line 6803) | function FeatureExpressionContext(lyr, arcs) {
function dissolvePointLayerGeometry (line 6957) | function dissolvePointLayerGeometry(lyr, getGroupId, opts) {
function reducePointCentroid (line 6995) | function reducePointCentroid(memo, p, weight) {
function dissolvePolygonGeometry (line 7020) | function dissolvePolygonGeometry(shapes, getGroupId) {
function dissolveFirstPass (line 7026) | function dissolveFirstPass(shapes, getGroupId) {
function dissolveSecondPass (line 7129) | function dissolveSecondPass(segments, shapes, getGroupId) {
function getNextSegment (line 7217) | function getNextSegment(seg, segments, shapes) {
function getPrevSegment (line 7221) | function getPrevSegment(seg, segments, shapes) {
function getSegmentByOffs (line 7225) | function getSegmentByOffs(seg, segments, shapes, offs) {
function clipPolygon (line 7426) | function clipPolygon(shape, type, index) {
function routeIsActive (line 7470) | function routeIsActive(id) {
function useRoute (line 7481) | function useRoute(id) {
function chooseRoute (line 7533) | function chooseRoute(id1, angle1, id2, angle2, prevId) {
function findUndividedClipShapes (line 7552) | function findUndividedClipShapes(clipShapes) {
function arcIsUnused (line 7577) | function arcIsUnused(id, flags) {
function arcIsVisible (line 7583) | function arcIsVisible(id, flags) {
function findInteriorPaths (line 7590) | function findInteriorPaths(shape, type, index) {
function clipPolyline (line 7627) | function clipPolyline(shp) {
function clipPath (line 7632) | function clipPath(memo, path) {
function translatePath (line 7701) | function translatePath(path) {
function extendDissolvedArc (line 7764) | function extendDissolvedArc(oldId, newId) {
function countArc (line 7800) | function countArc(arcId, i) {
function getXYHash (line 8279) | function getXYHash(size) {
function getXHash (line 8299) | function getXHash(size) {
function ArcIndex (line 8322) | function ArcIndex(pointCount) {
function initHashChains (line 8409) | function initHashChains(xx, yy) {
function initPointChains (line 8427) | function initPointChains(xx, yy) {
function usingTypedArrays (line 8507) | function usingTypedArrays() {
function convertPaths (line 8511) | function convertPaths(nn) {
function nextPoint (line 8523) | function nextPoint(id) {
function prevPoint (line 8533) | function prevPoint(id) {
function sameXY (line 8543) | function sameXY(a, b) {
function convertPath (line 8551) | function convertPath(start, end) {
function pointIsArcEndpoint (line 8588) | function pointIsArcEndpoint(id) {
function brokenEdge (line 8611) | function brokenEdge(aprev, anext, bprev, bnext) {
function mergeArcParts (line 8627) | function mergeArcParts(src, startId, endId, startId2, endId2) {
function addSplitEdge (line 8641) | function addSplitEdge(start1, end1, start2, end2) {
function addEdge (line 8650) | function addEdge(start, end) {
function addRing (line 8660) | function addRing(startId, endId) {
function initPathIds (line 8687) | function initPathIds(size, pathSizes) {
function replaceArcsInShape (line 8703) | function replaceArcsInShape(shape, replacements) {
function replaceArcsInPath (line 8710) | function replaceArcsInPath(path, replacements) {
function snapPoint (line 8769) | function snapPoint(i, limit, ids, xx, yy) {
function PathImportStream (line 8898) | function PathImportStream(drain) {
function PathImporter (line 8925) | function PathImporter(opts) {
function replace (line 9231) | function replace(key, val) {
function eachGeom (line 9557) | function eachGeom(o) {
function eachMultiPath (line 9563) | function eachMultiPath(arr) {
function ShpRecordClass (line 10188) | function ShpRecordClass(type) {
function ShpReader (line 10692) | function ShpReader(src) {
function BufferBytes (line 10844) | function BufferBytes(buf) {
function FileBytes (line 10861) | function FileBytes(path) {
function ShapefileTable (line 11107) | function ShapefileTable(buf, encoding) {
function FeatureCalculator (line 11941) | function FeatureCalculator() {
function resolveFileCollisions (line 12240) | function resolveFileCollisions(candidates) {
function addFileSuffix (line 12254) | function addFileSuffix(paths, suff) {
function testFileCollision (line 12260) | function testFileCollision(paths) {
function addLines (line 12472) | function addLines(lines) {
function onArc (line 12509) | function onArc(o) {
function onPart (line 12537) | function onPart(o) {
function classify (line 12567) | function classify(i, getKey) {
function layerKey (line 13399) | function layerKey(lyr) {
function initProj (line 13631) | function initProj(proj, name, opts, params) {
function initProjUnits (line 13696) | function initProjUnits(units) {
function WebMercator (line 13708) | function WebMercator() {
function Mercator (line 13713) | function Mercator(opts) {
function UTM (line 13735) | function UTM(opts) {
function TransverseMercator (line 13751) | function TransverseMercator(opts) {
function sinh (line 13789) | function sinh(x) {
function cosh (line 13794) | function cosh(x) {
function calcTransMercM (line 13798) | function calcTransMercM(lat, e) {
function AlbersNYT (line 13808) | function AlbersNYT(opts) {
function AlbersUSA (line 13815) | function AlbersUSA(opts) {
function AlbersEqualAreaConic (line 13830) | function AlbersEqualAreaConic(opts) {
function calcAlbersQell (line 13888) | function calcAlbersQell(e, lat) {
function calcAlbersMell (line 13894) | function calcAlbersMell(e, lat) {
function LambertConformalConic (line 13904) | function LambertConformalConic(opts) {
function calcLambertT (line 13957) | function calcLambertT(lat, e) {
function calcLambertM (line 13963) | function calcLambertM(lat, e) {
function WinkelTripel (line 13968) | function WinkelTripel() {
function Robinson (line 13979) | function Robinson() {
function MixedProjection (line 14039) | function MixedProjection(proj) {
function Matrix2D (line 14092) | function Matrix2D() {
function append (line 14147) | function append(p) {
function editArc (line 14153) | function editArc(arc, cb) {
function onPoint (line 14283) | function onPoint(append, lng, lat, prevLng, prevLat, i) {
function Heap (line 14321) | function Heap() {
function procSegment (line 14608) | function procSegment(startIdx, endIdx, depth, distSqPrev) {
function pointSegGeoDistSq (line 14913) | function pointSegGeoDistSq(alng, alat, blng, blat, clng, clat) {
function groupId (line 15271) | function groupId(shpBounds) {
function groupName (line 15279) | function groupName(i) {
function done (line 15604) | function done(err, output) {
function CommandParser (line 15635) | function CommandParser() {
function CommandOptions (line 15952) | function CommandOptions(name) {
function validateHelpOpts (line 16004) | function validateHelpOpts(cmd) {
function validateInputOpts (line 16011) | function validateInputOpts(cmd) {
function validateSimplifyOpts (line 16030) | function validateSimplifyOpts(cmd) {
function validateJoinOpts (line 16069) | function validateJoinOpts(cmd) {
function validateSplitOpts (line 16077) | function validateSplitOpts(cmd) {
function validateClipOpts (line 16085) | function validateClipOpts(cmd) {
function validateDissolveOpts (line 16104) | function validateDissolveOpts(cmd) {
function validateMergeLayersOpts (line 16114) | function validateMergeLayersOpts(cmd) {
function validateRenameLayersOpts (line 16118) | function validateRenameLayersOpts(cmd) {
function validateSplitOnGridOpts (line 16122) | function validateSplitOnGridOpts(cmd) {
function validateLinesOpts (line 16135) | function validateLinesOpts(cmd) {
function validateInnerLinesOpts (line 16145) | function validateInnerLinesOpts(cmd) {
function validateSubdivideOpts (line 16151) | function validateSubdivideOpts(cmd) {
function validateFilterFieldsOpts (line 16158) | function validateFilterFieldsOpts(cmd) {
function validateExpressionOpts (line 16167) | function validateExpressionOpts(cmd) {
function validateOutputOpts (line 16175) | function validateOutputOpts(cmd) {
function validateCommaSepNames (line 16242) | function validateCommaSepNames(str, min) {
function next (line 17172) | function next(err, memo) {
function init (line 17266) | function init () {
function toByteArray (line 17279) | function toByteArray (b64) {
function tripletToBase64 (line 17321) | function tripletToBase64 (num) {
function encodeChunk (line 17325) | function encodeChunk (uint8, start, end) {
function fromByteArray (line 17335) | function fromByteArray (uint8) {
function typedArraySupport (line 17424) | function typedArraySupport () {
function kMaxLength (line 17436) | function kMaxLength () {
function createBuffer (line 17442) | function createBuffer (that, length) {
function Buffer (line 17471) | function Buffer (arg, encodingOrOffset, length) {
function from (line 17496) | function from (that, value, encodingOrOffset, length) {
function assertSize (line 17537) | function assertSize (size) {
function alloc (line 17543) | function alloc (that, size, fill, encoding) {
function allocUnsafe (line 17567) | function allocUnsafe (that, size) {
function fromString (line 17591) | function fromString (that, string, encoding) {
function fromArrayLike (line 17607) | function fromArrayLike (that, array) {
function fromArrayBuffer (line 17616) | function fromArrayBuffer (that, array, byteOffset, length) {
function fromObject (line 17644) | function fromObject (that, obj) {
function checked (line 17674) | function checked (length) {
function SlowBuffer (line 17684) | function SlowBuffer (length) {
function byteLength (line 17767) | function byteLength (string, encoding) {
function slowToString (line 17814) | function slowToString (encoding, start, end) {
function swap (line 17887) | function swap (b, n, m) {
function arrayIndexOf (line 17998) | function arrayIndexOf (arr, val, byteOffset, encoding) {
function hexWrite (line 18080) | function hexWrite (buf, string, offset, length) {
function utf8Write (line 18107) | function utf8Write (buf, string, offset, length) {
function asciiWrite (line 18111) | function asciiWrite (buf, string, offset, length) {
function binaryWrite (line 18115) | function binaryWrite (buf, string, offset, length) {
function base64Write (line 18119) | function base64Write (buf, string, offset, length) {
function ucs2Write (line 18123) | function ucs2Write (buf, string, offset, length) {
function base64Slice (line 18205) | function base64Slice (buf, start, end) {
function utf8Slice (line 18213) | function utf8Slice (buf, start, end) {
function decodeCodePointsArray (line 18291) | function decodeCodePointsArray (codePoints) {
function asciiSlice (line 18309) | function asciiSlice (buf, start, end) {
function binarySlice (line 18319) | function binarySlice (buf, start, end) {
function hexSlice (line 18329) | function hexSlice (buf, start, end) {
function utf16leSlice (line 18342) | function utf16leSlice (buf, start, end) {
function checkOffset (line 18390) | function checkOffset (offset, ext, length) {
function checkInt (line 18551) | function checkInt (buf, value, offset, ext, max, min) {
function objectWriteUInt16 (line 18604) | function objectWriteUInt16 (buf, value, offset, littleEndian) {
function objectWriteUInt32 (line 18638) | function objectWriteUInt32 (buf, value, offset, littleEndian) {
function checkIEEE754 (line 18788) | function checkIEEE754 (buf, value, offset, ext, max, min) {
function writeFloat (line 18793) | function writeFloat (buf, value, offset, littleEndian, noAssert) {
function writeDouble (line 18809) | function writeDouble (buf, value, offset, littleEndian, noAssert) {
function base64clean (line 18942) | function base64clean (str) {
function stringtrim (line 18954) | function stringtrim (str) {
function toHex (line 18959) | function toHex (n) {
function utf8ToBytes (line 18964) | function utf8ToBytes (string, units) {
function asciiToBytes (line 19044) | function asciiToBytes (str) {
function utf16leToBytes (line 19053) | function utf16leToBytes (str, units) {
function base64ToBytes (line 19069) | function base64ToBytes (str) {
function blitBuffer (line 19073) | function blitBuffer (src, dst, offset, length) {
function isnan (line 19081) | function isnan (val) {
function isArray (line 19119) | function isArray(arg) {
function isBoolean (line 19127) | function isBoolean(arg) {
function isNull (line 19132) | function isNull(arg) {
function isNullOrUndefined (line 19137) | function isNullOrUndefined(arg) {
function isNumber (line 19142) | function isNumber(arg) {
function isString (line 19147) | function isString(arg) {
function isSymbol (line 19152) | function isSymbol(arg) {
function isUndefined (line 19157) | function isUndefined(arg) {
function isRegExp (line 19162) | function isRegExp(re) {
function isObject (line 19167) | function isObject(arg) {
function isDate (line 19172) | function isDate(d) {
function isError (line 19177) | function isError(e) {
function isFunction (line 19182) | function isFunction(arg) {
function isPrimitive (line 19187) | function isPrimitive(arg) {
function objectToString (line 19199) | function objectToString(o) {
function objectConverter (line 19213) | function objectConverter(columns) {
function customConverter (line 19219) | function customConverter(columns, f) {
function inferColumns (line 19227) | function inferColumns(rows) {
function dsv (line 19242) | function dsv(delimiter) {
function EventEmitter (line 19396) | function EventEmitter() {
function g (line 19530) | function g() {
function isFunction (line 19658) | function isFunction(arg) {
function isNumber (line 19662) | function isNumber(arg) {
function isObject (line 19666) | function isObject(arg) {
function isUndefined (line 19670) | function isUndefined(arg) {
function DBCSCodec (line 19696) | function DBCSCodec(codecOptions, iconv) {
function DBCSEncoder (line 19945) | function DBCSEncoder(options, codec) {
function DBCSDecoder (line 20114) | function DBCSDecoder(options, codec) {
function findIdx (line 20215) | function findIdx(table, val) {
function InternalCodec (line 20453) | function InternalCodec(codecOptions, iconv) {
function InternalDecoder (line 20483) | function InternalDecoder(options, codec) {
function InternalEncoder (line 20493) | function InternalEncoder(options, codec) {
function InternalEncoderBase64 (line 20508) | function InternalEncoderBase64(options, codec) {
function InternalEncoderCesu8 (line 20529) | function InternalEncoderCesu8(options, codec) {
function InternalDecoderCesu8 (line 20559) | function InternalDecoderCesu8(options, codec) {
function SBCSCodec (line 20627) | function SBCSCodec(codecOptions, iconv) {
function SBCSEncoder (line 20658) | function SBCSEncoder(options, codec) {
function SBCSDecoder (line 20674) | function SBCSDecoder(options, codec) {
function Utf16BECodec (line 22539) | function Utf16BECodec() {
function Utf16BEEncoder (line 22549) | function Utf16BEEncoder() {
function Utf16BEDecoder (line 22566) | function Utf16BEDecoder() {
function Utf16Codec (line 22606) | function Utf16Codec(codecOptions, iconv) {
function Utf16Encoder (line 22616) | function Utf16Encoder(options, codec) {
function Utf16Decoder (line 22634) | function Utf16Decoder(options, codec) {
function detectEncoding (line 22676) | function detectEncoding(buf, defaultEncoding) {
function Utf7Codec (line 22719) | function Utf7Codec(codecOptions, iconv) {
function Utf7Encoder (line 22732) | function Utf7Encoder(options, codec) {
function Utf7Decoder (line 22752) | function Utf7Decoder(options, codec) {
function Utf7IMAPCodec (line 22843) | function Utf7IMAPCodec(codecOptions, iconv) {
function Utf7IMAPEncoder (line 22854) | function Utf7IMAPEncoder(options, codec) {
function Utf7IMAPDecoder (line 22928) | function Utf7IMAPDecoder(options, codec) {
function PrependBOMWrapper (line 23009) | function PrependBOMWrapper(encoder, options) {
function StripBOMWrapper (line 23031) | function StripBOMWrapper(decoder, options) {
function IconvLiteEncoderStream (line 23450) | function IconvLiteEncoderStream(conv, options) {
function IconvLiteDecoderStream (line 23497) | function IconvLiteDecoderStream(conv, options) {
function normalizeArray (line 23701) | function normalizeArray(parts, allowAboveRoot) {
function trim (line 23810) | function trim(arr) {
function filter (line 23883) | function filter (xs, f) {
function nextTick (line 23914) | function nextTick(fn) {
function cleanUpNextTick (line 23935) | function cleanUpNextTick() {
function drainQueue (line 23947) | function drainQueue() {
function Item (line 23985) | function Item(fun, array) {
function noop (line 23999) | function noop() {}
function rbush (line 24029) | function rbush(maxEntries, format) {
function calcBBox (line 24503) | function calcBBox(node, toBBox) {
function distBBox (line 24508) | function distBBox(node, k, p, toBBox) {
function empty (line 24519) | function empty() { return [Infinity, Infinity, -Infinity, -Infinity]; }
function extend (line 24521) | function extend(a, b) {
function compareNodeMinX (line 24529) | function compareNodeMinX(a, b) { return a.bbox[0] - b.bbox[0]; }
function compareNodeMinY (line 24530) | function compareNodeMinY(a, b) { return a.bbox[1] - b.bbox[1]; }
function bboxArea (line 24532) | function bboxArea(a) { return (a[2] - a[0]) * (a[3] - a[1]); }
function bboxMargin (line 24533) | function bboxMargin(a) { return (a[2] - a[0]) + (a[3] - a[1]); }
function enlargedArea (line 24535) | function enlargedArea(a, b) {
function intersectionArea (line 24540) | function intersectionArea(a, b) {
function contains (line 24550) | function contains(a, b) {
function intersects (line 24557) | function intersects(a, b) {
function multiSelect (line 24567) | function multiSelect(arr, left, right, n, compare) {
function select (line 24586) | function select(arr, left, right, k, compare) {
function swap (line 24627) | function swap(arr, i, j) {
function Duplex (line 24685) | function Duplex(options) {
function onend (line 24702) | function onend() {
function onEndNT (line 24712) | function onEndNT(self) {
function forEach (line 24716) | function forEach(xs, f) {
function PassThrough (line 24739) | function PassThrough(options) {
function ReadableState (line 24809) | function ReadableState(options, stream) {
function Readable (line 24876) | function Readable(options) {
function readableAddChunk (line 24919) | function readableAddChunk(stream, state, chunk, encoding, addToFront) {
function needMoreData (line 24974) | function needMoreData(state) {
function computeNewHighWaterMark (line 24988) | function computeNewHighWaterMark(n) {
function howMuchToRead (line 25004) | function howMuchToRead(n, state) {
function chunkInvalid (line 25136) | function chunkInvalid(state, chunk) {
function onEofChunk (line 25144) | function onEofChunk(stream, state) {
function emitReadable (line 25162) | function emitReadable(stream) {
function emitReadable_ (line 25172) | function emitReadable_(stream) {
function maybeReadMore (line 25184) | function maybeReadMore(stream, state) {
function maybeReadMore_ (line 25191) | function maybeReadMore_(stream, state) {
function onunpipe (line 25235) | function onunpipe(readable) {
function onend (line 25242) | function onend() {
function cleanup (line 25255) | function cleanup() {
function ondata (line 25278) | function ondata(chunk) {
function onerror (line 25295) | function onerror(er) {
function onclose (line 25306) | function onclose() {
function onfinish (line 25311) | function onfinish() {
function unpipe (line 25318) | function unpipe() {
function pipeOnDrain (line 25335) | function pipeOnDrain(src) {
function nReadingNextTick (line 25425) | function nReadingNextTick(self) {
function resume (line 25442) | function resume(stream, state) {
function resume_ (line 25449) | function resume_(stream, state) {
function flow (line 25471) | function flow(stream) {
function fromList (line 25549) | function fromList(n, state) {
function endReadable (line 25596) | function endReadable(stream) {
function endReadableNT (line 25609) | function endReadableNT(state, stream) {
function forEach (line 25618) | function forEach(xs, f) {
function indexOf (line 25624) | function indexOf(xs, x) {
function TransformState (line 25687) | function TransformState(stream) {
function afterTransform (line 25699) | function afterTransform(stream, er, data) {
function Transform (line 25721) | function Transform(options) {
function done (line 25798) | function done(stream, er) {
function nop (line 25861) | function nop() {}
function WriteReq (line 25863) | function WriteReq(chunk, encoding, cb) {
function WritableState (line 25871) | function WritableState(options, stream) {
function Writable (line 25988) | function Writable(options) {
function writeAfterEnd (line 26014) | function writeAfterEnd(stream, cb) {
function validChunk (line 26026) | function validChunk(stream, state, chunk, cb) {
function decodeChunk (line 26082) | function decodeChunk(state, chunk, encoding) {
function writeOrBuffer (line 26092) | function writeOrBuffer(stream, state, chunk, encoding, cb) {
function doWrite (line 26120) | function doWrite(stream, state, writev, len, chunk, encoding, cb) {
function onwriteError (line 26129) | function onwriteError(stream, state, sync, er, cb) {
function onwriteStateUpdate (line 26137) | function onwriteStateUpdate(state) {
function onwrite (line 26144) | function onwrite(stream, er) {
function afterWrite (line 26169) | function afterWrite(stream, state, finished, cb) {
function onwriteDrain (line 26179) | function onwriteDrain(stream, state) {
function clearBuffer (line 26187) | function clearBuffer(stream, state) {
function needFinish (line 26270) | function needFinish(state) {
function prefinish (line 26274) | function prefinish(stream, state) {
function finishMaybe (line 26281) | function finishMaybe(stream, state) {
function endWritable (line 26295) | function endWritable(stream, state, cb) {
function CorkedRequest (line 26307) | function CorkedRequest(state) {
function dashify (line 26370) | function dashify(method, file) {
function identity (line 26393) | function identity() {
function encoding (line 26401) | function encoding(encoding) {
function Stream (line 26586) | function Stream() {
function ondata (line 26593) | function ondata(chunk) {
function ondrain (line 26603) | function ondrain() {
function onend (line 26619) | function onend() {
function onclose (line 26627) | function onclose() {
function onerror (line 26635) | function onerror(er) {
function cleanup (line 26646) | function cleanup() {
function assertEncoding (line 26706) | function assertEncoding(encoding) {
function passThroughWrite (line 26882) | function passThroughWrite(buffer) {
function utf16DetectIncompleteChar (line 26886) | function utf16DetectIncompleteChar(buffer) {
function base64DetectIncompleteChar (line 26891) | function base64DetectIncompleteChar(buffer) {
function deprecate (line 26923) | function deprecate (fn, msg) {
function config (line 26954) | function config (name) {
FILE: pako.deflate.js
function r (line 2) | function r(s,h){if(!a[s]){if(!e[s]){var l="function"==typeof require&&re...
function n (line 2) | function n(t,e){if(65537>e&&(t.subarray&&s||!t.subarray&&i))return Strin...
function n (line 2) | function n(t,e,a,n){for(var r=65535&t|0,i=t>>>16&65535|0,s=0;0!==a;){s=a...
function n (line 2) | function n(){for(var t,e=[],a=0;256>a;a++){t=a;for(var n=0;8>n;n++)t=1&t...
function r (line 2) | function r(t,e,a,n){var r=i,s=n+a;t=-1^t;for(var h=n;s>h;h++)t=t>>>8^r[2...
function n (line 2) | function n(t,e){return t.msg=I[e],e}
function r (line 2) | function r(t){return(t<<1)-(t>4?9:0)}
function i (line 2) | function i(t){for(var e=t.length;--e>=0;)t[e]=0}
function s (line 2) | function s(t){var e=t.state,a=e.pending;a>t.avail_out&&(a=t.avail_out),0...
function h (line 2) | function h(t,e){j._tr_flush_block(t,t.block_start>=0?t.block_start:-1,t....
function l (line 2) | function l(t,e){t.pending_buf[t.pending++]=e}
function o (line 2) | function o(t,e){t.pending_buf[t.pending++]=e>>>8&255,t.pending_buf[t.pen...
function _ (line 2) | function _(t,e,a,n){var r=t.avail_in;return r>n&&(r=n),0===r?0:(t.avail_...
function d (line 2) | function d(t,e){var a,n,r=t.max_chain_length,i=t.strstart,s=t.prev_lengt...
function u (line 2) | function u(t){var e,a,n,r,i,s=t.w_size;do{if(r=t.window_size-t.lookahead...
function f (line 2) | function f(t,e){var a=65535;for(a>t.pending_buf_size-5&&(a=t.pending_buf...
function c (line 2) | function c(t,e){for(var a,n;;){if(t.lookahead<ot){if(u(t),t.lookahead<ot...
function g (line 2) | function g(t,e){for(var a,n,r;;){if(t.lookahead<ot){if(u(t),t.lookahead<...
function p (line 2) | function p(t,e){for(var a,n,r,i,s=t.window;;){if(t.lookahead<=lt){if(u(t...
function m (line 2) | function m(t,e){for(var a;;){if(0===t.lookahead&&(u(t),0===t.lookahead))...
function b (line 2) | function b(t){t.window_size=2*t.w_size,i(t.head),t.max_lazy_match=C[t.le...
function v (line 2) | function v(){this.strm=null,this.status=0,this.pending_buf=null,this.pen...
function w (line 2) | function w(t){var e;return t&&t.state?(t.total_in=t.total_out=0,t.data_t...
function y (line 2) | function y(t){var e=w(t);return e===N&&b(t.state),e}
function z (line 2) | function z(t,e){return t&&t.state?2!==t.state.wrap?H:(t.state.gzhead=e,N...
function k (line 2) | function k(t,e,a,r,i,s){if(!t)return H;var h=1;if(e===M&&(e=6),0>r?(h=0,...
function x (line 2) | function x(t,e){return k(t,e,X,Z,$,V)}
function B (line 2) | function B(t,e){var a,h,_,d;if(!t||!t.state||e>L||0>e)return t?n(t,H):H;...
function A (line 2) | function A(t){var e;return t&&t.state?(e=t.state.status,e!==dt&&e!==ut&&...
function n (line 2) | function n(t){for(var e=t.length;--e>=0;)t[e]=0}
function r (line 2) | function r(t){return 256>t?st[t]:st[256+(t>>>7)]}
function i (line 2) | function i(t,e){t.pending_buf[t.pending++]=255&e,t.pending_buf[t.pending...
function s (line 2) | function s(t,e,a){t.bi_valid>Q-a?(t.bi_buf|=e<<t.bi_valid&65535,i(t,t.bi...
function h (line 2) | function h(t,e,a){s(t,a[2*e],a[2*e+1])}
function l (line 2) | function l(t,e){var a=0;do a|=1&t,t>>>=1,a<<=1;while(--e>0);return a>>>1}
function o (line 2) | function o(t){16===t.bi_valid?(i(t,t.bi_buf),t.bi_buf=0,t.bi_valid=0):t....
function _ (line 2) | function _(t,e){var a,n,r,i,s,h,l=e.dyn_tree,o=e.max_code,_=e.stat_desc....
function d (line 2) | function d(t,e,a){var n,r,i=new Array(J+1),s=0;for(n=1;J>=n;n++)i[n]=s=s...
function u (line 2) | function u(){var t,e,a,n,r,i=new Array(J+1);for(a=0,n=0;H-1>n;n++)for(lt...
function f (line 2) | function f(t){var e;for(e=0;K>e;e++)t.dyn_ltree[2*e]=0;for(e=0;M>e;e++)t...
function c (line 2) | function c(t){t.bi_valid>8?i(t,t.bi_buf):t.bi_valid>0&&(t.pending_buf[t....
function g (line 2) | function g(t,e,a,n){c(t),n&&(i(t,a),i(t,~a)),E.arraySet(t.pending_buf,t....
function p (line 2) | function p(t,e,a,n){var r=2*e,i=2*a;return t[r]<t[i]||t[r]===t[i]&&n[e]<...
function m (line 2) | function m(t,e,a){for(var n=t.heap[a],r=a<<1;r<=t.heap_len&&(r<t.heap_le...
function b (line 2) | function b(t,e,a){var n,i,l,o,_=0;if(0!==t.last_lit)do n=t.pending_buf[t...
function v (line 2) | function v(t,e){var a,n,r,i=e.dyn_tree,s=e.stat_desc.static_tree,h=e.sta...
function w (line 2) | function w(t,e,a){var n,r,i=-1,s=e[1],h=0,l=7,o=4;for(0===s&&(l=138,o=3)...
function y (line 2) | function y(t,e,a){var n,r,i=-1,l=e[1],o=0,_=7,d=4;for(0===l&&(_=138,d=3)...
function z (line 2) | function z(t){var e;for(w(t,t.dyn_ltree,t.l_desc.max_code),w(t,t.dyn_dtr...
function k (line 2) | function k(t,e,a,n){var r;for(s(t,e-257,5),s(t,a-1,5),s(t,n-4,4),r=0;n>r...
function x (line 2) | function x(t){var e,a=4093624447;for(e=0;31>=e;e++,a>>>=1)if(1&a&&0!==t....
function B (line 2) | function B(t){gt||(u(),gt=!0),t.l_desc=new ct(t.dyn_ltree,_t),t.d_desc=n...
function A (line 2) | function A(t,e,a,n){s(t,(q<<1)+(n?1:0),3),g(t,e,a,!0)}
function C (line 2) | function C(t){s(t,T<<1,3),h(t,W,rt),o(t)}
function S (line 2) | function S(t,e,a,n){var r,i,h=0;t.level>0?(t.strm.data_type===O&&(t.strm...
function j (line 2) | function j(t,e,a){return t.pending_buf[t.d_buf+2*t.last_lit]=e>>>8&255,t...
function n (line 2) | function n(){this.input=null,this.next_in=0,this.avail_in=0,this.total_i...
function n (line 2) | function n(t,e){var a=new w(e);if(a.push(t,!0),a.err)throw a.msg;return ...
function r (line 2) | function r(t,e){return e=e||{},e.raw=!0,n(t,e)}
function i (line 2) | function i(t,e){return e=e||{},e.gzip=!0,n(t,e)}
FILE: pako.inflate.js
function a (line 2) | function a(o,s){if(!i[o]){if(!t[o]){var f="function"==typeof require&&re...
function n (line 2) | function n(e,t){if(65537>t&&(e.subarray&&o||!e.subarray&&r))return Strin...
function n (line 2) | function n(e,t,i,n){for(var a=65535&e|0,r=e>>>16&65535|0,o=0;0!==i;){o=i...
function n (line 2) | function n(){for(var e,t=[],i=0;256>i;i++){e=i;for(var n=0;8>n;n++)e=1&e...
function a (line 2) | function a(e,t,i,n){var a=r,o=n+i;e=-1^e;for(var s=n;o>s;s++)e=e>>>8^a[2...
function n (line 2) | function n(){this.text=0,this.time=0,this.xflags=0,this.os=0,this.extra=...
function n (line 2) | function n(e){return(e>>>24&255)+(e>>>8&65280)+((65280&e)<<8)+((255&e)<<...
function a (line 2) | function a(){this.mode=0,this.last=!1,this.wrap=0,this.havedict=!1,this....
function r (line 2) | function r(e){var t;return e&&e.state?(t=e.state,e.total_in=e.total_out=...
function o (line 2) | function o(e){var t;return e&&e.state?(t=e.state,t.wsize=0,t.whave=0,t.w...
function s (line 2) | function s(e,t){var i,n;return e&&e.state?(n=e.state,0>t?(i=0,t=-t):(i=(...
function f (line 2) | function f(e,t){var i,n;return e?(n=new a,e.state=n,n.window=null,i=s(e,...
function l (line 2) | function l(e){return f(e,ke)}
function d (line 2) | function d(e){if(_e){var t;for(w=new k.Buf32(512),m=new k.Buf32(32),t=0;...
function u (line 2) | function u(e,t,i,n){var a,r=e.state;return null===r.window&&(r.wsize=1<<...
function h (line 2) | function h(e,t){var i,a,r,o,s,f,l,h,c,b,w,m,be,we,me,ke,_e,ge,ve,pe,xe,y...
function c (line 2) | function c(e){if(!e||!e.state)return R;var t=e.state;return t.window&&(t...
function b (line 2) | function b(e,t){var i;return e&&e.state?(i=e.state,0===(2&i.wrap)?R:(i.h...
function n (line 2) | function n(){this.input=null,this.next_in=0,this.avail_in=0,this.total_i...
function n (line 2) | function n(e,t){var i=new c(t);if(i.push(e,!0),i.err)throw i.msg;return ...
function a (line 2) | function a(e,t){return t=t||{},t.raw=!0,n(e,t)}
FILE: z-worker.js
function doImportScripts (line 33) | function doImportScripts(msg) {
function newTask (line 39) | function newTask(msg) {
function processData (line 56) | function processData(msg) {
function onError (line 106) | function onError(type, sn, e) {
function formatError (line 115) | function formatError(e) {
function Crc32 (line 120) | function Crc32() {
function NOOP (line 147) | function NOOP() {}
FILE: zip.js
function Crc32 (line 51) | function Crc32() {
function NOOP (line 78) | function NOOP() {}
function blobSlice (line 84) | function blobSlice(blob, index, length) {
function getDataHelper (line 97) | function getDataHelper(byteLength, bytes) {
function Reader (line 111) | function Reader() {
function TextReader (line 114) | function TextReader(text) {
function Data64URIReader (line 139) | function Data64URIReader(dataURI) {
function BlobReader (line 169) | function BlobReader(blob) {
function Writer (line 199) | function Writer() {
function TextWriter (line 205) | function TextWriter(encoding) {
function Data64URIWriter (line 238) | function Data64URIWriter(contentType) {
function BlobWriter (line 271) | function BlobWriter(contentType) {
function launchWorkerProcess (line 306) | function launchWorkerProcess(worker, initialMessage, reader, writer, off...
function launchProcess (line 392) | function launchProcess(process, reader, writer, offset, size, crcType, o...
function inflate (line 451) | function inflate(worker, sn, reader, writer, offset, size, computeCrc32,...
function deflate (line 464) | function deflate(worker, sn, reader, writer, level, onend, onprogress, o...
function copy (line 478) | function copy(worker, sn, reader, writer, offset, size, computeCrc32, on...
function decodeASCII (line 493) | function decodeASCII(str) {
function decodeUTF8 (line 513) | function decodeUTF8(string) {
function getString (line 517) | function getString(bytes) {
function getDate (line 524) | function getDate(timeRaw) {
function readCommonHeader (line 533) | function readCommonHeader(entry, data, index, centralDirectory, onerror) {
function createZipReader (line 556) | function createZipReader(reader, callback, onerror) {
function encodeUTF8 (line 709) | function encodeUTF8(string) {
function getBytes (line 713) | function getBytes(str) {
function createZipWriter (line 720) | function createZipWriter(writer, callback, onerror, dontDeflate) {
function resolveURLs (line 863) | function resolveURLs(urls) {
function createWorker (line 875) | function createWorker(type, callback, onerror) {
function onerror_default (line 918) | function onerror_default(error) {
Condensed preview — 14 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,343K chars).
[
{
"path": "README.md",
"chars": 1253,
"preview": "# mapshaper-plus\n\nGenerate geojson files for [Apache ECharts (incubating)](https://github.com/apache/incubator-echarts) "
},
{
"path": "codecs.js",
"chars": 1794,
"preview": "/// wrapper for pako (https://github.com/nodeca/pako)\n\n/* globals pako */\n(function(global) {\n\t\"use strict\";\n\n\tfunction "
},
{
"path": "deflate.js",
"chars": 70218,
"preview": "/*\n Copyright (c) 2013 Gildas Lormeau. All rights reserved.\n\n Redistribution and use in source and binary forms, with or"
},
{
"path": "elements.css",
"chars": 2647,
"preview": "\n/* Hide Firefox's indicator when images are loading */\nimg:-moz-loading {\n visibility: hidden;\n}\n\n/* ------ UTILITY "
},
{
"path": "encode.js",
"chars": 5893,
"preview": "/**\n * https://github.com/giscafer/mapshaper-plus\n * 对坐标数据进行加密\n * @author giscafer\n * @version 1.0\n * @date 2016-06-0"
},
{
"path": "index.html",
"chars": 9053,
"preview": "<!DOCTYPE html>\n<html>\n<head>\n\t<title>mapshaper plus</title>\n\t<meta name=\"Description\" content=\"A tool for topologically"
},
{
"path": "manifest.js",
"chars": 57,
"preview": "/* replaced by a file manifest by mapshaper-gui server */"
},
{
"path": "mapshaper-gui.js",
"chars": 126314,
"preview": "(function(){\n\nvar api = mapshaper; // assuming mapshaper is in global scope\nvar utils = api.utils;\nvar gui = api.gui = {"
},
{
"path": "mapshaper.js",
"chars": 831393,
"preview": "(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require==\"function\"&&require;if(!u&&a)return a(o,!0)"
},
{
"path": "page.css",
"chars": 11029,
"preview": "/*\n@import url(http://fonts.googleapis.com/css?family=Source+Sans+Pro:400,600);\n*/\n\n@font-face {\n font-family: 'Source "
},
{
"path": "pako.deflate.js",
"chars": 26979,
"preview": "/* pako 0.2.7 nodeca/pako */\n!function(t){if(\"object\"==typeof exports&&\"undefined\"!=typeof module)module.exports=t();els"
},
{
"path": "pako.inflate.js",
"chars": 22616,
"preview": "/* pako 0.2.7 nodeca/pako */\n!function(e){if(\"object\"==typeof exports&&\"undefined\"!=typeof module)module.exports=e();els"
},
{
"path": "z-worker.js",
"chars": 3679,
"preview": "/* jshint worker:true */\n(function main(global) {\n\t\"use strict\";\n\n\tif (global.zWorkerInitialized)\n\t\tthrow new Error('z-w"
},
{
"path": "zip.js",
"chars": 30486,
"preview": "/*\n Copyright (c) 2013 Gildas Lormeau. All rights reserved.\n\n Redistribution and use in source and binary forms, with or"
}
]
About this extraction
This page contains the full source code of the giscafer/mapshaper-plus GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 14 files (1.1 MB), approximately 449.5k tokens, and a symbol index with 604 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.