diff geotemco/platin.js @ 8:8f05c2a84bba

Apply new platin and Add tree layers
author nylin@mpiwg-berlin.mpg.de
date Wed, 07 Oct 2015 11:09:20 +0200
parents 0330b2138c87
children 8f5635197895
line wrap: on
line diff
--- a/geotemco/platin.js	Fri Sep 04 16:16:10 2015 +0200
+++ b/geotemco/platin.js	Wed Oct 07 11:09:20 2015 +0200
@@ -1,58 +1,58 @@
 (function($){
 
 var jQuery = $;
-/*
-* basic.js
-*
-* Copyright (c) 2012, Stefan Jänicke. All rights reserved.
-*
-* This library is free software; you can redistribute it and/or
-* modify it under the terms of the GNU Lesser General Public
-* License as published by the Free Software Foundation; either
-* version 3 of the License, or (at your option) any later version.
-*
-* This library is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-* Lesser General Public License for more details.
-*
-* You should have received a copy of the GNU Lesser General Public
-* License along with this library; if not, write to the Free Software
-* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
-* MA 02110-1301  USA
-*/
-
-/**
- * basic code which is included first for the minified version
- * @author Stefan Jänicke (stjaenicke@informatik.uni-leipzig.de)
- * @release 1.0
- * @release date: 2012-07-27
- * @version date: 2012-07-27
- */
-
-var arrayIndex = function(array, obj) {
-	if (Array.indexOf) {
-		return array.indexOf(obj);
-	}
-	for (var i = 0; i < array.length; i++) {
-		if (array[i] == obj) {
-			return i;
-		}
-	}
-	return -1;
-}
-var GeoTemCoMinifier_urlPrefix;
-for (var i = 0; i < document.getElementsByTagName("script").length; i++) {
-	var script = document.getElementsByTagName("script")[i];
-	var index = script.src.indexOf("platin.js");
-	if (index == -1) {
-		index = script.src.indexOf("platin-min.js");
-	}
-	if (index != -1) {
-		GeoTemCoMinifier_urlPrefix = script.src.substring(0, index);
-		break;
-	}
-}
+/*
+* basic.js
+*
+* Copyright (c) 2012, Stefan Jänicke. All rights reserved.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 3 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+* MA 02110-1301  USA
+*/
+
+/**
+ * basic code which is included first for the minified version
+ * @author Stefan Jänicke (stjaenicke@informatik.uni-leipzig.de)
+ * @release 1.0
+ * @release date: 2012-07-27
+ * @version date: 2012-07-27
+ */
+
+var arrayIndex = function(array, obj) {
+	if (Array.indexOf) {
+		return array.indexOf(obj);
+	}
+	for (var i = 0; i < array.length; i++) {
+		if (array[i] == obj) {
+			return i;
+		}
+	}
+	return -1;
+}
+var GeoTemCoMinifier_urlPrefix;
+for (var i = 0; i < document.getElementsByTagName("script").length; i++) {
+	var script = document.getElementsByTagName("script")[i];
+	var index = script.src.indexOf("platin.js");
+	if (index == -1) {
+		index = script.src.indexOf("platin-min.js");
+	}
+	if (index != -1) {
+		GeoTemCoMinifier_urlPrefix = script.src.substring(0, index);
+		break;
+	}
+}
 // Copyright 2006 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
@@ -977,694 +977,694 @@
 })();
 
 } // if
-/*----------------------------------------------------------------------------\
-|                                Range Class                                  |
-|-----------------------------------------------------------------------------|
-|                         Created by Erik Arvidsson                           |
-|                  (http://webfx.eae.net/contact.html#erik)                   |
-|                      For WebFX (http://webfx.eae.net/)                      |
-|-----------------------------------------------------------------------------|
-| Used to  model the data  used  when working  with  sliders,  scrollbars and |
-| progress bars.  Based  on  the  ideas of  the javax.swing.BoundedRangeModel |
-| interface  defined  by  Sun  for  Java;   http://java.sun.com/products/jfc/ |
-| swingdoc-api-1.0.3/com/sun/java/swing/BoundedRangeModel.html                |
-|-----------------------------------------------------------------------------|
-|                Copyright (c) 2002, 2005, 2006 Erik Arvidsson                |
-|-----------------------------------------------------------------------------|
-| Licensed under the Apache License, Version 2.0 (the "License"); you may not |
-| use this file except in compliance with the License.  You may obtain a copy |
-| of the License at http://www.apache.org/licenses/LICENSE-2.0                |
-| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
-| Unless  required  by  applicable law or  agreed  to  in  writing,  software |
-| distributed under the License is distributed on an  "AS IS" BASIS,  WITHOUT |
-| WARRANTIES OR  CONDITIONS OF ANY KIND,  either express or implied.  See the |
-| License  for the  specific language  governing permissions  and limitations |
-| under the License.                                                          |
-|-----------------------------------------------------------------------------|
-| 2002-10-14 | Original version released                                      |
-| 2005-10-27 | Use Math.round instead of Math.floor                           |
-| 2006-05-28 | Changed license to Apache Software License 2.0.                |
-|-----------------------------------------------------------------------------|
-| Created 2002-10-14 | All changes are in the log above. | Updated 2006-05-28 |
-\----------------------------------------------------------------------------*/
-
-
-function Range() {
-	this._value = 0;
-	this._minimum = 0;
-	this._maximum = 100;
-	this._extent = 0;
-
-	this._isChanging = false;
-}
-
-Range.prototype.setValue = function (value) {
-	value = Math.round(parseFloat(value));
-	if (isNaN(value)) return;
-	if (this._value != value) {
-		if (value + this._extent > this._maximum)
-			this._value = this._maximum - this._extent;
-		else if (value < this._minimum)
-			this._value = this._minimum;
-		else
-			this._value = value;
-		if (!this._isChanging && typeof this.onchange == "function")
-			 this.onchange();
-	}
-};
-
-Range.prototype.getValue = function () {
-	return this._value;
-};
-
-Range.prototype.setExtent = function (extent) {
-	if (this._extent != extent) {
-		if (extent < 0)
-			this._extent = 0;
-		else if (this._value + extent > this._maximum)
-			this._extent = this._maximum - this._value;
-		else
-			this._extent = extent;
-		if (!this._isChanging && typeof this.onchange == "function")
-			this.onchange();
-	}
-};
-
-Range.prototype.getExtent = function () {
-	return this._extent;
-};
-
-Range.prototype.setMinimum = function (minimum) {
-	if (this._minimum != minimum) {
-		var oldIsChanging = this._isChanging;
-		this._isChanging = true;
-
-		this._minimum = minimum;
-
-		if (minimum > this._value)
-			this.setValue(minimum);
-		if (minimum > this._maximum) {
-			this._extent = 0;
-			this.setMaximum(minimum);
-			this.setValue(minimum)
-		}
-		if (minimum + this._extent > this._maximum)
-			this._extent = this._maximum - this._minimum;
-
-		this._isChanging = oldIsChanging;
-		if (!this._isChanging && typeof this.onchange == "function")
-			this.onchange();
-	}
-};
-
-Range.prototype.getMinimum = function () {
-	return this._minimum;
-};
-
-Range.prototype.setMaximum = function (maximum) {
-	if (this._maximum != maximum) {
-		var oldIsChanging = this._isChanging;
-		this._isChanging = true;
-
-		this._maximum = maximum;
-
-		if (maximum < this._value)
-			this.setValue(maximum - this._extent);
-		if (maximum < this._minimum) {
-			this._extent = 0;
-			this.setMinimum(maximum);
-			this.setValue(this._maximum);
-		}
-		if (maximum < this._minimum + this._extent)
-			this._extent = this._maximum - this._minimum;
-		if (maximum < this._value + this._extent)
-			this._extent = this._maximum - this._value;
-
-		this._isChanging = oldIsChanging;
-		if (!this._isChanging && typeof this.onchange == "function")
-			this.onchange();
-	}
-};
-
-Range.prototype.getMaximum = function () {
-	return this._maximum;
-};
-/*----------------------------------------------------------------------------\
-|                                Slider 1.02                                  |
-|-----------------------------------------------------------------------------|
-|                         Created by Erik Arvidsson                           |
-|                  (http://webfx.eae.net/contact.html#erik)                   |
-|                      For WebFX (http://webfx.eae.net/)                      |
-|-----------------------------------------------------------------------------|
-| A  slider  control that  degrades  to an  input control  for non  supported |
-| browsers.                                                                   |
-|-----------------------------------------------------------------------------|
-|                Copyright (c) 2002, 2003, 2006 Erik Arvidsson                |
-|-----------------------------------------------------------------------------|
-| Licensed under the Apache License, Version 2.0 (the "License"); you may not |
-| use this file except in compliance with the License.  You may obtain a copy |
-| of the License at http://www.apache.org/licenses/LICENSE-2.0                |
-| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
-| Unless  required  by  applicable law or  agreed  to  in  writing,  software |
-| distributed under the License is distributed on an  "AS IS" BASIS,  WITHOUT |
-| WARRANTIES OR  CONDITIONS OF ANY KIND,  either express or implied.  See the |
-| License  for the  specific language  governing permissions  and limitations |
-| under the License.                                                          |
-|-----------------------------------------------------------------------------|
-| Dependencies: timer.js - an OO abstraction of timers                        |
-|               range.js - provides the data model for the slider             |
-|               winclassic.css or any other css file describing the look      |
-|-----------------------------------------------------------------------------|
-| 2002-10-14 | Original version released                                      |
-| 2003-03-27 | Added a test in the constructor for missing oElement arg       |
-| 2003-11-27 | Only use mousewheel when focused                               |
-| 2006-05-28 | Changed license to Apache Software License 2.0.                |
-|-----------------------------------------------------------------------------|
-| Created 2002-10-14 | All changes are in the log above. | Updated 2006-05-28 |
-\----------------------------------------------------------------------------*/
-
-Slider.isSupported = typeof document.createElement != "undefined" &&
-	typeof document.documentElement != "undefined" &&
-	typeof document.documentElement.offsetWidth == "number";
-
-
-function Slider(oElement, oInput, sOrientation) {
-	if (!oElement) return;
-	this._orientation = sOrientation || "horizontal";
-	this._range = new Range();
-	this._range.setExtent(0);
-	this._blockIncrement = 10;
-	this._unitIncrement = 1;
-	this._timer = new Timer(100);
-
-
-	if (Slider.isSupported && oElement) {
-
-		this.document = oElement.ownerDocument || oElement.document;
-
-		this.element = oElement;
-		this.element.slider = this;
-		this.element.unselectable = "on";
-
-		// add class name tag to class name
-		this.element.className = this._orientation + " " + this.classNameTag + " " + this.element.className;
-
-		// create line
-		this.line = this.document.createElement("DIV");
-		this.line.className = "line";
-		this.line.unselectable = "on";
-		this.line.appendChild(this.document.createElement("DIV"));
-		this.element.appendChild(this.line);
-
-		// create handle
-		this.handle = this.document.createElement("DIV");
-		this.handle.className = "handle";
-		this.handle.unselectable = "on";
-		this.handle.appendChild(this.document.createElement("DIV"));
-		this.handle.firstChild.appendChild(
-			this.document.createTextNode(String.fromCharCode(160)));
-		this.element.appendChild(this.handle);
-	}
-
-	this.input = oInput;
-
-	// events
-	var oThis = this;
-	this._range.onchange = function () {
-		oThis.recalculate();
-		if (typeof oThis.onchange == "function")
-			oThis.onchange();
-	};
-
-	if (Slider.isSupported && oElement) {
-		this.element.onfocus		= Slider.eventHandlers.onfocus;
-		this.element.onblur			= Slider.eventHandlers.onblur;
-		this.element.onmousedown	= Slider.eventHandlers.onmousedown;
-		this.element.onmouseover	= Slider.eventHandlers.onmouseover;
-		this.element.onmouseout		= Slider.eventHandlers.onmouseout;
-		this.element.onkeydown		= Slider.eventHandlers.onkeydown;
-		this.element.onkeypress		= Slider.eventHandlers.onkeypress;
-		this.element.onmousewheel	= Slider.eventHandlers.onmousewheel;
-		this.handle.onselectstart	=
-		this.element.onselectstart	= function () { return false; };
-
-		this._timer.ontimer = function () {
-			oThis.ontimer();
-		};
-
-		// extra recalculate for ie
-		window.setTimeout(function() {
-			oThis.recalculate();
-		}, 1);
-	}
-	else {
-		this.input.onchange = function (e) {
-			oThis.setValue(oThis.input.value);
-		};
-	}
-}
-
-Slider.eventHandlers = {
-
-	// helpers to make events a bit easier
-	getEvent:	function (e, el) {
-		if (!e) {
-			if (el)
-				e = el.document.parentWindow.event;
-			else
-				e = window.event;
-		}
-		if (!e.srcElement) {
-			var el = e.target;
-			while (el != null && el.nodeType != 1)
-				el = el.parentNode;
-			e.srcElement = el;
-		}
-		if (typeof e.offsetX == "undefined") {
-			e.offsetX = e.layerX;
-			e.offsetY = e.layerY;
-		}
-
-		return e;
-	},
-
-	getDocument:	function (e) {
-		if (e.target)
-			return e.target.ownerDocument;
-		return e.srcElement.document;
-	},
-
-	getSlider:	function (e) {
-		var el = e.target || e.srcElement;
-		while (el != null && el.slider == null)	{
-			el = el.parentNode;
-		}
-		if (el)
-			return el.slider;
-		return null;
-	},
-
-	getLine:	function (e) {
-		var el = e.target || e.srcElement;
-		while (el != null && el.className != "line")	{
-			el = el.parentNode;
-		}
-		return el;
-	},
-
-	getHandle:	function (e) {
-		var el = e.target || e.srcElement;
-		var re = /handle/;
-		while (el != null && !re.test(el.className))	{
-			el = el.parentNode;
-		}
-		return el;
-	},
-	// end helpers
-
-	onfocus:	function (e) {
-		var s = this.slider;
-		s._focused = true;
-		s.handle.className = "handle hover";
-	},
-
-	onblur:	function (e) {
-		var s = this.slider
-		s._focused = false;
-		s.handle.className = "handle";
-	},
-
-	onmouseover:	function (e) {
-		e = Slider.eventHandlers.getEvent(e, this);
-		var s = this.slider;
-		if (e.srcElement == s.handle)
-			s.handle.className = "handle hover";
-	},
-
-	onmouseout:	function (e) {
-		e = Slider.eventHandlers.getEvent(e, this);
-		var s = this.slider;
-		if (e.srcElement == s.handle && !s._focused)
-			s.handle.className = "handle";
-	},
-
-	onmousedown:	function (e) {
-		e = Slider.eventHandlers.getEvent(e, this);
-		var s = this.slider;
-		if (s.element.focus)
-			s.element.focus();
-
-		Slider._currentInstance = s;
-		var doc = s.document;
-
-		if (doc.addEventListener) {
-			doc.addEventListener("mousemove", Slider.eventHandlers.onmousemove, true);
-			doc.addEventListener("mouseup", Slider.eventHandlers.onmouseup, true);
-		}
-		else if (doc.attachEvent) {
-			doc.attachEvent("onmousemove", Slider.eventHandlers.onmousemove);
-			doc.attachEvent("onmouseup", Slider.eventHandlers.onmouseup);
-			doc.attachEvent("onlosecapture", Slider.eventHandlers.onmouseup);
-			s.element.setCapture();
-		}
-
-		if (Slider.eventHandlers.getHandle(e)) {	// start drag
-			Slider._sliderDragData = {
-				screenX:	e.screenX,
-				screenY:	e.screenY,
-				dx:			e.screenX - s.handle.offsetLeft,
-				dy:			e.screenY - s.handle.offsetTop,
-				startValue:	s.getValue(),
-				slider:		s
-			};
-		}
-		else {
-			return;
-			var lineEl = Slider.eventHandlers.getLine(e);
-			s._mouseX = e.offsetX + (lineEl ? s.line.offsetLeft : 0);
-			s._mouseY = e.offsetY + (lineEl ? s.line.offsetTop : 0);
-			s._increasing = null;
-			s.ontimer();
-		}
-	},
-
-	onmousemove:	function (e) {
-		e = Slider.eventHandlers.getEvent(e, this);
-
-		if (Slider._sliderDragData) {	// drag
-			var s = Slider._sliderDragData.slider;
-
-			var boundSize = s.getMaximum() - s.getMinimum();
-			var size, pos, reset;
-
-			if (s._orientation == "horizontal") {
-				size = s.element.offsetWidth - s.handle.offsetWidth;
-				pos = e.screenX - Slider._sliderDragData.dx;
-				reset = Math.abs(e.screenY - Slider._sliderDragData.screenY) > 100;
-			}
-			else {
-				size = s.element.offsetHeight - s.handle.offsetHeight;
-				pos = s.element.offsetHeight - s.handle.offsetHeight -
-					(e.screenY - Slider._sliderDragData.dy);
-				reset = Math.abs(e.screenX - Slider._sliderDragData.screenX) > 100;
-			}
-			s.setValue(reset ? Slider._sliderDragData.startValue :
-						s.getMinimum() + boundSize * pos / size);
-			return false;
-		}
-		else {
-			return;
-			var s = Slider._currentInstance;
-			if (s != null) {
-				var lineEl = Slider.eventHandlers.getLine(e);
-				s._mouseX = e.offsetX + (lineEl ? s.line.offsetLeft : 0);
-				s._mouseY = e.offsetY + (lineEl ? s.line.offsetTop : 0);
-			}
-		}
-
-	},
-
-	onmouseup:	function (e) {
-		e = Slider.eventHandlers.getEvent(e, this);
-		var s = Slider._currentInstance;
-		var doc = s.document;
-		if (doc.removeEventListener) {
-			doc.removeEventListener("mousemove", Slider.eventHandlers.onmousemove, true);
-			doc.removeEventListener("mouseup", Slider.eventHandlers.onmouseup, true);
-		}
-		else if (doc.detachEvent) {
-			doc.detachEvent("onmousemove", Slider.eventHandlers.onmousemove);
-			doc.detachEvent("onmouseup", Slider.eventHandlers.onmouseup);
-			doc.detachEvent("onlosecapture", Slider.eventHandlers.onmouseup);
-			s.element.releaseCapture();
-		}
-
-		if (Slider._sliderDragData) {	// end drag
-			Slider._sliderDragData = null;
-		}
-		else {
-			return;
-			s._timer.stop();
-			s._increasing = null;
-		}
-		Slider._currentInstance = null;
-	},
-
-	onkeydown:	function (e) {
-		return;
-		e = Slider.eventHandlers.getEvent(e, this);
-		//var s = Slider.eventHandlers.getSlider(e);
-		var s = this.slider;
-		var kc = e.keyCode;
-		switch (kc) {
-			case 33:	// page up
-				s.setValue(s.getValue() + s.getBlockIncrement());
-				break;
-			case 34:	// page down
-				s.setValue(s.getValue() - s.getBlockIncrement());
-				break;
-			case 35:	// end
-				s.setValue(s.getOrientation() == "horizontal" ?
-					s.getMaximum() :
-					s.getMinimum());
-				break;
-			case 36:	// home
-				s.setValue(s.getOrientation() == "horizontal" ?
-					s.getMinimum() :
-					s.getMaximum());
-				break;
-			case 38:	// up
-			case 39:	// right
-				s.setValue(s.getValue() + s.getUnitIncrement());
-				break;
-
-			case 37:	// left
-			case 40:	// down
-				s.setValue(s.getValue() - s.getUnitIncrement());
-				break;
-		}
-
-		if (kc >= 33 && kc <= 40) {
-			return false;
-		}
-	},
-
-	onkeypress:	function (e) {
-		return;
-		e = Slider.eventHandlers.getEvent(e, this);
-		var kc = e.keyCode;
-		if (kc >= 33 && kc <= 40) {
-			return false;
-		}
-	},
-
-	onmousewheel:	function (e) {
-		return;
-		e = Slider.eventHandlers.getEvent(e, this);
-		var s = this.slider;
-		if (s._focused) {
-			s.setValue(s.getValue() + e.wheelDelta / 120 * s.getUnitIncrement());
-			// windows inverts this on horizontal sliders. That does not
-			// make sense to me
-			return false;
-		}
-	}
-};
-
-
-
-Slider.prototype.classNameTag = "dynamic-slider-control",
-
-Slider.prototype.setValue = function (v) {
-	this._range.setValue(v);
-	this.input.value = this.getValue();
-};
-
-Slider.prototype.getValue = function () {
-	return this._range.getValue();
-};
-
-Slider.prototype.setMinimum = function (v) {
-	this._range.setMinimum(v);
-	this.input.value = this.getValue();
-};
-
-Slider.prototype.getMinimum = function () {
-	return this._range.getMinimum();
-};
-
-Slider.prototype.setMaximum = function (v) {
-	this._range.setMaximum(v);
-	this.input.value = this.getValue();
-};
-
-Slider.prototype.getMaximum = function () {
-	return this._range.getMaximum();
-};
-
-Slider.prototype.setUnitIncrement = function (v) {
-	this._unitIncrement = v;
-};
-
-Slider.prototype.getUnitIncrement = function () {
-	return this._unitIncrement;
-};
-
-Slider.prototype.setBlockIncrement = function (v) {
-	this._blockIncrement = v;
-};
-
-Slider.prototype.getBlockIncrement = function () {
-	return this._blockIncrement;
-};
-
-Slider.prototype.getOrientation = function () {
-	return this._orientation;
-};
-
-Slider.prototype.setOrientation = function (sOrientation) {
-	if (sOrientation != this._orientation) {
-		if (Slider.isSupported && this.element) {
-			// add class name tag to class name
-			this.element.className = this.element.className.replace(this._orientation,
-									sOrientation);
-		}
-		this._orientation = sOrientation;
-		this.recalculate();
-
-	}
-};
-
-Slider.prototype.recalculate = function() {
-	if (!Slider.isSupported || !this.element) return;
-
-	var w = this.element.offsetWidth;
-	var h = this.element.offsetHeight;
-	var hw = this.handle.offsetWidth;
-	var hh = this.handle.offsetHeight;
-	var lw = this.line.offsetWidth;
-	var lh = this.line.offsetHeight;
-
-	// this assumes a border-box layout
-
-	if (this._orientation == "horizontal") {
-		this.handle.style.left = (w - hw) * (this.getValue() - this.getMinimum()) /
-			(this.getMaximum() - this.getMinimum()) + "px";
-		this.handle.style.top = (h - hh) / 2 + "px";
-
-		this.line.style.top = (h - lh) / 2 + "px";
-		this.line.style.left = hw / 2 + "px";
-		//this.line.style.right = hw / 2 + "px";
-		this.line.style.width = Math.max(0, w - hw - 2)+ "px";
-		this.line.firstChild.style.width = Math.max(0, w - hw - 4)+ "px";
-	}
-	else {
-		this.handle.style.left = (w - hw) / 2 + "px";
-		this.handle.style.top = h - hh - (h - hh) * (this.getValue() - this.getMinimum()) /
-			(this.getMaximum() - this.getMinimum()) + "px";
-
-		this.line.style.left = (w - lw) / 2 + "px";
-		this.line.style.top = hh / 2 + "px";
-		this.line.style.height = Math.max(0, h - hh - 2) + "px";	//hard coded border width
-		//this.line.style.bottom = hh / 2 + "px";
-		this.line.firstChild.style.height = Math.max(0, h - hh - 4) + "px";	//hard coded border width
-	}
-};
-
-Slider.prototype.ontimer = function () {
-	var hw = this.handle.offsetWidth;
-	var hh = this.handle.offsetHeight;
-	var hl = this.handle.offsetLeft;
-	var ht = this.handle.offsetTop;
-
-	if (this._orientation == "horizontal") {
-		if (this._mouseX > hl + hw &&
-			(this._increasing == null || this._increasing)) {
-			this.setValue(this.getValue() + this.getBlockIncrement());
-			this._increasing = true;
-		}
-		else if (this._mouseX < hl &&
-			(this._increasing == null || !this._increasing)) {
-			this.setValue(this.getValue() - this.getBlockIncrement());
-			this._increasing = false;
-		}
-	}
-	else {
-		if (this._mouseY > ht + hh &&
-			(this._increasing == null || !this._increasing)) {
-			this.setValue(this.getValue() - this.getBlockIncrement());
-			this._increasing = false;
-		}
-		else if (this._mouseY < ht &&
-			(this._increasing == null || this._increasing)) {
-			this.setValue(this.getValue() + this.getBlockIncrement());
-			this._increasing = true;
-		}
-	}
-
-	this._timer.start();
-};
-/*----------------------------------------------------------------------------\
-|                                 Timer Class                                 |
-|-----------------------------------------------------------------------------|
-|                         Created by Erik Arvidsson                           |
-|                  (http://webfx.eae.net/contact.html#erik)                   |
-|                      For WebFX (http://webfx.eae.net/)                      |
-|-----------------------------------------------------------------------------|
-| Object Oriented Encapsulation  of setTimeout  fires ontimer when the  timer |
-| is triggered. Does not work in IE 5.00                                      |
-|-----------------------------------------------------------------------------|
-|                   Copyright (c) 2002, 2006 Erik Arvidsson                   |
-|-----------------------------------------------------------------------------|
-| Licensed under the Apache License, Version 2.0 (the "License"); you may not |
-| use this file except in compliance with the License.  You may obtain a copy |
-| of the License at http://www.apache.org/licenses/LICENSE-2.0                |
-| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
-| Unless  required  by  applicable law or  agreed  to  in  writing,  software |
-| distributed under the License is distributed on an  "AS IS" BASIS,  WITHOUT |
-| WARRANTIES OR  CONDITIONS OF ANY KIND,  either express or implied.  See the |
-| License  for the  specific language  governing permissions  and limitations |
-| under the License.                                                          |
-|-----------------------------------------------------------------------------|
-| 2002-10-14 | Original version released                                      |
-| 2006-05-28 | Changed license to Apache Software License 2.0.                |
-|-----------------------------------------------------------------------------|
-| Created 2002-10-14 | All changes are in the log above. | Updated 2006-05-28 |
-\----------------------------------------------------------------------------*/
-
-function Timer(nPauseTime) {
-	this._pauseTime = typeof nPauseTime == "undefined" ? 1000 : nPauseTime;
-	this._timer = null;
-	this._isStarted = false;
-}
-
-Timer.prototype.start = function () {
-	if (this.isStarted())
-		this.stop();
-	var oThis = this;
-	this._timer = window.setTimeout(function () {
-		if (typeof oThis.ontimer == "function")
-			oThis.ontimer();
-	}, this._pauseTime);
-	this._isStarted = false;
-};
-
-Timer.prototype.stop = function () {
-	if (this._timer != null)
-		window.clearTimeout(this._timer);
-	this._isStarted = false;
-};
-
-Timer.prototype.isStarted = function () {
-	return this._isStarted;
-};
-
-Timer.prototype.getPauseTime = function () {
-	return this._pauseTime;
-};
-
-Timer.prototype.setPauseTime = function (nPauseTime) {
-	this._pauseTime = nPauseTime;
+/*----------------------------------------------------------------------------\
+|                                Range Class                                  |
+|-----------------------------------------------------------------------------|
+|                         Created by Erik Arvidsson                           |
+|                  (http://webfx.eae.net/contact.html#erik)                   |
+|                      For WebFX (http://webfx.eae.net/)                      |
+|-----------------------------------------------------------------------------|
+| Used to  model the data  used  when working  with  sliders,  scrollbars and |
+| progress bars.  Based  on  the  ideas of  the javax.swing.BoundedRangeModel |
+| interface  defined  by  Sun  for  Java;   http://java.sun.com/products/jfc/ |
+| swingdoc-api-1.0.3/com/sun/java/swing/BoundedRangeModel.html                |
+|-----------------------------------------------------------------------------|
+|                Copyright (c) 2002, 2005, 2006 Erik Arvidsson                |
+|-----------------------------------------------------------------------------|
+| Licensed under the Apache License, Version 2.0 (the "License"); you may not |
+| use this file except in compliance with the License.  You may obtain a copy |
+| of the License at http://www.apache.org/licenses/LICENSE-2.0                |
+| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
+| Unless  required  by  applicable law or  agreed  to  in  writing,  software |
+| distributed under the License is distributed on an  "AS IS" BASIS,  WITHOUT |
+| WARRANTIES OR  CONDITIONS OF ANY KIND,  either express or implied.  See the |
+| License  for the  specific language  governing permissions  and limitations |
+| under the License.                                                          |
+|-----------------------------------------------------------------------------|
+| 2002-10-14 | Original version released                                      |
+| 2005-10-27 | Use Math.round instead of Math.floor                           |
+| 2006-05-28 | Changed license to Apache Software License 2.0.                |
+|-----------------------------------------------------------------------------|
+| Created 2002-10-14 | All changes are in the log above. | Updated 2006-05-28 |
+\----------------------------------------------------------------------------*/
+
+
+function Range() {
+	this._value = 0;
+	this._minimum = 0;
+	this._maximum = 100;
+	this._extent = 0;
+
+	this._isChanging = false;
+}
+
+Range.prototype.setValue = function (value) {
+	value = Math.round(parseFloat(value));
+	if (isNaN(value)) return;
+	if (this._value != value) {
+		if (value + this._extent > this._maximum)
+			this._value = this._maximum - this._extent;
+		else if (value < this._minimum)
+			this._value = this._minimum;
+		else
+			this._value = value;
+		if (!this._isChanging && typeof this.onchange == "function")
+			 this.onchange();
+	}
+};
+
+Range.prototype.getValue = function () {
+	return this._value;
+};
+
+Range.prototype.setExtent = function (extent) {
+	if (this._extent != extent) {
+		if (extent < 0)
+			this._extent = 0;
+		else if (this._value + extent > this._maximum)
+			this._extent = this._maximum - this._value;
+		else
+			this._extent = extent;
+		if (!this._isChanging && typeof this.onchange == "function")
+			this.onchange();
+	}
+};
+
+Range.prototype.getExtent = function () {
+	return this._extent;
+};
+
+Range.prototype.setMinimum = function (minimum) {
+	if (this._minimum != minimum) {
+		var oldIsChanging = this._isChanging;
+		this._isChanging = true;
+
+		this._minimum = minimum;
+
+		if (minimum > this._value)
+			this.setValue(minimum);
+		if (minimum > this._maximum) {
+			this._extent = 0;
+			this.setMaximum(minimum);
+			this.setValue(minimum)
+		}
+		if (minimum + this._extent > this._maximum)
+			this._extent = this._maximum - this._minimum;
+
+		this._isChanging = oldIsChanging;
+		if (!this._isChanging && typeof this.onchange == "function")
+			this.onchange();
+	}
+};
+
+Range.prototype.getMinimum = function () {
+	return this._minimum;
+};
+
+Range.prototype.setMaximum = function (maximum) {
+	if (this._maximum != maximum) {
+		var oldIsChanging = this._isChanging;
+		this._isChanging = true;
+
+		this._maximum = maximum;
+
+		if (maximum < this._value)
+			this.setValue(maximum - this._extent);
+		if (maximum < this._minimum) {
+			this._extent = 0;
+			this.setMinimum(maximum);
+			this.setValue(this._maximum);
+		}
+		if (maximum < this._minimum + this._extent)
+			this._extent = this._maximum - this._minimum;
+		if (maximum < this._value + this._extent)
+			this._extent = this._maximum - this._value;
+
+		this._isChanging = oldIsChanging;
+		if (!this._isChanging && typeof this.onchange == "function")
+			this.onchange();
+	}
+};
+
+Range.prototype.getMaximum = function () {
+	return this._maximum;
+};
+/*----------------------------------------------------------------------------\
+|                                Slider 1.02                                  |
+|-----------------------------------------------------------------------------|
+|                         Created by Erik Arvidsson                           |
+|                  (http://webfx.eae.net/contact.html#erik)                   |
+|                      For WebFX (http://webfx.eae.net/)                      |
+|-----------------------------------------------------------------------------|
+| A  slider  control that  degrades  to an  input control  for non  supported |
+| browsers.                                                                   |
+|-----------------------------------------------------------------------------|
+|                Copyright (c) 2002, 2003, 2006 Erik Arvidsson                |
+|-----------------------------------------------------------------------------|
+| Licensed under the Apache License, Version 2.0 (the "License"); you may not |
+| use this file except in compliance with the License.  You may obtain a copy |
+| of the License at http://www.apache.org/licenses/LICENSE-2.0                |
+| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
+| Unless  required  by  applicable law or  agreed  to  in  writing,  software |
+| distributed under the License is distributed on an  "AS IS" BASIS,  WITHOUT |
+| WARRANTIES OR  CONDITIONS OF ANY KIND,  either express or implied.  See the |
+| License  for the  specific language  governing permissions  and limitations |
+| under the License.                                                          |
+|-----------------------------------------------------------------------------|
+| Dependencies: timer.js - an OO abstraction of timers                        |
+|               range.js - provides the data model for the slider             |
+|               winclassic.css or any other css file describing the look      |
+|-----------------------------------------------------------------------------|
+| 2002-10-14 | Original version released                                      |
+| 2003-03-27 | Added a test in the constructor for missing oElement arg       |
+| 2003-11-27 | Only use mousewheel when focused                               |
+| 2006-05-28 | Changed license to Apache Software License 2.0.                |
+|-----------------------------------------------------------------------------|
+| Created 2002-10-14 | All changes are in the log above. | Updated 2006-05-28 |
+\----------------------------------------------------------------------------*/
+
+Slider.isSupported = typeof document.createElement != "undefined" &&
+	typeof document.documentElement != "undefined" &&
+	typeof document.documentElement.offsetWidth == "number";
+
+
+function Slider(oElement, oInput, sOrientation) {
+	if (!oElement) return;
+	this._orientation = sOrientation || "horizontal";
+	this._range = new Range();
+	this._range.setExtent(0);
+	this._blockIncrement = 10;
+	this._unitIncrement = 1;
+	this._timer = new Timer(100);
+
+
+	if (Slider.isSupported && oElement) {
+
+		this.document = oElement.ownerDocument || oElement.document;
+
+		this.element = oElement;
+		this.element.slider = this;
+		this.element.unselectable = "on";
+
+		// add class name tag to class name
+		this.element.className = this._orientation + " " + this.classNameTag + " " + this.element.className;
+
+		// create line
+		this.line = this.document.createElement("DIV");
+		this.line.className = "line";
+		this.line.unselectable = "on";
+		this.line.appendChild(this.document.createElement("DIV"));
+		this.element.appendChild(this.line);
+
+		// create handle
+		this.handle = this.document.createElement("DIV");
+		this.handle.className = "handle";
+		this.handle.unselectable = "on";
+		this.handle.appendChild(this.document.createElement("DIV"));
+		this.handle.firstChild.appendChild(
+			this.document.createTextNode(String.fromCharCode(160)));
+		this.element.appendChild(this.handle);
+	}
+
+	this.input = oInput;
+
+	// events
+	var oThis = this;
+	this._range.onchange = function () {
+		oThis.recalculate();
+		if (typeof oThis.onchange == "function")
+			oThis.onchange();
+	};
+
+	if (Slider.isSupported && oElement) {
+		this.element.onfocus		= Slider.eventHandlers.onfocus;
+		this.element.onblur			= Slider.eventHandlers.onblur;
+		this.element.onmousedown	= Slider.eventHandlers.onmousedown;
+		this.element.onmouseover	= Slider.eventHandlers.onmouseover;
+		this.element.onmouseout		= Slider.eventHandlers.onmouseout;
+		this.element.onkeydown		= Slider.eventHandlers.onkeydown;
+		this.element.onkeypress		= Slider.eventHandlers.onkeypress;
+		this.element.onmousewheel	= Slider.eventHandlers.onmousewheel;
+		this.handle.onselectstart	=
+		this.element.onselectstart	= function () { return false; };
+
+		this._timer.ontimer = function () {
+			oThis.ontimer();
+		};
+
+		// extra recalculate for ie
+		window.setTimeout(function() {
+			oThis.recalculate();
+		}, 1);
+	}
+	else {
+		this.input.onchange = function (e) {
+			oThis.setValue(oThis.input.value);
+		};
+	}
+}
+
+Slider.eventHandlers = {
+
+	// helpers to make events a bit easier
+	getEvent:	function (e, el) {
+		if (!e) {
+			if (el)
+				e = el.document.parentWindow.event;
+			else
+				e = window.event;
+		}
+		if (!e.srcElement) {
+			var el = e.target;
+			while (el != null && el.nodeType != 1)
+				el = el.parentNode;
+			e.srcElement = el;
+		}
+		if (typeof e.offsetX == "undefined") {
+			e.offsetX = e.layerX;
+			e.offsetY = e.layerY;
+		}
+
+		return e;
+	},
+
+	getDocument:	function (e) {
+		if (e.target)
+			return e.target.ownerDocument;
+		return e.srcElement.document;
+	},
+
+	getSlider:	function (e) {
+		var el = e.target || e.srcElement;
+		while (el != null && el.slider == null)	{
+			el = el.parentNode;
+		}
+		if (el)
+			return el.slider;
+		return null;
+	},
+
+	getLine:	function (e) {
+		var el = e.target || e.srcElement;
+		while (el != null && el.className != "line")	{
+			el = el.parentNode;
+		}
+		return el;
+	},
+
+	getHandle:	function (e) {
+		var el = e.target || e.srcElement;
+		var re = /handle/;
+		while (el != null && !re.test(el.className))	{
+			el = el.parentNode;
+		}
+		return el;
+	},
+	// end helpers
+
+	onfocus:	function (e) {
+		var s = this.slider;
+		s._focused = true;
+		s.handle.className = "handle hover";
+	},
+
+	onblur:	function (e) {
+		var s = this.slider
+		s._focused = false;
+		s.handle.className = "handle";
+	},
+
+	onmouseover:	function (e) {
+		e = Slider.eventHandlers.getEvent(e, this);
+		var s = this.slider;
+		if (e.srcElement == s.handle)
+			s.handle.className = "handle hover";
+	},
+
+	onmouseout:	function (e) {
+		e = Slider.eventHandlers.getEvent(e, this);
+		var s = this.slider;
+		if (e.srcElement == s.handle && !s._focused)
+			s.handle.className = "handle";
+	},
+
+	onmousedown:	function (e) {
+		e = Slider.eventHandlers.getEvent(e, this);
+		var s = this.slider;
+		if (s.element.focus)
+			s.element.focus();
+
+		Slider._currentInstance = s;
+		var doc = s.document;
+
+		if (doc.addEventListener) {
+			doc.addEventListener("mousemove", Slider.eventHandlers.onmousemove, true);
+			doc.addEventListener("mouseup", Slider.eventHandlers.onmouseup, true);
+		}
+		else if (doc.attachEvent) {
+			doc.attachEvent("onmousemove", Slider.eventHandlers.onmousemove);
+			doc.attachEvent("onmouseup", Slider.eventHandlers.onmouseup);
+			doc.attachEvent("onlosecapture", Slider.eventHandlers.onmouseup);
+			s.element.setCapture();
+		}
+
+		if (Slider.eventHandlers.getHandle(e)) {	// start drag
+			Slider._sliderDragData = {
+				screenX:	e.screenX,
+				screenY:	e.screenY,
+				dx:			e.screenX - s.handle.offsetLeft,
+				dy:			e.screenY - s.handle.offsetTop,
+				startValue:	s.getValue(),
+				slider:		s
+			};
+		}
+		else {
+			return;
+			var lineEl = Slider.eventHandlers.getLine(e);
+			s._mouseX = e.offsetX + (lineEl ? s.line.offsetLeft : 0);
+			s._mouseY = e.offsetY + (lineEl ? s.line.offsetTop : 0);
+			s._increasing = null;
+			s.ontimer();
+		}
+	},
+
+	onmousemove:	function (e) {
+		e = Slider.eventHandlers.getEvent(e, this);
+
+		if (Slider._sliderDragData) {	// drag
+			var s = Slider._sliderDragData.slider;
+
+			var boundSize = s.getMaximum() - s.getMinimum();
+			var size, pos, reset;
+
+			if (s._orientation == "horizontal") {
+				size = s.element.offsetWidth - s.handle.offsetWidth;
+				pos = e.screenX - Slider._sliderDragData.dx;
+				reset = Math.abs(e.screenY - Slider._sliderDragData.screenY) > 100;
+			}
+			else {
+				size = s.element.offsetHeight - s.handle.offsetHeight;
+				pos = s.element.offsetHeight - s.handle.offsetHeight -
+					(e.screenY - Slider._sliderDragData.dy);
+				reset = Math.abs(e.screenX - Slider._sliderDragData.screenX) > 100;
+			}
+			s.setValue(reset ? Slider._sliderDragData.startValue :
+						s.getMinimum() + boundSize * pos / size);
+			return false;
+		}
+		else {
+			return;
+			var s = Slider._currentInstance;
+			if (s != null) {
+				var lineEl = Slider.eventHandlers.getLine(e);
+				s._mouseX = e.offsetX + (lineEl ? s.line.offsetLeft : 0);
+				s._mouseY = e.offsetY + (lineEl ? s.line.offsetTop : 0);
+			}
+		}
+
+	},
+
+	onmouseup:	function (e) {
+		e = Slider.eventHandlers.getEvent(e, this);
+		var s = Slider._currentInstance;
+		var doc = s.document;
+		if (doc.removeEventListener) {
+			doc.removeEventListener("mousemove", Slider.eventHandlers.onmousemove, true);
+			doc.removeEventListener("mouseup", Slider.eventHandlers.onmouseup, true);
+		}
+		else if (doc.detachEvent) {
+			doc.detachEvent("onmousemove", Slider.eventHandlers.onmousemove);
+			doc.detachEvent("onmouseup", Slider.eventHandlers.onmouseup);
+			doc.detachEvent("onlosecapture", Slider.eventHandlers.onmouseup);
+			s.element.releaseCapture();
+		}
+
+		if (Slider._sliderDragData) {	// end drag
+			Slider._sliderDragData = null;
+		}
+		else {
+			return;
+			s._timer.stop();
+			s._increasing = null;
+		}
+		Slider._currentInstance = null;
+	},
+
+	onkeydown:	function (e) {
+		return;
+		e = Slider.eventHandlers.getEvent(e, this);
+		//var s = Slider.eventHandlers.getSlider(e);
+		var s = this.slider;
+		var kc = e.keyCode;
+		switch (kc) {
+			case 33:	// page up
+				s.setValue(s.getValue() + s.getBlockIncrement());
+				break;
+			case 34:	// page down
+				s.setValue(s.getValue() - s.getBlockIncrement());
+				break;
+			case 35:	// end
+				s.setValue(s.getOrientation() == "horizontal" ?
+					s.getMaximum() :
+					s.getMinimum());
+				break;
+			case 36:	// home
+				s.setValue(s.getOrientation() == "horizontal" ?
+					s.getMinimum() :
+					s.getMaximum());
+				break;
+			case 38:	// up
+			case 39:	// right
+				s.setValue(s.getValue() + s.getUnitIncrement());
+				break;
+
+			case 37:	// left
+			case 40:	// down
+				s.setValue(s.getValue() - s.getUnitIncrement());
+				break;
+		}
+
+		if (kc >= 33 && kc <= 40) {
+			return false;
+		}
+	},
+
+	onkeypress:	function (e) {
+		return;
+		e = Slider.eventHandlers.getEvent(e, this);
+		var kc = e.keyCode;
+		if (kc >= 33 && kc <= 40) {
+			return false;
+		}
+	},
+
+	onmousewheel:	function (e) {
+		return;
+		e = Slider.eventHandlers.getEvent(e, this);
+		var s = this.slider;
+		if (s._focused) {
+			s.setValue(s.getValue() + e.wheelDelta / 120 * s.getUnitIncrement());
+			// windows inverts this on horizontal sliders. That does not
+			// make sense to me
+			return false;
+		}
+	}
+};
+
+
+
+Slider.prototype.classNameTag = "dynamic-slider-control",
+
+Slider.prototype.setValue = function (v) {
+	this._range.setValue(v);
+	this.input.value = this.getValue();
+};
+
+Slider.prototype.getValue = function () {
+	return this._range.getValue();
+};
+
+Slider.prototype.setMinimum = function (v) {
+	this._range.setMinimum(v);
+	this.input.value = this.getValue();
+};
+
+Slider.prototype.getMinimum = function () {
+	return this._range.getMinimum();
+};
+
+Slider.prototype.setMaximum = function (v) {
+	this._range.setMaximum(v);
+	this.input.value = this.getValue();
+};
+
+Slider.prototype.getMaximum = function () {
+	return this._range.getMaximum();
+};
+
+Slider.prototype.setUnitIncrement = function (v) {
+	this._unitIncrement = v;
+};
+
+Slider.prototype.getUnitIncrement = function () {
+	return this._unitIncrement;
+};
+
+Slider.prototype.setBlockIncrement = function (v) {
+	this._blockIncrement = v;
+};
+
+Slider.prototype.getBlockIncrement = function () {
+	return this._blockIncrement;
+};
+
+Slider.prototype.getOrientation = function () {
+	return this._orientation;
+};
+
+Slider.prototype.setOrientation = function (sOrientation) {
+	if (sOrientation != this._orientation) {
+		if (Slider.isSupported && this.element) {
+			// add class name tag to class name
+			this.element.className = this.element.className.replace(this._orientation,
+									sOrientation);
+		}
+		this._orientation = sOrientation;
+		this.recalculate();
+
+	}
+};
+
+Slider.prototype.recalculate = function() {
+	if (!Slider.isSupported || !this.element) return;
+
+	var w = this.element.offsetWidth;
+	var h = this.element.offsetHeight;
+	var hw = this.handle.offsetWidth;
+	var hh = this.handle.offsetHeight;
+	var lw = this.line.offsetWidth;
+	var lh = this.line.offsetHeight;
+
+	// this assumes a border-box layout
+
+	if (this._orientation == "horizontal") {
+		this.handle.style.left = (w - hw) * (this.getValue() - this.getMinimum()) /
+			(this.getMaximum() - this.getMinimum()) + "px";
+		this.handle.style.top = (h - hh) / 2 + "px";
+
+		this.line.style.top = (h - lh) / 2 + "px";
+		this.line.style.left = hw / 2 + "px";
+		//this.line.style.right = hw / 2 + "px";
+		this.line.style.width = Math.max(0, w - hw - 2)+ "px";
+		this.line.firstChild.style.width = Math.max(0, w - hw - 4)+ "px";
+	}
+	else {
+		this.handle.style.left = (w - hw) / 2 + "px";
+		this.handle.style.top = h - hh - (h - hh) * (this.getValue() - this.getMinimum()) /
+			(this.getMaximum() - this.getMinimum()) + "px";
+
+		this.line.style.left = (w - lw) / 2 + "px";
+		this.line.style.top = hh / 2 + "px";
+		this.line.style.height = Math.max(0, h - hh - 2) + "px";	//hard coded border width
+		//this.line.style.bottom = hh / 2 + "px";
+		this.line.firstChild.style.height = Math.max(0, h - hh - 4) + "px";	//hard coded border width
+	}
+};
+
+Slider.prototype.ontimer = function () {
+	var hw = this.handle.offsetWidth;
+	var hh = this.handle.offsetHeight;
+	var hl = this.handle.offsetLeft;
+	var ht = this.handle.offsetTop;
+
+	if (this._orientation == "horizontal") {
+		if (this._mouseX > hl + hw &&
+			(this._increasing == null || this._increasing)) {
+			this.setValue(this.getValue() + this.getBlockIncrement());
+			this._increasing = true;
+		}
+		else if (this._mouseX < hl &&
+			(this._increasing == null || !this._increasing)) {
+			this.setValue(this.getValue() - this.getBlockIncrement());
+			this._increasing = false;
+		}
+	}
+	else {
+		if (this._mouseY > ht + hh &&
+			(this._increasing == null || !this._increasing)) {
+			this.setValue(this.getValue() - this.getBlockIncrement());
+			this._increasing = false;
+		}
+		else if (this._mouseY < ht &&
+			(this._increasing == null || this._increasing)) {
+			this.setValue(this.getValue() + this.getBlockIncrement());
+			this._increasing = true;
+		}
+	}
+
+	this._timer.start();
+};
+/*----------------------------------------------------------------------------\
+|                                 Timer Class                                 |
+|-----------------------------------------------------------------------------|
+|                         Created by Erik Arvidsson                           |
+|                  (http://webfx.eae.net/contact.html#erik)                   |
+|                      For WebFX (http://webfx.eae.net/)                      |
+|-----------------------------------------------------------------------------|
+| Object Oriented Encapsulation  of setTimeout  fires ontimer when the  timer |
+| is triggered. Does not work in IE 5.00                                      |
+|-----------------------------------------------------------------------------|
+|                   Copyright (c) 2002, 2006 Erik Arvidsson                   |
+|-----------------------------------------------------------------------------|
+| Licensed under the Apache License, Version 2.0 (the "License"); you may not |
+| use this file except in compliance with the License.  You may obtain a copy |
+| of the License at http://www.apache.org/licenses/LICENSE-2.0                |
+| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
+| Unless  required  by  applicable law or  agreed  to  in  writing,  software |
+| distributed under the License is distributed on an  "AS IS" BASIS,  WITHOUT |
+| WARRANTIES OR  CONDITIONS OF ANY KIND,  either express or implied.  See the |
+| License  for the  specific language  governing permissions  and limitations |
+| under the License.                                                          |
+|-----------------------------------------------------------------------------|
+| 2002-10-14 | Original version released                                      |
+| 2006-05-28 | Changed license to Apache Software License 2.0.                |
+|-----------------------------------------------------------------------------|
+| Created 2002-10-14 | All changes are in the log above. | Updated 2006-05-28 |
+\----------------------------------------------------------------------------*/
+
+function Timer(nPauseTime) {
+	this._pauseTime = typeof nPauseTime == "undefined" ? 1000 : nPauseTime;
+	this._timer = null;
+	this._isStarted = false;
+}
+
+Timer.prototype.start = function () {
+	if (this.isStarted())
+		this.stop();
+	var oThis = this;
+	this._timer = window.setTimeout(function () {
+		if (typeof oThis.ontimer == "function")
+			oThis.ontimer();
+	}, this._pauseTime);
+	this._isStarted = false;
+};
+
+Timer.prototype.stop = function () {
+	if (this._timer != null)
+		window.clearTimeout(this._timer);
+	this._isStarted = false;
+};
+
+Timer.prototype.isStarted = function () {
+	return this._isStarted;
+};
+
+Timer.prototype.getPauseTime = function () {
+	return this._pauseTime;
+};
+
+Timer.prototype.setPauseTime = function (nPauseTime) {
+	this._pauseTime = nPauseTime;
 };
 /*
 
@@ -18303,3975 +18303,5571 @@
 };
 
 })(jQuery);
-/**
+/*!
 
 JSZip - A Javascript class for generating and reading zip files
 <http://stuartk.com/jszip>
 
-(c) 2009-2012 Stuart Knightley <stuart [at] stuartk.com>
-Dual licenced under the MIT license or GPLv3. See LICENSE.markdown.
-
-Usage:
-   zip = new JSZip();
-   zip.file("hello.txt", "Hello, World!").add("tempfile", "nothing");
-   zip.folder("images").file("smile.gif", base64Data, {base64: true});
-   zip.file("Xmas.txt", "Ho ho ho !", {date : new Date("December 25, 2007 00:00:01")});
-   zip.remove("tempfile");
-
-   base64zip = zip.generate();
-
-**/
-
-/**
- * Representation a of zip file in js
- * @constructor
- * @param {String=|ArrayBuffer=|Uint8Array=} data the data to load, if any (optional).
- * @param {Object=} options the options for creating this objects (optional).
- */
-var JSZip = function(data, options) {
-   // object containing the files :
-   // {
-   //   "folder/" : {...},
-   //   "folder/data.txt" : {...}
-   // }
-   this.files = {};
-
-   // Where we are in the hierarchy
-   this.root = "";
-
-   if (data) {
-      this.load(data, options);
-   }
-};
-
-JSZip.signature = {
-   LOCAL_FILE_HEADER : "\x50\x4b\x03\x04",
-   CENTRAL_FILE_HEADER : "\x50\x4b\x01\x02",
-   CENTRAL_DIRECTORY_END : "\x50\x4b\x05\x06",
-   ZIP64_CENTRAL_DIRECTORY_LOCATOR : "\x50\x4b\x06\x07",
-   ZIP64_CENTRAL_DIRECTORY_END : "\x50\x4b\x06\x06",
-   DATA_DESCRIPTOR : "\x50\x4b\x07\x08"
-};
-
-// Default properties for a new file
-JSZip.defaults = {
-   base64: false,
-   binary: false,
-   dir: false,
-   date: null
-};
-
-
-JSZip.prototype = (function () {
-   /**
-    * A simple object representing a file in the zip file.
-    * @constructor
-    * @param {string} name the name of the file
-    * @param {string} data the data
-    * @param {Object} options the options of the file
-    */
-   var ZipObject = function (name, data, options) {
-      this.name = name;
-      this.data = data;
-      this.options = options;
-   };
-
-   ZipObject.prototype = {
-      /**
-       * Return the content as UTF8 string.
-       * @return {string} the UTF8 string.
-       */
-      asText : function () {
-         var result = this.data;
-         if (result === null || typeof result === "undefined") {
-            return "";
-         }
-         if (this.options.base64) {
-            result = JSZipBase64.decode(result);
-         }
-         if (this.options.binary) {
-            result = JSZip.prototype.utf8decode(result);
-         }
-         return result;
-      },
-      /**
-       * Returns the binary content.
-       * @return {string} the content as binary.
-       */
-      asBinary : function () {
-         var result = this.data;
-         if (result === null || typeof result === "undefined") {
-            return "";
-         }
-         if (this.options.base64) {
-            result = JSZipBase64.decode(result);
-         }
-         if (!this.options.binary) {
-            result = JSZip.prototype.utf8encode(result);
-         }
-         return result;
-      },
-      /**
-       * Returns the content as an Uint8Array.
-       * @return {Uint8Array} the content as an Uint8Array.
-       */
-      asUint8Array : function () {
-         return JSZip.utils.string2Uint8Array(this.asBinary());
-      },
-      /**
-       * Returns the content as an ArrayBuffer.
-       * @return {ArrayBuffer} the content as an ArrayBufer.
-       */
-      asArrayBuffer : function () {
-         return JSZip.utils.string2Uint8Array(this.asBinary()).buffer;
-      }
-   };
-
-   /**
-    * Transform an integer into a string in hexadecimal.
-    * @private
-    * @param {number} dec the number to convert.
-    * @param {number} bytes the number of bytes to generate.
-    * @returns {string} the result.
-    */
-   var decToHex = function(dec, bytes) {
-      var hex = "", i;
-      for(i = 0; i < bytes; i++) {
-         hex += String.fromCharCode(dec&0xff);
-         dec=dec>>>8;
-      }
-      return hex;
-   };
-
-   /**
-    * Merge the objects passed as parameters into a new one.
-    * @private
-    * @param {...Object} var_args All objects to merge.
-    * @return {Object} a new object with the data of the others.
-    */
-   var extend = function () {
-      var result = {}, i, attr;
-      for (i = 0; i < arguments.length; i++) { // arguments is not enumerable in some browsers
-         for (attr in arguments[i]) {
-            if (arguments[i].hasOwnProperty(attr) && typeof result[attr] === "undefined") {
-               result[attr] = arguments[i][attr];
-            }
-         }
-      }
-      return result;
-   };
-
-   /**
-    * Transforms the (incomplete) options from the user into the complete
-    * set of options to create a file.
-    * @private
-    * @param {Object} o the options from the user.
-    * @return {Object} the complete set of options.
-    */
-   var prepareFileAttrs = function (o) {
-      o = o || {};
-      if (o.base64 === true && o.binary == null) {
-         o.binary = true;
-      }
-      o = extend(o, JSZip.defaults);
-      o.date = o.date || new Date();
-
-      return o;
-   };
-
-  /**
-   * Add a file in the current folder.
-   * @private
-   * @param {string} name the name of the file
-   * @param {String|ArrayBuffer|Uint8Array} data the data of the file
-   * @param {Object} o the options of the file
-   * @return {Object} the new file.
-   */
-   var fileAdd = function (name, data, o) {
-      // be sure sub folders exist
-      var parent = parentFolder(name);
-      if (parent) {
-         folderAdd.call(this, parent);
-      }
-
-      o = prepareFileAttrs(o);
-
-      if (o.dir || data === null || typeof data === "undefined") {
-         o.base64 = false;
-         o.binary = false;
-         data = null;
-      } else if (JSZip.support.uint8array && data instanceof Uint8Array) {
-         o.base64 = false;
-         o.binary = true;
-         data = JSZip.utils.uint8Array2String(data);
-      } else if (JSZip.support.arraybuffer && data instanceof ArrayBuffer) {
-         o.base64 = false;
-         o.binary = true;
-         var bufferView = new Uint8Array(data);
-         data = JSZip.utils.uint8Array2String(bufferView);
-      } else if (o.binary && !o.base64) {
-         // optimizedBinaryString == true means that the file has already been filtered with a 0xFF mask
-         if (o.optimizedBinaryString !== true) {
-            // this is a string, not in a base64 format.
-            // Be sure that this is a correct "binary string"
-            data = JSZip.utils.string2binary(data);
-         }
-         // we remove this option since it's only relevant here
-         delete o.optimizedBinaryString;
-      }
-
-      return this.files[name] = new ZipObject(name, data, o);
-   };
-
-
-   /**
-    * Find the parent folder of the path.
-    * @private
-    * @param {string} path the path to use
-    * @return {string} the parent folder, or ""
-    */
-   var parentFolder = function (path) {
-      if (path.slice(-1) == '/') {
-         path = path.substring(0, path.length - 1);
-      }
-      var lastSlash = path.lastIndexOf('/');
-      return (lastSlash > 0) ? path.substring(0, lastSlash) : "";
-   };
-
-   /**
-    * Add a (sub) folder in the current folder.
-    * @private
-    * @param {string} name the folder's name
-    * @return {Object} the new folder.
-    */
-   var folderAdd = function (name) {
-      // Check the name ends with a /
-      if (name.slice(-1) != "/") {
-         name += "/"; // IE doesn't like substr(-1)
-      }
-
-      // Does this folder already exist?
-      if (!this.files[name]) {
-         // be sure sub folders exist
-         var parent = parentFolder(name);
-         if (parent) {
-            folderAdd.call(this, parent);
-         }
-
-         fileAdd.call(this, name, null, {dir:true});
+(c) 2009-2014 Stuart Knightley <stuart [at] stuartk.com>
+Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip/master/LICENSE.markdown.
+
+JSZip uses the library pako released under the MIT license :
+https://github.com/nodeca/pako/blob/master/LICENSE
+*/
+!function(a){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=a();else if("function"==typeof define&&define.amd)define([],a);else{var b;"undefined"!=typeof window?b=window:"undefined"!=typeof global?b=global:"undefined"!=typeof self&&(b=self),b.JSZip=a()}}(function(){return function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);throw new Error("Cannot find module '"+g+"'")}var j=c[g]={exports:{}};b[g][0].call(j.exports,function(a){var c=b[g][1][a];return e(c?c:a)},j,j.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g<d.length;g++)e(d[g]);return e}({1:[function(a,b,c){"use strict";var d="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";c.encode=function(a){for(var b,c,e,f,g,h,i,j="",k=0;k<a.length;)b=a.charCodeAt(k++),c=a.charCodeAt(k++),e=a.charCodeAt(k++),f=b>>2,g=(3&b)<<4|c>>4,h=(15&c)<<2|e>>6,i=63&e,isNaN(c)?h=i=64:isNaN(e)&&(i=64),j=j+d.charAt(f)+d.charAt(g)+d.charAt(h)+d.charAt(i);return j},c.decode=function(a){var b,c,e,f,g,h,i,j="",k=0;for(a=a.replace(/[^A-Za-z0-9\+\/\=]/g,"");k<a.length;)f=d.indexOf(a.charAt(k++)),g=d.indexOf(a.charAt(k++)),h=d.indexOf(a.charAt(k++)),i=d.indexOf(a.charAt(k++)),b=f<<2|g>>4,c=(15&g)<<4|h>>2,e=(3&h)<<6|i,j+=String.fromCharCode(b),64!=h&&(j+=String.fromCharCode(c)),64!=i&&(j+=String.fromCharCode(e));return j}},{}],2:[function(a,b){"use strict";function c(){this.compressedSize=0,this.uncompressedSize=0,this.crc32=0,this.compressionMethod=null,this.compressedContent=null}c.prototype={getContent:function(){return null},getCompressedContent:function(){return null}},b.exports=c},{}],3:[function(a,b,c){"use strict";c.STORE={magic:"\x00\x00",compress:function(a){return a},uncompress:function(a){return a},compressInputType:null,uncompressInputType:null},c.DEFLATE=a("./flate")},{"./flate":8}],4:[function(a,b){"use strict";var c=a("./utils"),d=[0,1996959894,3993919788,2567524794,124634137,1886057615,3915621685,2657392035,249268274,2044508324,3772115230,2547177864,162941995,2125561021,3887607047,2428444049,498536548,1789927666,4089016648,2227061214,450548861,1843258603,4107580753,2211677639,325883990,1684777152,4251122042,2321926636,335633487,1661365465,4195302755,2366115317,997073096,1281953886,3579855332,2724688242,1006888145,1258607687,3524101629,2768942443,901097722,1119000684,3686517206,2898065728,853044451,1172266101,3705015759,2882616665,651767980,1373503546,3369554304,3218104598,565507253,1454621731,3485111705,3099436303,671266974,1594198024,3322730930,2970347812,795835527,1483230225,3244367275,3060149565,1994146192,31158534,2563907772,4023717930,1907459465,112637215,2680153253,3904427059,2013776290,251722036,2517215374,3775830040,2137656763,141376813,2439277719,3865271297,1802195444,476864866,2238001368,4066508878,1812370925,453092731,2181625025,4111451223,1706088902,314042704,2344532202,4240017532,1658658271,366619977,2362670323,4224994405,1303535960,984961486,2747007092,3569037538,1256170817,1037604311,2765210733,3554079995,1131014506,879679996,2909243462,3663771856,1141124467,855842277,2852801631,3708648649,1342533948,654459306,3188396048,3373015174,1466479909,544179635,3110523913,3462522015,1591671054,702138776,2966460450,3352799412,1504918807,783551873,3082640443,3233442989,3988292384,2596254646,62317068,1957810842,3939845945,2647816111,81470997,1943803523,3814918930,2489596804,225274430,2053790376,3826175755,2466906013,167816743,2097651377,4027552580,2265490386,503444072,1762050814,4150417245,2154129355,426522225,1852507879,4275313526,2312317920,282753626,1742555852,4189708143,2394877945,397917763,1622183637,3604390888,2714866558,953729732,1340076626,3518719985,2797360999,1068828381,1219638859,3624741850,2936675148,906185462,1090812512,3747672003,2825379669,829329135,1181335161,3412177804,3160834842,628085408,1382605366,3423369109,3138078467,570562233,1426400815,3317316542,2998733608,733239954,1555261956,3268935591,3050360625,752459403,1541320221,2607071920,3965973030,1969922972,40735498,2617837225,3943577151,1913087877,83908371,2512341634,3803740692,2075208622,213261112,2463272603,3855990285,2094854071,198958881,2262029012,4057260610,1759359992,534414190,2176718541,4139329115,1873836001,414664567,2282248934,4279200368,1711684554,285281116,2405801727,4167216745,1634467795,376229701,2685067896,3608007406,1308918612,956543938,2808555105,3495958263,1231636301,1047427035,2932959818,3654703836,1088359270,936918e3,2847714899,3736837829,1202900863,817233897,3183342108,3401237130,1404277552,615818150,3134207493,3453421203,1423857449,601450431,3009837614,3294710456,1567103746,711928724,3020668471,3272380065,1510334235,755167117];b.exports=function(a,b){if("undefined"==typeof a||!a.length)return 0;var e="string"!==c.getTypeOf(a);"undefined"==typeof b&&(b=0);var f=0,g=0,h=0;b=-1^b;for(var i=0,j=a.length;j>i;i++)h=e?a[i]:a.charCodeAt(i),g=255&(b^h),f=d[g],b=b>>>8^f;return-1^b}},{"./utils":21}],5:[function(a,b){"use strict";function c(){this.data=null,this.length=0,this.index=0}var d=a("./utils");c.prototype={checkOffset:function(a){this.checkIndex(this.index+a)},checkIndex:function(a){if(this.length<a||0>a)throw new Error("End of data reached (data length = "+this.length+", asked index = "+a+"). Corrupted zip ?")},setIndex:function(a){this.checkIndex(a),this.index=a},skip:function(a){this.setIndex(this.index+a)},byteAt:function(){},readInt:function(a){var b,c=0;for(this.checkOffset(a),b=this.index+a-1;b>=this.index;b--)c=(c<<8)+this.byteAt(b);return this.index+=a,c},readString:function(a){return d.transformTo("string",this.readData(a))},readData:function(){},lastIndexOfSignature:function(){},readDate:function(){var a=this.readInt(4);return new Date((a>>25&127)+1980,(a>>21&15)-1,a>>16&31,a>>11&31,a>>5&63,(31&a)<<1)}},b.exports=c},{"./utils":21}],6:[function(a,b,c){"use strict";c.base64=!1,c.binary=!1,c.dir=!1,c.createFolders=!1,c.date=null,c.compression=null,c.comment=null},{}],7:[function(a,b,c){"use strict";var d=a("./utils");c.string2binary=function(a){return d.string2binary(a)},c.string2Uint8Array=function(a){return d.transformTo("uint8array",a)},c.uint8Array2String=function(a){return d.transformTo("string",a)},c.string2Blob=function(a){var b=d.transformTo("arraybuffer",a);return d.arrayBuffer2Blob(b)},c.arrayBuffer2Blob=function(a){return d.arrayBuffer2Blob(a)},c.transformTo=function(a,b){return d.transformTo(a,b)},c.getTypeOf=function(a){return d.getTypeOf(a)},c.checkSupport=function(a){return d.checkSupport(a)},c.MAX_VALUE_16BITS=d.MAX_VALUE_16BITS,c.MAX_VALUE_32BITS=d.MAX_VALUE_32BITS,c.pretty=function(a){return d.pretty(a)},c.findCompression=function(a){return d.findCompression(a)},c.isRegExp=function(a){return d.isRegExp(a)}},{"./utils":21}],8:[function(a,b,c){"use strict";var d="undefined"!=typeof Uint8Array&&"undefined"!=typeof Uint16Array&&"undefined"!=typeof Uint32Array,e=a("pako");c.uncompressInputType=d?"uint8array":"array",c.compressInputType=d?"uint8array":"array",c.magic="\b\x00",c.compress=function(a){return e.deflateRaw(a)},c.uncompress=function(a){return e.inflateRaw(a)}},{pako:24}],9:[function(a,b){"use strict";function c(a,b){return this instanceof c?(this.files={},this.comment=null,this.root="",a&&this.load(a,b),void(this.clone=function(){var a=new c;for(var b in this)"function"!=typeof this[b]&&(a[b]=this[b]);return a})):new c(a,b)}var d=a("./base64");c.prototype=a("./object"),c.prototype.load=a("./load"),c.support=a("./support"),c.defaults=a("./defaults"),c.utils=a("./deprecatedPublicUtils"),c.base64={encode:function(a){return d.encode(a)},decode:function(a){return d.decode(a)}},c.compressions=a("./compressions"),b.exports=c},{"./base64":1,"./compressions":3,"./defaults":6,"./deprecatedPublicUtils":7,"./load":10,"./object":13,"./support":17}],10:[function(a,b){"use strict";var c=a("./base64"),d=a("./zipEntries");b.exports=function(a,b){var e,f,g,h;for(b=b||{},b.base64&&(a=c.decode(a)),f=new d(a,b),e=f.files,g=0;g<e.length;g++)h=e[g],this.file(h.fileName,h.decompressed,{binary:!0,optimizedBinaryString:!0,date:h.date,dir:h.dir,comment:h.fileComment.length?h.fileComment:null,createFolders:b.createFolders});return f.zipComment.length&&(this.comment=f.zipComment),this}},{"./base64":1,"./zipEntries":22}],11:[function(a,b){(function(a){"use strict";b.exports=function(b,c){return new a(b,c)},b.exports.test=function(b){return a.isBuffer(b)}}).call(this,"undefined"!=typeof Buffer?Buffer:void 0)},{}],12:[function(a,b){"use strict";function c(a){this.data=a,this.length=this.data.length,this.index=0}var d=a("./uint8ArrayReader");c.prototype=new d,c.prototype.readData=function(a){this.checkOffset(a);var b=this.data.slice(this.index,this.index+a);return this.index+=a,b},b.exports=c},{"./uint8ArrayReader":18}],13:[function(a,b){"use strict";var c=a("./support"),d=a("./utils"),e=a("./crc32"),f=a("./signature"),g=a("./defaults"),h=a("./base64"),i=a("./compressions"),j=a("./compressedObject"),k=a("./nodeBuffer"),l=a("./utf8"),m=a("./stringWriter"),n=a("./uint8ArrayWriter"),o=function(a){if(a._data instanceof j&&(a._data=a._data.getContent(),a.options.binary=!0,a.options.base64=!1,"uint8array"===d.getTypeOf(a._data))){var b=a._data;a._data=new Uint8Array(b.length),0!==b.length&&a._data.set(b,0)}return a._data},p=function(a){var b=o(a),e=d.getTypeOf(b);return"string"===e?!a.options.binary&&c.nodebuffer?k(b,"utf-8"):a.asBinary():b},q=function(a){var b=o(this);return null===b||"undefined"==typeof b?"":(this.options.base64&&(b=h.decode(b)),b=a&&this.options.binary?A.utf8decode(b):d.transformTo("string",b),a||this.options.binary||(b=d.transformTo("string",A.utf8encode(b))),b)},r=function(a,b,c){this.name=a,this.dir=c.dir,this.date=c.date,this.comment=c.comment,this._data=b,this.options=c,this._initialMetadata={dir:c.dir,date:c.date}};r.prototype={asText:function(){return q.call(this,!0)},asBinary:function(){return q.call(this,!1)},asNodeBuffer:function(){var a=p(this);return d.transformTo("nodebuffer",a)},asUint8Array:function(){var a=p(this);return d.transformTo("uint8array",a)},asArrayBuffer:function(){return this.asUint8Array().buffer}};var s=function(a,b){var c,d="";for(c=0;b>c;c++)d+=String.fromCharCode(255&a),a>>>=8;return d},t=function(){var a,b,c={};for(a=0;a<arguments.length;a++)for(b in arguments[a])arguments[a].hasOwnProperty(b)&&"undefined"==typeof c[b]&&(c[b]=arguments[a][b]);return c},u=function(a){return a=a||{},a.base64!==!0||null!==a.binary&&void 0!==a.binary||(a.binary=!0),a=t(a,g),a.date=a.date||new Date,null!==a.compression&&(a.compression=a.compression.toUpperCase()),a},v=function(a,b,c){var e,f=d.getTypeOf(b);if(c=u(c),c.createFolders&&(e=w(a))&&x.call(this,e,!0),c.dir||null===b||"undefined"==typeof b)c.base64=!1,c.binary=!1,b=null;else if("string"===f)c.binary&&!c.base64&&c.optimizedBinaryString!==!0&&(b=d.string2binary(b));else{if(c.base64=!1,c.binary=!0,!(f||b instanceof j))throw new Error("The data of '"+a+"' is in an unsupported format !");"arraybuffer"===f&&(b=d.transformTo("uint8array",b))}var g=new r(a,b,c);return this.files[a]=g,g},w=function(a){"/"==a.slice(-1)&&(a=a.substring(0,a.length-1));var b=a.lastIndexOf("/");return b>0?a.substring(0,b):""},x=function(a,b){return"/"!=a.slice(-1)&&(a+="/"),b="undefined"!=typeof b?b:!1,this.files[a]||v.call(this,a,null,{dir:!0,createFolders:b}),this.files[a]},y=function(a,b){var c,f=new j;return a._data instanceof j?(f.uncompressedSize=a._data.uncompressedSize,f.crc32=a._data.crc32,0===f.uncompressedSize||a.dir?(b=i.STORE,f.compressedContent="",f.crc32=0):a._data.compressionMethod===b.magic?f.compressedContent=a._data.getCompressedContent():(c=a._data.getContent(),f.compressedContent=b.compress(d.transformTo(b.compressInputType,c)))):(c=p(a),(!c||0===c.length||a.dir)&&(b=i.STORE,c=""),f.uncompressedSize=c.length,f.crc32=e(c),f.compressedContent=b.compress(d.transformTo(b.compressInputType,c))),f.compressedSize=f.compressedContent.length,f.compressionMethod=b.magic,f},z=function(a,b,c,g){var h,i,j,k,m=(c.compressedContent,d.transformTo("string",l.utf8encode(b.name))),n=b.comment||"",o=d.transformTo("string",l.utf8encode(n)),p=m.length!==b.name.length,q=o.length!==n.length,r=b.options,t="",u="",v="";j=b._initialMetadata.dir!==b.dir?b.dir:r.dir,k=b._initialMetadata.date!==b.date?b.date:r.date,h=k.getHours(),h<<=6,h|=k.getMinutes(),h<<=5,h|=k.getSeconds()/2,i=k.getFullYear()-1980,i<<=4,i|=k.getMonth()+1,i<<=5,i|=k.getDate(),p&&(u=s(1,1)+s(e(m),4)+m,t+="up"+s(u.length,2)+u),q&&(v=s(1,1)+s(this.crc32(o),4)+o,t+="uc"+s(v.length,2)+v);var w="";w+="\n\x00",w+=p||q?"\x00\b":"\x00\x00",w+=c.compressionMethod,w+=s(h,2),w+=s(i,2),w+=s(c.crc32,4),w+=s(c.compressedSize,4),w+=s(c.uncompressedSize,4),w+=s(m.length,2),w+=s(t.length,2);var x=f.LOCAL_FILE_HEADER+w+m+t,y=f.CENTRAL_FILE_HEADER+"\x00"+w+s(o.length,2)+"\x00\x00\x00\x00"+(j===!0?"\x00\x00\x00":"\x00\x00\x00\x00")+s(g,4)+m+t+o;return{fileRecord:x,dirRecord:y,compressedObject:c}},A={load:function(){throw new Error("Load method is not defined. Is the file jszip-load.js included ?")},filter:function(a){var b,c,d,e,f=[];for(b in this.files)this.files.hasOwnProperty(b)&&(d=this.files[b],e=new r(d.name,d._data,t(d.options)),c=b.slice(this.root.length,b.length),b.slice(0,this.root.length)===this.root&&a(c,e)&&f.push(e));return f},file:function(a,b,c){if(1===arguments.length){if(d.isRegExp(a)){var e=a;return this.filter(function(a,b){return!b.dir&&e.test(a)})}return this.filter(function(b,c){return!c.dir&&b===a})[0]||null}return a=this.root+a,v.call(this,a,b,c),this},folder:function(a){if(!a)return this;if(d.isRegExp(a))return this.filter(function(b,c){return c.dir&&a.test(b)});var b=this.root+a,c=x.call(this,b),e=this.clone();return e.root=c.name,e},remove:function(a){a=this.root+a;var b=this.files[a];if(b||("/"!=a.slice(-1)&&(a+="/"),b=this.files[a]),b&&!b.dir)delete this.files[a];else for(var c=this.filter(function(b,c){return c.name.slice(0,a.length)===a}),d=0;d<c.length;d++)delete this.files[c[d].name];return this},generate:function(a){a=t(a||{},{base64:!0,compression:"STORE",type:"base64",comment:null}),d.checkSupport(a.type);var b,c,e=[],g=0,j=0,k=d.transformTo("string",this.utf8encode(a.comment||this.comment||""));for(var l in this.files)if(this.files.hasOwnProperty(l)){var o=this.files[l],p=o.options.compression||a.compression.toUpperCase(),q=i[p];if(!q)throw new Error(p+" is not a valid compression method !");var r=y.call(this,o,q),u=z.call(this,l,o,r,g);g+=u.fileRecord.length+r.compressedSize,j+=u.dirRecord.length,e.push(u)}var v="";v=f.CENTRAL_DIRECTORY_END+"\x00\x00\x00\x00"+s(e.length,2)+s(e.length,2)+s(j,4)+s(g,4)+s(k.length,2)+k;var w=a.type.toLowerCase();for(b="uint8array"===w||"arraybuffer"===w||"blob"===w||"nodebuffer"===w?new n(g+j+v.length):new m(g+j+v.length),c=0;c<e.length;c++)b.append(e[c].fileRecord),b.append(e[c].compressedObject.compressedContent);for(c=0;c<e.length;c++)b.append(e[c].dirRecord);b.append(v);var x=b.finalize();switch(a.type.toLowerCase()){case"uint8array":case"arraybuffer":case"nodebuffer":return d.transformTo(a.type.toLowerCase(),x);case"blob":return d.arrayBuffer2Blob(d.transformTo("arraybuffer",x));case"base64":return a.base64?h.encode(x):x;default:return x}},crc32:function(a,b){return e(a,b)},utf8encode:function(a){return d.transformTo("string",l.utf8encode(a))},utf8decode:function(a){return l.utf8decode(a)}};b.exports=A},{"./base64":1,"./compressedObject":2,"./compressions":3,"./crc32":4,"./defaults":6,"./nodeBuffer":11,"./signature":14,"./stringWriter":16,"./support":17,"./uint8ArrayWriter":19,"./utf8":20,"./utils":21}],14:[function(a,b,c){"use strict";c.LOCAL_FILE_HEADER="PK",c.CENTRAL_FILE_HEADER="PK",c.CENTRAL_DIRECTORY_END="PK",c.ZIP64_CENTRAL_DIRECTORY_LOCATOR="PK",c.ZIP64_CENTRAL_DIRECTORY_END="PK",c.DATA_DESCRIPTOR="PK\b"},{}],15:[function(a,b){"use strict";function c(a,b){this.data=a,b||(this.data=e.string2binary(this.data)),this.length=this.data.length,this.index=0}var d=a("./dataReader"),e=a("./utils");c.prototype=new d,c.prototype.byteAt=function(a){return this.data.charCodeAt(a)},c.prototype.lastIndexOfSignature=function(a){return this.data.lastIndexOf(a)},c.prototype.readData=function(a){this.checkOffset(a);var b=this.data.slice(this.index,this.index+a);return this.index+=a,b},b.exports=c},{"./dataReader":5,"./utils":21}],16:[function(a,b){"use strict";var c=a("./utils"),d=function(){this.data=[]};d.prototype={append:function(a){a=c.transformTo("string",a),this.data.push(a)},finalize:function(){return this.data.join("")}},b.exports=d},{"./utils":21}],17:[function(a,b,c){(function(a){"use strict";if(c.base64=!0,c.array=!0,c.string=!0,c.arraybuffer="undefined"!=typeof ArrayBuffer&&"undefined"!=typeof Uint8Array,c.nodebuffer="undefined"!=typeof a,c.uint8array="undefined"!=typeof Uint8Array,"undefined"==typeof ArrayBuffer)c.blob=!1;else{var b=new ArrayBuffer(0);try{c.blob=0===new Blob([b],{type:"application/zip"}).size}catch(d){try{var e=window.BlobBuilder||window.WebKitBlobBuilder||window.MozBlobBuilder||window.MSBlobBuilder,f=new e;f.append(b),c.blob=0===f.getBlob("application/zip").size}catch(d){c.blob=!1}}}}).call(this,"undefined"!=typeof Buffer?Buffer:void 0)},{}],18:[function(a,b){"use strict";function c(a){a&&(this.data=a,this.length=this.data.length,this.index=0)}var d=a("./dataReader");c.prototype=new d,c.prototype.byteAt=function(a){return this.data[a]},c.prototype.lastIndexOfSignature=function(a){for(var b=a.charCodeAt(0),c=a.charCodeAt(1),d=a.charCodeAt(2),e=a.charCodeAt(3),f=this.length-4;f>=0;--f)if(this.data[f]===b&&this.data[f+1]===c&&this.data[f+2]===d&&this.data[f+3]===e)return f;return-1},c.prototype.readData=function(a){if(this.checkOffset(a),0===a)return new Uint8Array(0);var b=this.data.subarray(this.index,this.index+a);return this.index+=a,b},b.exports=c},{"./dataReader":5}],19:[function(a,b){"use strict";var c=a("./utils"),d=function(a){this.data=new Uint8Array(a),this.index=0};d.prototype={append:function(a){0!==a.length&&(a=c.transformTo("uint8array",a),this.data.set(a,this.index),this.index+=a.length)},finalize:function(){return this.data}},b.exports=d},{"./utils":21}],20:[function(a,b,c){"use strict";for(var d=a("./utils"),e=a("./support"),f=a("./nodeBuffer"),g=new Array(256),h=0;256>h;h++)g[h]=h>=252?6:h>=248?5:h>=240?4:h>=224?3:h>=192?2:1;g[254]=g[254]=1;var i=function(a){var b,c,d,f,g,h=a.length,i=0;for(f=0;h>f;f++)c=a.charCodeAt(f),55296===(64512&c)&&h>f+1&&(d=a.charCodeAt(f+1),56320===(64512&d)&&(c=65536+(c-55296<<10)+(d-56320),f++)),i+=128>c?1:2048>c?2:65536>c?3:4;for(b=e.uint8array?new Uint8Array(i):new Array(i),g=0,f=0;i>g;f++)c=a.charCodeAt(f),55296===(64512&c)&&h>f+1&&(d=a.charCodeAt(f+1),56320===(64512&d)&&(c=65536+(c-55296<<10)+(d-56320),f++)),128>c?b[g++]=c:2048>c?(b[g++]=192|c>>>6,b[g++]=128|63&c):65536>c?(b[g++]=224|c>>>12,b[g++]=128|c>>>6&63,b[g++]=128|63&c):(b[g++]=240|c>>>18,b[g++]=128|c>>>12&63,b[g++]=128|c>>>6&63,b[g++]=128|63&c);return b},j=function(a,b){var c;for(b=b||a.length,b>a.length&&(b=a.length),c=b-1;c>=0&&128===(192&a[c]);)c--;return 0>c?b:0===c?b:c+g[a[c]]>b?c:b},k=function(a){var b,c,e,f,h=a.length,i=new Array(2*h);for(c=0,b=0;h>b;)if(e=a[b++],128>e)i[c++]=e;else if(f=g[e],f>4)i[c++]=65533,b+=f-1;else{for(e&=2===f?31:3===f?15:7;f>1&&h>b;)e=e<<6|63&a[b++],f--;f>1?i[c++]=65533:65536>e?i[c++]=e:(e-=65536,i[c++]=55296|e>>10&1023,i[c++]=56320|1023&e)}return i.length!==c&&(i.subarray?i=i.subarray(0,c):i.length=c),d.applyFromCharCode(i)};c.utf8encode=function(a){return e.nodebuffer?f(a,"utf-8"):i(a)},c.utf8decode=function(a){if(e.nodebuffer)return d.transformTo("nodebuffer",a).toString("utf-8");a=d.transformTo(e.uint8array?"uint8array":"array",a);for(var b=[],c=0,f=a.length,g=65536;f>c;){var h=j(a,Math.min(c+g,f));b.push(e.uint8array?k(a.subarray(c,h)):k(a.slice(c,h))),c=h}return b.join("")}},{"./nodeBuffer":11,"./support":17,"./utils":21}],21:[function(a,b,c){"use strict";function d(a){return a}function e(a,b){for(var c=0;c<a.length;++c)b[c]=255&a.charCodeAt(c);return b}function f(a){var b=65536,d=[],e=a.length,f=c.getTypeOf(a),g=0,h=!0;try{switch(f){case"uint8array":String.fromCharCode.apply(null,new Uint8Array(0));break;case"nodebuffer":String.fromCharCode.apply(null,j(0))}}catch(i){h=!1}if(!h){for(var k="",l=0;l<a.length;l++)k+=String.fromCharCode(a[l]);return k}for(;e>g&&b>1;)try{d.push("array"===f||"nodebuffer"===f?String.fromCharCode.apply(null,a.slice(g,Math.min(g+b,e))):String.fromCharCode.apply(null,a.subarray(g,Math.min(g+b,e)))),g+=b}catch(i){b=Math.floor(b/2)}return d.join("")}function g(a,b){for(var c=0;c<a.length;c++)b[c]=a[c];return b}var h=a("./support"),i=a("./compressions"),j=a("./nodeBuffer");c.string2binary=function(a){for(var b="",c=0;c<a.length;c++)b+=String.fromCharCode(255&a.charCodeAt(c));return b},c.arrayBuffer2Blob=function(a){c.checkSupport("blob");try{return new Blob([a],{type:"application/zip"})}catch(b){try{var d=window.BlobBuilder||window.WebKitBlobBuilder||window.MozBlobBuilder||window.MSBlobBuilder,e=new d;return e.append(a),e.getBlob("application/zip")}catch(b){throw new Error("Bug : can't construct the Blob.")}}},c.applyFromCharCode=f;var k={};k.string={string:d,array:function(a){return e(a,new Array(a.length))},arraybuffer:function(a){return k.string.uint8array(a).buffer},uint8array:function(a){return e(a,new Uint8Array(a.length))},nodebuffer:function(a){return e(a,j(a.length))}},k.array={string:f,array:d,arraybuffer:function(a){return new Uint8Array(a).buffer},uint8array:function(a){return new Uint8Array(a)},nodebuffer:function(a){return j(a)}},k.arraybuffer={string:function(a){return f(new Uint8Array(a))},array:function(a){return g(new Uint8Array(a),new Array(a.byteLength))},arraybuffer:d,uint8array:function(a){return new Uint8Array(a)},nodebuffer:function(a){return j(new Uint8Array(a))}},k.uint8array={string:f,array:function(a){return g(a,new Array(a.length))},arraybuffer:function(a){return a.buffer},uint8array:d,nodebuffer:function(a){return j(a)}},k.nodebuffer={string:f,array:function(a){return g(a,new Array(a.length))},arraybuffer:function(a){return k.nodebuffer.uint8array(a).buffer},uint8array:function(a){return g(a,new Uint8Array(a.length))},nodebuffer:d},c.transformTo=function(a,b){if(b||(b=""),!a)return b;c.checkSupport(a);var d=c.getTypeOf(b),e=k[d][a](b);return e},c.getTypeOf=function(a){return"string"==typeof a?"string":"[object Array]"===Object.prototype.toString.call(a)?"array":h.nodebuffer&&j.test(a)?"nodebuffer":h.uint8array&&a instanceof Uint8Array?"uint8array":h.arraybuffer&&a instanceof ArrayBuffer?"arraybuffer":void 0},c.checkSupport=function(a){var b=h[a.toLowerCase()];if(!b)throw new Error(a+" is not supported by this browser")},c.MAX_VALUE_16BITS=65535,c.MAX_VALUE_32BITS=-1,c.pretty=function(a){var b,c,d="";for(c=0;c<(a||"").length;c++)b=a.charCodeAt(c),d+="\\x"+(16>b?"0":"")+b.toString(16).toUpperCase();return d},c.findCompression=function(a){for(var b in i)if(i.hasOwnProperty(b)&&i[b].magic===a)return i[b];return null},c.isRegExp=function(a){return"[object RegExp]"===Object.prototype.toString.call(a)}},{"./compressions":3,"./nodeBuffer":11,"./support":17}],22:[function(a,b){"use strict";function c(a,b){this.files=[],this.loadOptions=b,a&&this.load(a)}var d=a("./stringReader"),e=a("./nodeBufferReader"),f=a("./uint8ArrayReader"),g=a("./utils"),h=a("./signature"),i=a("./zipEntry"),j=a("./support"),k=a("./object");c.prototype={checkSignature:function(a){var b=this.reader.readString(4);if(b!==a)throw new Error("Corrupted zip or bug : unexpected signature ("+g.pretty(b)+", expected "+g.pretty(a)+")")},readBlockEndOfCentral:function(){this.diskNumber=this.reader.readInt(2),this.diskWithCentralDirStart=this.reader.readInt(2),this.centralDirRecordsOnThisDisk=this.reader.readInt(2),this.centralDirRecords=this.reader.readInt(2),this.centralDirSize=this.reader.readInt(4),this.centralDirOffset=this.reader.readInt(4),this.zipCommentLength=this.reader.readInt(2),this.zipComment=this.reader.readString(this.zipCommentLength),this.zipComment=k.utf8decode(this.zipComment)},readBlockZip64EndOfCentral:function(){this.zip64EndOfCentralSize=this.reader.readInt(8),this.versionMadeBy=this.reader.readString(2),this.versionNeeded=this.reader.readInt(2),this.diskNumber=this.reader.readInt(4),this.diskWithCentralDirStart=this.reader.readInt(4),this.centralDirRecordsOnThisDisk=this.reader.readInt(8),this.centralDirRecords=this.reader.readInt(8),this.centralDirSize=this.reader.readInt(8),this.centralDirOffset=this.reader.readInt(8),this.zip64ExtensibleData={};for(var a,b,c,d=this.zip64EndOfCentralSize-44,e=0;d>e;)a=this.reader.readInt(2),b=this.reader.readInt(4),c=this.reader.readString(b),this.zip64ExtensibleData[a]={id:a,length:b,value:c}},readBlockZip64EndOfCentralLocator:function(){if(this.diskWithZip64CentralDirStart=this.reader.readInt(4),this.relativeOffsetEndOfZip64CentralDir=this.reader.readInt(8),this.disksCount=this.reader.readInt(4),this.disksCount>1)throw new Error("Multi-volumes zip are not supported")},readLocalFiles:function(){var a,b;for(a=0;a<this.files.length;a++)b=this.files[a],this.reader.setIndex(b.localHeaderOffset),this.checkSignature(h.LOCAL_FILE_HEADER),b.readLocalPart(this.reader),b.handleUTF8()},readCentralDir:function(){var a;for(this.reader.setIndex(this.centralDirOffset);this.reader.readString(4)===h.CENTRAL_FILE_HEADER;)a=new i({zip64:this.zip64},this.loadOptions),a.readCentralPart(this.reader),this.files.push(a)},readEndOfCentral:function(){var a=this.reader.lastIndexOfSignature(h.CENTRAL_DIRECTORY_END);if(-1===a)throw new Error("Corrupted zip : can't find end of central directory");if(this.reader.setIndex(a),this.checkSignature(h.CENTRAL_DIRECTORY_END),this.readBlockEndOfCentral(),this.diskNumber===g.MAX_VALUE_16BITS||this.diskWithCentralDirStart===g.MAX_VALUE_16BITS||this.centralDirRecordsOnThisDisk===g.MAX_VALUE_16BITS||this.centralDirRecords===g.MAX_VALUE_16BITS||this.centralDirSize===g.MAX_VALUE_32BITS||this.centralDirOffset===g.MAX_VALUE_32BITS){if(this.zip64=!0,a=this.reader.lastIndexOfSignature(h.ZIP64_CENTRAL_DIRECTORY_LOCATOR),-1===a)throw new Error("Corrupted zip : can't find the ZIP64 end of central directory locator");this.reader.setIndex(a),this.checkSignature(h.ZIP64_CENTRAL_DIRECTORY_LOCATOR),this.readBlockZip64EndOfCentralLocator(),this.reader.setIndex(this.relativeOffsetEndOfZip64CentralDir),this.checkSignature(h.ZIP64_CENTRAL_DIRECTORY_END),this.readBlockZip64EndOfCentral()}},prepareReader:function(a){var b=g.getTypeOf(a);this.reader="string"!==b||j.uint8array?"nodebuffer"===b?new e(a):new f(g.transformTo("uint8array",a)):new d(a,this.loadOptions.optimizedBinaryString)},load:function(a){this.prepareReader(a),this.readEndOfCentral(),this.readCentralDir(),this.readLocalFiles()}},b.exports=c},{"./nodeBufferReader":12,"./object":13,"./signature":14,"./stringReader":15,"./support":17,"./uint8ArrayReader":18,"./utils":21,"./zipEntry":23}],23:[function(a,b){"use strict";function c(a,b){this.options=a,this.loadOptions=b}var d=a("./stringReader"),e=a("./utils"),f=a("./compressedObject"),g=a("./object");c.prototype={isEncrypted:function(){return 1===(1&this.bitFlag)},useUTF8:function(){return 2048===(2048&this.bitFlag)},prepareCompressedContent:function(a,b,c){return function(){var d=a.index;a.setIndex(b);var e=a.readData(c);return a.setIndex(d),e}},prepareContent:function(a,b,c,d,f){return function(){var a=e.transformTo(d.uncompressInputType,this.getCompressedContent()),b=d.uncompress(a);if(b.length!==f)throw new Error("Bug : uncompressed data size mismatch");return b}},readLocalPart:function(a){var b,c;if(a.skip(22),this.fileNameLength=a.readInt(2),c=a.readInt(2),this.fileName=a.readString(this.fileNameLength),a.skip(c),-1==this.compressedSize||-1==this.uncompressedSize)throw new Error("Bug or corrupted zip : didn't get enough informations from the central directory (compressedSize == -1 || uncompressedSize == -1)");if(b=e.findCompression(this.compressionMethod),null===b)throw new Error("Corrupted zip : compression "+e.pretty(this.compressionMethod)+" unknown (inner file : "+this.fileName+")");if(this.decompressed=new f,this.decompressed.compressedSize=this.compressedSize,this.decompressed.uncompressedSize=this.uncompressedSize,this.decompressed.crc32=this.crc32,this.decompressed.compressionMethod=this.compressionMethod,this.decompressed.getCompressedContent=this.prepareCompressedContent(a,a.index,this.compressedSize,b),this.decompressed.getContent=this.prepareContent(a,a.index,this.compressedSize,b,this.uncompressedSize),this.loadOptions.checkCRC32&&(this.decompressed=e.transformTo("string",this.decompressed.getContent()),g.crc32(this.decompressed)!==this.crc32))throw new Error("Corrupted zip : CRC32 mismatch")},readCentralPart:function(a){if(this.versionMadeBy=a.readString(2),this.versionNeeded=a.readInt(2),this.bitFlag=a.readInt(2),this.compressionMethod=a.readString(2),this.date=a.readDate(),this.crc32=a.readInt(4),this.compressedSize=a.readInt(4),this.uncompressedSize=a.readInt(4),this.fileNameLength=a.readInt(2),this.extraFieldsLength=a.readInt(2),this.fileCommentLength=a.readInt(2),this.diskNumberStart=a.readInt(2),this.internalFileAttributes=a.readInt(2),this.externalFileAttributes=a.readInt(4),this.localHeaderOffset=a.readInt(4),this.isEncrypted())throw new Error("Encrypted zip are not supported");this.fileName=a.readString(this.fileNameLength),this.readExtraFields(a),this.parseZIP64ExtraField(a),this.fileComment=a.readString(this.fileCommentLength),this.dir=16&this.externalFileAttributes?!0:!1},parseZIP64ExtraField:function(){if(this.extraFields[1]){var a=new d(this.extraFields[1].value);this.uncompressedSize===e.MAX_VALUE_32BITS&&(this.uncompressedSize=a.readInt(8)),this.compressedSize===e.MAX_VALUE_32BITS&&(this.compressedSize=a.readInt(8)),this.localHeaderOffset===e.MAX_VALUE_32BITS&&(this.localHeaderOffset=a.readInt(8)),this.diskNumberStart===e.MAX_VALUE_32BITS&&(this.diskNumberStart=a.readInt(4))}},readExtraFields:function(a){var b,c,d,e=a.index;for(this.extraFields=this.extraFields||{};a.index<e+this.extraFieldsLength;)b=a.readInt(2),c=a.readInt(2),d=a.readString(c),this.extraFields[b]={id:b,length:c,value:d}},handleUTF8:function(){if(this.useUTF8())this.fileName=g.utf8decode(this.fileName),this.fileComment=g.utf8decode(this.fileComment);else{var a=this.findExtraFieldUnicodePath();null!==a&&(this.fileName=a);var b=this.findExtraFieldUnicodeComment();null!==b&&(this.fileComment=b)}},findExtraFieldUnicodePath:function(){var a=this.extraFields[28789];if(a){var b=new d(a.value);return 1!==b.readInt(1)?null:g.crc32(this.fileName)!==b.readInt(4)?null:g.utf8decode(b.readString(a.length-5))}return null},findExtraFieldUnicodeComment:function(){var a=this.extraFields[25461];if(a){var b=new d(a.value);return 1!==b.readInt(1)?null:g.crc32(this.fileComment)!==b.readInt(4)?null:g.utf8decode(b.readString(a.length-5))}return null}},b.exports=c},{"./compressedObject":2,"./object":13,"./stringReader":15,"./utils":21}],24:[function(a,b){"use strict";var c=a("./lib/utils/common").assign,d=a("./lib/deflate"),e=a("./lib/inflate"),f=a("./lib/zlib/constants"),g={};c(g,d,e,f),b.exports=g},{"./lib/deflate":25,"./lib/inflate":26,"./lib/utils/common":27,"./lib/zlib/constants":30}],25:[function(a,b,c){"use strict";function d(a,b){var c=new s(b);if(c.push(a,!0),c.err)throw c.msg;return c.result}function e(a,b){return b=b||{},b.raw=!0,d(a,b)}function f(a,b){return b=b||{},b.gzip=!0,d(a,b)}var g=a("./zlib/deflate.js"),h=a("./utils/common"),i=a("./utils/strings"),j=a("./zlib/messages"),k=a("./zlib/zstream"),l=0,m=4,n=0,o=1,p=-1,q=0,r=8,s=function(a){this.options=h.assign({level:p,method:r,chunkSize:16384,windowBits:15,memLevel:8,strategy:q,to:""},a||{});var b=this.options;b.raw&&b.windowBits>0?b.windowBits=-b.windowBits:b.gzip&&b.windowBits>0&&b.windowBits<16&&(b.windowBits+=16),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new k,this.strm.avail_out=0;var c=g.deflateInit2(this.strm,b.level,b.method,b.windowBits,b.memLevel,b.strategy);if(c!==n)throw new Error(j[c]);b.header&&g.deflateSetHeader(this.strm,b.header)
+};s.prototype.push=function(a,b){var c,d,e=this.strm,f=this.options.chunkSize;if(this.ended)return!1;d=b===~~b?b:b===!0?m:l,e.input="string"==typeof a?i.string2buf(a):a,e.next_in=0,e.avail_in=e.input.length;do{if(0===e.avail_out&&(e.output=new h.Buf8(f),e.next_out=0,e.avail_out=f),c=g.deflate(e,d),c!==o&&c!==n)return this.onEnd(c),this.ended=!0,!1;(0===e.avail_out||0===e.avail_in&&d===m)&&this.onData("string"===this.options.to?i.buf2binstring(h.shrinkBuf(e.output,e.next_out)):h.shrinkBuf(e.output,e.next_out))}while((e.avail_in>0||0===e.avail_out)&&c!==o);return d===m?(c=g.deflateEnd(this.strm),this.onEnd(c),this.ended=!0,c===n):!0},s.prototype.onData=function(a){this.chunks.push(a)},s.prototype.onEnd=function(a){a===n&&(this.result="string"===this.options.to?this.chunks.join(""):h.flattenChunks(this.chunks)),this.chunks=[],this.err=a,this.msg=this.strm.msg},c.Deflate=s,c.deflate=d,c.deflateRaw=e,c.gzip=f},{"./utils/common":27,"./utils/strings":28,"./zlib/deflate.js":32,"./zlib/messages":37,"./zlib/zstream":39}],26:[function(a,b,c){"use strict";function d(a,b){var c=new m(b);if(c.push(a,!0),c.err)throw c.msg;return c.result}function e(a,b){return b=b||{},b.raw=!0,d(a,b)}var f=a("./zlib/inflate.js"),g=a("./utils/common"),h=a("./utils/strings"),i=a("./zlib/constants"),j=a("./zlib/messages"),k=a("./zlib/zstream"),l=a("./zlib/gzheader"),m=function(a){this.options=g.assign({chunkSize:16384,windowBits:0,to:""},a||{});var b=this.options;b.raw&&b.windowBits>=0&&b.windowBits<16&&(b.windowBits=-b.windowBits,0===b.windowBits&&(b.windowBits=-15)),!(b.windowBits>=0&&b.windowBits<16)||a&&a.windowBits||(b.windowBits+=32),b.windowBits>15&&b.windowBits<48&&0===(15&b.windowBits)&&(b.windowBits|=15),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new k,this.strm.avail_out=0;var c=f.inflateInit2(this.strm,b.windowBits);if(c!==i.Z_OK)throw new Error(j[c]);this.header=new l,f.inflateGetHeader(this.strm,this.header)};m.prototype.push=function(a,b){var c,d,e,j,k,l=this.strm,m=this.options.chunkSize;if(this.ended)return!1;d=b===~~b?b:b===!0?i.Z_FINISH:i.Z_NO_FLUSH,l.input="string"==typeof a?h.binstring2buf(a):a,l.next_in=0,l.avail_in=l.input.length;do{if(0===l.avail_out&&(l.output=new g.Buf8(m),l.next_out=0,l.avail_out=m),c=f.inflate(l,i.Z_NO_FLUSH),c!==i.Z_STREAM_END&&c!==i.Z_OK)return this.onEnd(c),this.ended=!0,!1;l.next_out&&(0===l.avail_out||c===i.Z_STREAM_END||0===l.avail_in&&d===i.Z_FINISH)&&("string"===this.options.to?(e=h.utf8border(l.output,l.next_out),j=l.next_out-e,k=h.buf2string(l.output,e),l.next_out=j,l.avail_out=m-j,j&&g.arraySet(l.output,l.output,e,j,0),this.onData(k)):this.onData(g.shrinkBuf(l.output,l.next_out)))}while(l.avail_in>0&&c!==i.Z_STREAM_END);return c===i.Z_STREAM_END&&(d=i.Z_FINISH),d===i.Z_FINISH?(c=f.inflateEnd(this.strm),this.onEnd(c),this.ended=!0,c===i.Z_OK):!0},m.prototype.onData=function(a){this.chunks.push(a)},m.prototype.onEnd=function(a){a===i.Z_OK&&(this.result="string"===this.options.to?this.chunks.join(""):g.flattenChunks(this.chunks)),this.chunks=[],this.err=a,this.msg=this.strm.msg},c.Inflate=m,c.inflate=d,c.inflateRaw=e,c.ungzip=d},{"./utils/common":27,"./utils/strings":28,"./zlib/constants":30,"./zlib/gzheader":33,"./zlib/inflate.js":35,"./zlib/messages":37,"./zlib/zstream":39}],27:[function(a,b,c){"use strict";var d="undefined"!=typeof Uint8Array&&"undefined"!=typeof Uint16Array&&"undefined"!=typeof Int32Array;c.assign=function(a){for(var b=Array.prototype.slice.call(arguments,1);b.length;){var c=b.shift();if(c){if("object"!=typeof c)throw new TypeError(c+"must be non-object");for(var d in c)c.hasOwnProperty(d)&&(a[d]=c[d])}}return a},c.shrinkBuf=function(a,b){return a.length===b?a:a.subarray?a.subarray(0,b):(a.length=b,a)};var e={arraySet:function(a,b,c,d,e){if(b.subarray&&a.subarray)return void a.set(b.subarray(c,c+d),e);for(var f=0;d>f;f++)a[e+f]=b[c+f]},flattenChunks:function(a){var b,c,d,e,f,g;for(d=0,b=0,c=a.length;c>b;b++)d+=a[b].length;for(g=new Uint8Array(d),e=0,b=0,c=a.length;c>b;b++)f=a[b],g.set(f,e),e+=f.length;return g}},f={arraySet:function(a,b,c,d,e){for(var f=0;d>f;f++)a[e+f]=b[c+f]},flattenChunks:function(a){return[].concat.apply([],a)}};c.setTyped=function(a){a?(c.Buf8=Uint8Array,c.Buf16=Uint16Array,c.Buf32=Int32Array,c.assign(c,e)):(c.Buf8=Array,c.Buf16=Array,c.Buf32=Array,c.assign(c,f))},c.setTyped(d)},{}],28:[function(a,b,c){"use strict";function d(a,b){if(65537>b&&(a.subarray&&g||!a.subarray&&f))return String.fromCharCode.apply(null,e.shrinkBuf(a,b));for(var c="",d=0;b>d;d++)c+=String.fromCharCode(a[d]);return c}var e=a("./common"),f=!0,g=!0;try{String.fromCharCode.apply(null,[0])}catch(h){f=!1}try{String.fromCharCode.apply(null,new Uint8Array(1))}catch(h){g=!1}for(var i=new e.Buf8(256),j=0;256>j;j++)i[j]=j>=252?6:j>=248?5:j>=240?4:j>=224?3:j>=192?2:1;i[254]=i[254]=1,c.string2buf=function(a){var b,c,d,f,g,h=a.length,i=0;for(f=0;h>f;f++)c=a.charCodeAt(f),55296===(64512&c)&&h>f+1&&(d=a.charCodeAt(f+1),56320===(64512&d)&&(c=65536+(c-55296<<10)+(d-56320),f++)),i+=128>c?1:2048>c?2:65536>c?3:4;for(b=new e.Buf8(i),g=0,f=0;i>g;f++)c=a.charCodeAt(f),55296===(64512&c)&&h>f+1&&(d=a.charCodeAt(f+1),56320===(64512&d)&&(c=65536+(c-55296<<10)+(d-56320),f++)),128>c?b[g++]=c:2048>c?(b[g++]=192|c>>>6,b[g++]=128|63&c):65536>c?(b[g++]=224|c>>>12,b[g++]=128|c>>>6&63,b[g++]=128|63&c):(b[g++]=240|c>>>18,b[g++]=128|c>>>12&63,b[g++]=128|c>>>6&63,b[g++]=128|63&c);return b},c.buf2binstring=function(a){return d(a,a.length)},c.binstring2buf=function(a){for(var b=new e.Buf8(a.length),c=0,d=b.length;d>c;c++)b[c]=a.charCodeAt(c);return b},c.buf2string=function(a,b){var c,e,f,g,h=b||a.length,j=new Array(2*h);for(e=0,c=0;h>c;)if(f=a[c++],128>f)j[e++]=f;else if(g=i[f],g>4)j[e++]=65533,c+=g-1;else{for(f&=2===g?31:3===g?15:7;g>1&&h>c;)f=f<<6|63&a[c++],g--;g>1?j[e++]=65533:65536>f?j[e++]=f:(f-=65536,j[e++]=55296|f>>10&1023,j[e++]=56320|1023&f)}return d(j,e)},c.utf8border=function(a,b){var c;for(b=b||a.length,b>a.length&&(b=a.length),c=b-1;c>=0&&128===(192&a[c]);)c--;return 0>c?b:0===c?b:c+i[a[c]]>b?c:b}},{"./common":27}],29:[function(a,b){"use strict";function c(a,b,c,d){for(var e=65535&a|0,f=a>>>16&65535|0,g=0;0!==c;){g=c>2e3?2e3:c,c-=g;do e=e+b[d++]|0,f=f+e|0;while(--g);e%=65521,f%=65521}return e|f<<16|0}b.exports=c},{}],30:[function(a,b){b.exports={Z_NO_FLUSH:0,Z_PARTIAL_FLUSH:1,Z_SYNC_FLUSH:2,Z_FULL_FLUSH:3,Z_FINISH:4,Z_BLOCK:5,Z_TREES:6,Z_OK:0,Z_STREAM_END:1,Z_NEED_DICT:2,Z_ERRNO:-1,Z_STREAM_ERROR:-2,Z_DATA_ERROR:-3,Z_BUF_ERROR:-5,Z_NO_COMPRESSION:0,Z_BEST_SPEED:1,Z_BEST_COMPRESSION:9,Z_DEFAULT_COMPRESSION:-1,Z_FILTERED:1,Z_HUFFMAN_ONLY:2,Z_RLE:3,Z_FIXED:4,Z_DEFAULT_STRATEGY:0,Z_BINARY:0,Z_TEXT:1,Z_UNKNOWN:2,Z_DEFLATED:8}},{}],31:[function(a,b){"use strict";function c(){for(var a,b=[],c=0;256>c;c++){a=c;for(var d=0;8>d;d++)a=1&a?3988292384^a>>>1:a>>>1;b[c]=a}return b}function d(a,b,c,d){var f=e,g=d+c;a=-1^a;for(var h=d;g>h;h++)a=a>>>8^f[255&(a^b[h])];return-1^a}var e=c();b.exports=d},{}],32:[function(a,b,c){"use strict";function d(a,b){return a.msg=G[b],b}function e(a){return(a<<1)-(a>4?9:0)}function f(a){for(var b=a.length;--b>=0;)a[b]=0}function g(a){var b=a.state,c=b.pending;c>a.avail_out&&(c=a.avail_out),0!==c&&(C.arraySet(a.output,b.pending_buf,b.pending_out,c,a.next_out),a.next_out+=c,b.pending_out+=c,a.total_out+=c,a.avail_out-=c,b.pending-=c,0===b.pending&&(b.pending_out=0))}function h(a,b){D._tr_flush_block(a,a.block_start>=0?a.block_start:-1,a.strstart-a.block_start,b),a.block_start=a.strstart,g(a.strm)}function i(a,b){a.pending_buf[a.pending++]=b}function j(a,b){a.pending_buf[a.pending++]=b>>>8&255,a.pending_buf[a.pending++]=255&b}function k(a,b,c,d){var e=a.avail_in;return e>d&&(e=d),0===e?0:(a.avail_in-=e,C.arraySet(b,a.input,a.next_in,e,c),1===a.state.wrap?a.adler=E(a.adler,b,e,c):2===a.state.wrap&&(a.adler=F(a.adler,b,e,c)),a.next_in+=e,a.total_in+=e,e)}function l(a,b){var c,d,e=a.max_chain_length,f=a.strstart,g=a.prev_length,h=a.nice_match,i=a.strstart>a.w_size-jb?a.strstart-(a.w_size-jb):0,j=a.window,k=a.w_mask,l=a.prev,m=a.strstart+ib,n=j[f+g-1],o=j[f+g];a.prev_length>=a.good_match&&(e>>=2),h>a.lookahead&&(h=a.lookahead);do if(c=b,j[c+g]===o&&j[c+g-1]===n&&j[c]===j[f]&&j[++c]===j[f+1]){f+=2,c++;do;while(j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&j[++f]===j[++c]&&m>f);if(d=ib-(m-f),f=m-ib,d>g){if(a.match_start=b,g=d,d>=h)break;n=j[f+g-1],o=j[f+g]}}while((b=l[b&k])>i&&0!==--e);return g<=a.lookahead?g:a.lookahead}function m(a){var b,c,d,e,f,g=a.w_size;do{if(e=a.window_size-a.lookahead-a.strstart,a.strstart>=g+(g-jb)){C.arraySet(a.window,a.window,g,g,0),a.match_start-=g,a.strstart-=g,a.block_start-=g,c=a.hash_size,b=c;do d=a.head[--b],a.head[b]=d>=g?d-g:0;while(--c);c=g,b=c;do d=a.prev[--b],a.prev[b]=d>=g?d-g:0;while(--c);e+=g}if(0===a.strm.avail_in)break;if(c=k(a.strm,a.window,a.strstart+a.lookahead,e),a.lookahead+=c,a.lookahead+a.insert>=hb)for(f=a.strstart-a.insert,a.ins_h=a.window[f],a.ins_h=(a.ins_h<<a.hash_shift^a.window[f+1])&a.hash_mask;a.insert&&(a.ins_h=(a.ins_h<<a.hash_shift^a.window[f+hb-1])&a.hash_mask,a.prev[f&a.w_mask]=a.head[a.ins_h],a.head[a.ins_h]=f,f++,a.insert--,!(a.lookahead+a.insert<hb)););}while(a.lookahead<jb&&0!==a.strm.avail_in)}function n(a,b){var c=65535;for(c>a.pending_buf_size-5&&(c=a.pending_buf_size-5);;){if(a.lookahead<=1){if(m(a),0===a.lookahead&&b===H)return sb;if(0===a.lookahead)break}a.strstart+=a.lookahead,a.lookahead=0;var d=a.block_start+c;if((0===a.strstart||a.strstart>=d)&&(a.lookahead=a.strstart-d,a.strstart=d,h(a,!1),0===a.strm.avail_out))return sb;if(a.strstart-a.block_start>=a.w_size-jb&&(h(a,!1),0===a.strm.avail_out))return sb}return a.insert=0,b===K?(h(a,!0),0===a.strm.avail_out?ub:vb):a.strstart>a.block_start&&(h(a,!1),0===a.strm.avail_out)?sb:sb}function o(a,b){for(var c,d;;){if(a.lookahead<jb){if(m(a),a.lookahead<jb&&b===H)return sb;if(0===a.lookahead)break}if(c=0,a.lookahead>=hb&&(a.ins_h=(a.ins_h<<a.hash_shift^a.window[a.strstart+hb-1])&a.hash_mask,c=a.prev[a.strstart&a.w_mask]=a.head[a.ins_h],a.head[a.ins_h]=a.strstart),0!==c&&a.strstart-c<=a.w_size-jb&&(a.match_length=l(a,c)),a.match_length>=hb)if(d=D._tr_tally(a,a.strstart-a.match_start,a.match_length-hb),a.lookahead-=a.match_length,a.match_length<=a.max_lazy_match&&a.lookahead>=hb){a.match_length--;do a.strstart++,a.ins_h=(a.ins_h<<a.hash_shift^a.window[a.strstart+hb-1])&a.hash_mask,c=a.prev[a.strstart&a.w_mask]=a.head[a.ins_h],a.head[a.ins_h]=a.strstart;while(0!==--a.match_length);a.strstart++}else a.strstart+=a.match_length,a.match_length=0,a.ins_h=a.window[a.strstart],a.ins_h=(a.ins_h<<a.hash_shift^a.window[a.strstart+1])&a.hash_mask;else d=D._tr_tally(a,0,a.window[a.strstart]),a.lookahead--,a.strstart++;if(d&&(h(a,!1),0===a.strm.avail_out))return sb}return a.insert=a.strstart<hb-1?a.strstart:hb-1,b===K?(h(a,!0),0===a.strm.avail_out?ub:vb):a.last_lit&&(h(a,!1),0===a.strm.avail_out)?sb:tb}function p(a,b){for(var c,d,e;;){if(a.lookahead<jb){if(m(a),a.lookahead<jb&&b===H)return sb;if(0===a.lookahead)break}if(c=0,a.lookahead>=hb&&(a.ins_h=(a.ins_h<<a.hash_shift^a.window[a.strstart+hb-1])&a.hash_mask,c=a.prev[a.strstart&a.w_mask]=a.head[a.ins_h],a.head[a.ins_h]=a.strstart),a.prev_length=a.match_length,a.prev_match=a.match_start,a.match_length=hb-1,0!==c&&a.prev_length<a.max_lazy_match&&a.strstart-c<=a.w_size-jb&&(a.match_length=l(a,c),a.match_length<=5&&(a.strategy===S||a.match_length===hb&&a.strstart-a.match_start>4096)&&(a.match_length=hb-1)),a.prev_length>=hb&&a.match_length<=a.prev_length){e=a.strstart+a.lookahead-hb,d=D._tr_tally(a,a.strstart-1-a.prev_match,a.prev_length-hb),a.lookahead-=a.prev_length-1,a.prev_length-=2;do++a.strstart<=e&&(a.ins_h=(a.ins_h<<a.hash_shift^a.window[a.strstart+hb-1])&a.hash_mask,c=a.prev[a.strstart&a.w_mask]=a.head[a.ins_h],a.head[a.ins_h]=a.strstart);while(0!==--a.prev_length);if(a.match_available=0,a.match_length=hb-1,a.strstart++,d&&(h(a,!1),0===a.strm.avail_out))return sb}else if(a.match_available){if(d=D._tr_tally(a,0,a.window[a.strstart-1]),d&&h(a,!1),a.strstart++,a.lookahead--,0===a.strm.avail_out)return sb}else a.match_available=1,a.strstart++,a.lookahead--}return a.match_available&&(d=D._tr_tally(a,0,a.window[a.strstart-1]),a.match_available=0),a.insert=a.strstart<hb-1?a.strstart:hb-1,b===K?(h(a,!0),0===a.strm.avail_out?ub:vb):a.last_lit&&(h(a,!1),0===a.strm.avail_out)?sb:tb}function q(a,b){for(var c,d,e,f,g=a.window;;){if(a.lookahead<=ib){if(m(a),a.lookahead<=ib&&b===H)return sb;if(0===a.lookahead)break}if(a.match_length=0,a.lookahead>=hb&&a.strstart>0&&(e=a.strstart-1,d=g[e],d===g[++e]&&d===g[++e]&&d===g[++e])){f=a.strstart+ib;do;while(d===g[++e]&&d===g[++e]&&d===g[++e]&&d===g[++e]&&d===g[++e]&&d===g[++e]&&d===g[++e]&&d===g[++e]&&f>e);a.match_length=ib-(f-e),a.match_length>a.lookahead&&(a.match_length=a.lookahead)}if(a.match_length>=hb?(c=D._tr_tally(a,1,a.match_length-hb),a.lookahead-=a.match_length,a.strstart+=a.match_length,a.match_length=0):(c=D._tr_tally(a,0,a.window[a.strstart]),a.lookahead--,a.strstart++),c&&(h(a,!1),0===a.strm.avail_out))return sb}return a.insert=0,b===K?(h(a,!0),0===a.strm.avail_out?ub:vb):a.last_lit&&(h(a,!1),0===a.strm.avail_out)?sb:tb}function r(a,b){for(var c;;){if(0===a.lookahead&&(m(a),0===a.lookahead)){if(b===H)return sb;break}if(a.match_length=0,c=D._tr_tally(a,0,a.window[a.strstart]),a.lookahead--,a.strstart++,c&&(h(a,!1),0===a.strm.avail_out))return sb}return a.insert=0,b===K?(h(a,!0),0===a.strm.avail_out?ub:vb):a.last_lit&&(h(a,!1),0===a.strm.avail_out)?sb:tb}function s(a){a.window_size=2*a.w_size,f(a.head),a.max_lazy_match=B[a.level].max_lazy,a.good_match=B[a.level].good_length,a.nice_match=B[a.level].nice_length,a.max_chain_length=B[a.level].max_chain,a.strstart=0,a.block_start=0,a.lookahead=0,a.insert=0,a.match_length=a.prev_length=hb-1,a.match_available=0,a.ins_h=0}function t(){this.strm=null,this.status=0,this.pending_buf=null,this.pending_buf_size=0,this.pending_out=0,this.pending=0,this.wrap=0,this.gzhead=null,this.gzindex=0,this.method=Y,this.last_flush=-1,this.w_size=0,this.w_bits=0,this.w_mask=0,this.window=null,this.window_size=0,this.prev=null,this.head=null,this.ins_h=0,this.hash_size=0,this.hash_bits=0,this.hash_mask=0,this.hash_shift=0,this.block_start=0,this.match_length=0,this.prev_match=0,this.match_available=0,this.strstart=0,this.match_start=0,this.lookahead=0,this.prev_length=0,this.max_chain_length=0,this.max_lazy_match=0,this.level=0,this.strategy=0,this.good_match=0,this.nice_match=0,this.dyn_ltree=new C.Buf16(2*fb),this.dyn_dtree=new C.Buf16(2*(2*db+1)),this.bl_tree=new C.Buf16(2*(2*eb+1)),f(this.dyn_ltree),f(this.dyn_dtree),f(this.bl_tree),this.l_desc=null,this.d_desc=null,this.bl_desc=null,this.bl_count=new C.Buf16(gb+1),this.heap=new C.Buf16(2*cb+1),f(this.heap),this.heap_len=0,this.heap_max=0,this.depth=new C.Buf16(2*cb+1),f(this.depth),this.l_buf=0,this.lit_bufsize=0,this.last_lit=0,this.d_buf=0,this.opt_len=0,this.static_len=0,this.matches=0,this.insert=0,this.bi_buf=0,this.bi_valid=0}function u(a){var b;return a&&a.state?(a.total_in=a.total_out=0,a.data_type=X,b=a.state,b.pending=0,b.pending_out=0,b.wrap<0&&(b.wrap=-b.wrap),b.status=b.wrap?lb:qb,a.adler=2===b.wrap?0:1,b.last_flush=H,D._tr_init(b),M):d(a,O)}function v(a){var b=u(a);return b===M&&s(a.state),b}function w(a,b){return a&&a.state?2!==a.state.wrap?O:(a.state.gzhead=b,M):O}function x(a,b,c,e,f,g){if(!a)return O;var h=1;if(b===R&&(b=6),0>e?(h=0,e=-e):e>15&&(h=2,e-=16),1>f||f>Z||c!==Y||8>e||e>15||0>b||b>9||0>g||g>V)return d(a,O);8===e&&(e=9);var i=new t;return a.state=i,i.strm=a,i.wrap=h,i.gzhead=null,i.w_bits=e,i.w_size=1<<i.w_bits,i.w_mask=i.w_size-1,i.hash_bits=f+7,i.hash_size=1<<i.hash_bits,i.hash_mask=i.hash_size-1,i.hash_shift=~~((i.hash_bits+hb-1)/hb),i.window=new C.Buf8(2*i.w_size),i.head=new C.Buf16(i.hash_size),i.prev=new C.Buf16(i.w_size),i.lit_bufsize=1<<f+6,i.pending_buf_size=4*i.lit_bufsize,i.pending_buf=new C.Buf8(i.pending_buf_size),i.d_buf=i.lit_bufsize>>1,i.l_buf=3*i.lit_bufsize,i.level=b,i.strategy=g,i.method=c,v(a)}function y(a,b){return x(a,b,Y,$,_,W)}function z(a,b){var c,h,k,l;if(!a||!a.state||b>L||0>b)return a?d(a,O):O;if(h=a.state,!a.output||!a.input&&0!==a.avail_in||h.status===rb&&b!==K)return d(a,0===a.avail_out?Q:O);if(h.strm=a,c=h.last_flush,h.last_flush=b,h.status===lb)if(2===h.wrap)a.adler=0,i(h,31),i(h,139),i(h,8),h.gzhead?(i(h,(h.gzhead.text?1:0)+(h.gzhead.hcrc?2:0)+(h.gzhead.extra?4:0)+(h.gzhead.name?8:0)+(h.gzhead.comment?16:0)),i(h,255&h.gzhead.time),i(h,h.gzhead.time>>8&255),i(h,h.gzhead.time>>16&255),i(h,h.gzhead.time>>24&255),i(h,9===h.level?2:h.strategy>=T||h.level<2?4:0),i(h,255&h.gzhead.os),h.gzhead.extra&&h.gzhead.extra.length&&(i(h,255&h.gzhead.extra.length),i(h,h.gzhead.extra.length>>8&255)),h.gzhead.hcrc&&(a.adler=F(a.adler,h.pending_buf,h.pending,0)),h.gzindex=0,h.status=mb):(i(h,0),i(h,0),i(h,0),i(h,0),i(h,0),i(h,9===h.level?2:h.strategy>=T||h.level<2?4:0),i(h,wb),h.status=qb);else{var m=Y+(h.w_bits-8<<4)<<8,n=-1;n=h.strategy>=T||h.level<2?0:h.level<6?1:6===h.level?2:3,m|=n<<6,0!==h.strstart&&(m|=kb),m+=31-m%31,h.status=qb,j(h,m),0!==h.strstart&&(j(h,a.adler>>>16),j(h,65535&a.adler)),a.adler=1}if(h.status===mb)if(h.gzhead.extra){for(k=h.pending;h.gzindex<(65535&h.gzhead.extra.length)&&(h.pending!==h.pending_buf_size||(h.gzhead.hcrc&&h.pending>k&&(a.adler=F(a.adler,h.pending_buf,h.pending-k,k)),g(a),k=h.pending,h.pending!==h.pending_buf_size));)i(h,255&h.gzhead.extra[h.gzindex]),h.gzindex++;h.gzhead.hcrc&&h.pending>k&&(a.adler=F(a.adler,h.pending_buf,h.pending-k,k)),h.gzindex===h.gzhead.extra.length&&(h.gzindex=0,h.status=nb)}else h.status=nb;if(h.status===nb)if(h.gzhead.name){k=h.pending;do{if(h.pending===h.pending_buf_size&&(h.gzhead.hcrc&&h.pending>k&&(a.adler=F(a.adler,h.pending_buf,h.pending-k,k)),g(a),k=h.pending,h.pending===h.pending_buf_size)){l=1;break}l=h.gzindex<h.gzhead.name.length?255&h.gzhead.name.charCodeAt(h.gzindex++):0,i(h,l)}while(0!==l);h.gzhead.hcrc&&h.pending>k&&(a.adler=F(a.adler,h.pending_buf,h.pending-k,k)),0===l&&(h.gzindex=0,h.status=ob)}else h.status=ob;if(h.status===ob)if(h.gzhead.comment){k=h.pending;do{if(h.pending===h.pending_buf_size&&(h.gzhead.hcrc&&h.pending>k&&(a.adler=F(a.adler,h.pending_buf,h.pending-k,k)),g(a),k=h.pending,h.pending===h.pending_buf_size)){l=1;break}l=h.gzindex<h.gzhead.comment.length?255&h.gzhead.comment.charCodeAt(h.gzindex++):0,i(h,l)}while(0!==l);h.gzhead.hcrc&&h.pending>k&&(a.adler=F(a.adler,h.pending_buf,h.pending-k,k)),0===l&&(h.status=pb)}else h.status=pb;if(h.status===pb&&(h.gzhead.hcrc?(h.pending+2>h.pending_buf_size&&g(a),h.pending+2<=h.pending_buf_size&&(i(h,255&a.adler),i(h,a.adler>>8&255),a.adler=0,h.status=qb)):h.status=qb),0!==h.pending){if(g(a),0===a.avail_out)return h.last_flush=-1,M}else if(0===a.avail_in&&e(b)<=e(c)&&b!==K)return d(a,Q);if(h.status===rb&&0!==a.avail_in)return d(a,Q);if(0!==a.avail_in||0!==h.lookahead||b!==H&&h.status!==rb){var o=h.strategy===T?r(h,b):h.strategy===U?q(h,b):B[h.level].func(h,b);if((o===ub||o===vb)&&(h.status=rb),o===sb||o===ub)return 0===a.avail_out&&(h.last_flush=-1),M;if(o===tb&&(b===I?D._tr_align(h):b!==L&&(D._tr_stored_block(h,0,0,!1),b===J&&(f(h.head),0===h.lookahead&&(h.strstart=0,h.block_start=0,h.insert=0))),g(a),0===a.avail_out))return h.last_flush=-1,M}return b!==K?M:h.wrap<=0?N:(2===h.wrap?(i(h,255&a.adler),i(h,a.adler>>8&255),i(h,a.adler>>16&255),i(h,a.adler>>24&255),i(h,255&a.total_in),i(h,a.total_in>>8&255),i(h,a.total_in>>16&255),i(h,a.total_in>>24&255)):(j(h,a.adler>>>16),j(h,65535&a.adler)),g(a),h.wrap>0&&(h.wrap=-h.wrap),0!==h.pending?M:N)}function A(a){var b;return a&&a.state?(b=a.state.status,b!==lb&&b!==mb&&b!==nb&&b!==ob&&b!==pb&&b!==qb&&b!==rb?d(a,O):(a.state=null,b===qb?d(a,P):M)):O}var B,C=a("../utils/common"),D=a("./trees"),E=a("./adler32"),F=a("./crc32"),G=a("./messages"),H=0,I=1,J=3,K=4,L=5,M=0,N=1,O=-2,P=-3,Q=-5,R=-1,S=1,T=2,U=3,V=4,W=0,X=2,Y=8,Z=9,$=15,_=8,ab=29,bb=256,cb=bb+1+ab,db=30,eb=19,fb=2*cb+1,gb=15,hb=3,ib=258,jb=ib+hb+1,kb=32,lb=42,mb=69,nb=73,ob=91,pb=103,qb=113,rb=666,sb=1,tb=2,ub=3,vb=4,wb=3,xb=function(a,b,c,d,e){this.good_length=a,this.max_lazy=b,this.nice_length=c,this.max_chain=d,this.func=e};B=[new xb(0,0,0,0,n),new xb(4,4,8,4,o),new xb(4,5,16,8,o),new xb(4,6,32,32,o),new xb(4,4,16,16,p),new xb(8,16,32,32,p),new xb(8,16,128,128,p),new xb(8,32,128,256,p),new xb(32,128,258,1024,p),new xb(32,258,258,4096,p)],c.deflateInit=y,c.deflateInit2=x,c.deflateReset=v,c.deflateResetKeep=u,c.deflateSetHeader=w,c.deflate=z,c.deflateEnd=A,c.deflateInfo="pako deflate (from Nodeca project)"},{"../utils/common":27,"./adler32":29,"./crc32":31,"./messages":37,"./trees":38}],33:[function(a,b){"use strict";function c(){this.text=0,this.time=0,this.xflags=0,this.os=0,this.extra=null,this.extra_len=0,this.name="",this.comment="",this.hcrc=0,this.done=!1}b.exports=c},{}],34:[function(a,b){"use strict";var c=30,d=12;b.exports=function(a,b){var e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C;e=a.state,f=a.next_in,B=a.input,g=f+(a.avail_in-5),h=a.next_out,C=a.output,i=h-(b-a.avail_out),j=h+(a.avail_out-257),k=e.dmax,l=e.wsize,m=e.whave,n=e.wnext,o=e.window,p=e.hold,q=e.bits,r=e.lencode,s=e.distcode,t=(1<<e.lenbits)-1,u=(1<<e.distbits)-1;a:do{15>q&&(p+=B[f++]<<q,q+=8,p+=B[f++]<<q,q+=8),v=r[p&t];b:for(;;){if(w=v>>>24,p>>>=w,q-=w,w=v>>>16&255,0===w)C[h++]=65535&v;else{if(!(16&w)){if(0===(64&w)){v=r[(65535&v)+(p&(1<<w)-1)];continue b}if(32&w){e.mode=d;break a}a.msg="invalid literal/length code",e.mode=c;break a}x=65535&v,w&=15,w&&(w>q&&(p+=B[f++]<<q,q+=8),x+=p&(1<<w)-1,p>>>=w,q-=w),15>q&&(p+=B[f++]<<q,q+=8,p+=B[f++]<<q,q+=8),v=s[p&u];c:for(;;){if(w=v>>>24,p>>>=w,q-=w,w=v>>>16&255,!(16&w)){if(0===(64&w)){v=s[(65535&v)+(p&(1<<w)-1)];continue c}a.msg="invalid distance code",e.mode=c;break a}if(y=65535&v,w&=15,w>q&&(p+=B[f++]<<q,q+=8,w>q&&(p+=B[f++]<<q,q+=8)),y+=p&(1<<w)-1,y>k){a.msg="invalid distance too far back",e.mode=c;break a}if(p>>>=w,q-=w,w=h-i,y>w){if(w=y-w,w>m&&e.sane){a.msg="invalid distance too far back",e.mode=c;break a}if(z=0,A=o,0===n){if(z+=l-w,x>w){x-=w;do C[h++]=o[z++];while(--w);z=h-y,A=C}}else if(w>n){if(z+=l+n-w,w-=n,x>w){x-=w;do C[h++]=o[z++];while(--w);if(z=0,x>n){w=n,x-=w;do C[h++]=o[z++];while(--w);z=h-y,A=C}}}else if(z+=n-w,x>w){x-=w;do C[h++]=o[z++];while(--w);z=h-y,A=C}for(;x>2;)C[h++]=A[z++],C[h++]=A[z++],C[h++]=A[z++],x-=3;x&&(C[h++]=A[z++],x>1&&(C[h++]=A[z++]))}else{z=h-y;do C[h++]=C[z++],C[h++]=C[z++],C[h++]=C[z++],x-=3;while(x>2);x&&(C[h++]=C[z++],x>1&&(C[h++]=C[z++]))}break}}break}}while(g>f&&j>h);x=q>>3,f-=x,q-=x<<3,p&=(1<<q)-1,a.next_in=f,a.next_out=h,a.avail_in=g>f?5+(g-f):5-(f-g),a.avail_out=j>h?257+(j-h):257-(h-j),e.hold=p,e.bits=q}},{}],35:[function(a,b,c){"use strict";function d(a){return(a>>>24&255)+(a>>>8&65280)+((65280&a)<<8)+((255&a)<<24)}function e(){this.mode=0,this.last=!1,this.wrap=0,this.havedict=!1,this.flags=0,this.dmax=0,this.check=0,this.total=0,this.head=null,this.wbits=0,this.wsize=0,this.whave=0,this.wnext=0,this.window=null,this.hold=0,this.bits=0,this.length=0,this.offset=0,this.extra=0,this.lencode=null,this.distcode=null,this.lenbits=0,this.distbits=0,this.ncode=0,this.nlen=0,this.ndist=0,this.have=0,this.next=null,this.lens=new r.Buf16(320),this.work=new r.Buf16(288),this.lendyn=null,this.distdyn=null,this.sane=0,this.back=0,this.was=0}function f(a){var b;return a&&a.state?(b=a.state,a.total_in=a.total_out=b.total=0,a.msg="",b.wrap&&(a.adler=1&b.wrap),b.mode=K,b.last=0,b.havedict=0,b.dmax=32768,b.head=null,b.hold=0,b.bits=0,b.lencode=b.lendyn=new r.Buf32(ob),b.distcode=b.distdyn=new r.Buf32(pb),b.sane=1,b.back=-1,C):F}function g(a){var b;return a&&a.state?(b=a.state,b.wsize=0,b.whave=0,b.wnext=0,f(a)):F}function h(a,b){var c,d;return a&&a.state?(d=a.state,0>b?(c=0,b=-b):(c=(b>>4)+1,48>b&&(b&=15)),b&&(8>b||b>15)?F:(null!==d.window&&d.wbits!==b&&(d.window=null),d.wrap=c,d.wbits=b,g(a))):F}function i(a,b){var c,d;return a?(d=new e,a.state=d,d.window=null,c=h(a,b),c!==C&&(a.state=null),c):F}function j(a){return i(a,rb)}function k(a){if(sb){var b;for(p=new r.Buf32(512),q=new r.Buf32(32),b=0;144>b;)a.lens[b++]=8;for(;256>b;)a.lens[b++]=9;for(;280>b;)a.lens[b++]=7;for(;288>b;)a.lens[b++]=8;for(v(x,a.lens,0,288,p,0,a.work,{bits:9}),b=0;32>b;)a.lens[b++]=5;v(y,a.lens,0,32,q,0,a.work,{bits:5}),sb=!1}a.lencode=p,a.lenbits=9,a.distcode=q,a.distbits=5}function l(a,b,c,d){var e,f=a.state;return null===f.window&&(f.wsize=1<<f.wbits,f.wnext=0,f.whave=0,f.window=new r.Buf8(f.wsize)),d>=f.wsize?(r.arraySet(f.window,b,c-f.wsize,f.wsize,0),f.wnext=0,f.whave=f.wsize):(e=f.wsize-f.wnext,e>d&&(e=d),r.arraySet(f.window,b,c-d,e,f.wnext),d-=e,d?(r.arraySet(f.window,b,c-d,d,0),f.wnext=d,f.whave=f.wsize):(f.wnext+=e,f.wnext===f.wsize&&(f.wnext=0),f.whave<f.wsize&&(f.whave+=e))),0}function m(a,b){var c,e,f,g,h,i,j,m,n,o,p,q,ob,pb,qb,rb,sb,tb,ub,vb,wb,xb,yb,zb,Ab=0,Bb=new r.Buf8(4),Cb=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15];if(!a||!a.state||!a.output||!a.input&&0!==a.avail_in)return F;c=a.state,c.mode===V&&(c.mode=W),h=a.next_out,f=a.output,j=a.avail_out,g=a.next_in,e=a.input,i=a.avail_in,m=c.hold,n=c.bits,o=i,p=j,xb=C;a:for(;;)switch(c.mode){case K:if(0===c.wrap){c.mode=W;break}for(;16>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(2&c.wrap&&35615===m){c.check=0,Bb[0]=255&m,Bb[1]=m>>>8&255,c.check=t(c.check,Bb,2,0),m=0,n=0,c.mode=L;break}if(c.flags=0,c.head&&(c.head.done=!1),!(1&c.wrap)||(((255&m)<<8)+(m>>8))%31){a.msg="incorrect header check",c.mode=lb;break}if((15&m)!==J){a.msg="unknown compression method",c.mode=lb;break}if(m>>>=4,n-=4,wb=(15&m)+8,0===c.wbits)c.wbits=wb;else if(wb>c.wbits){a.msg="invalid window size",c.mode=lb;break}c.dmax=1<<wb,a.adler=c.check=1,c.mode=512&m?T:V,m=0,n=0;break;case L:for(;16>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(c.flags=m,(255&c.flags)!==J){a.msg="unknown compression method",c.mode=lb;break}if(57344&c.flags){a.msg="unknown header flags set",c.mode=lb;break}c.head&&(c.head.text=m>>8&1),512&c.flags&&(Bb[0]=255&m,Bb[1]=m>>>8&255,c.check=t(c.check,Bb,2,0)),m=0,n=0,c.mode=M;case M:for(;32>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}c.head&&(c.head.time=m),512&c.flags&&(Bb[0]=255&m,Bb[1]=m>>>8&255,Bb[2]=m>>>16&255,Bb[3]=m>>>24&255,c.check=t(c.check,Bb,4,0)),m=0,n=0,c.mode=N;case N:for(;16>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}c.head&&(c.head.xflags=255&m,c.head.os=m>>8),512&c.flags&&(Bb[0]=255&m,Bb[1]=m>>>8&255,c.check=t(c.check,Bb,2,0)),m=0,n=0,c.mode=O;case O:if(1024&c.flags){for(;16>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}c.length=m,c.head&&(c.head.extra_len=m),512&c.flags&&(Bb[0]=255&m,Bb[1]=m>>>8&255,c.check=t(c.check,Bb,2,0)),m=0,n=0}else c.head&&(c.head.extra=null);c.mode=P;case P:if(1024&c.flags&&(q=c.length,q>i&&(q=i),q&&(c.head&&(wb=c.head.extra_len-c.length,c.head.extra||(c.head.extra=new Array(c.head.extra_len)),r.arraySet(c.head.extra,e,g,q,wb)),512&c.flags&&(c.check=t(c.check,e,q,g)),i-=q,g+=q,c.length-=q),c.length))break a;c.length=0,c.mode=Q;case Q:if(2048&c.flags){if(0===i)break a;q=0;do wb=e[g+q++],c.head&&wb&&c.length<65536&&(c.head.name+=String.fromCharCode(wb));while(wb&&i>q);if(512&c.flags&&(c.check=t(c.check,e,q,g)),i-=q,g+=q,wb)break a}else c.head&&(c.head.name=null);c.length=0,c.mode=R;case R:if(4096&c.flags){if(0===i)break a;q=0;do wb=e[g+q++],c.head&&wb&&c.length<65536&&(c.head.comment+=String.fromCharCode(wb));while(wb&&i>q);if(512&c.flags&&(c.check=t(c.check,e,q,g)),i-=q,g+=q,wb)break a}else c.head&&(c.head.comment=null);c.mode=S;case S:if(512&c.flags){for(;16>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(m!==(65535&c.check)){a.msg="header crc mismatch",c.mode=lb;break}m=0,n=0}c.head&&(c.head.hcrc=c.flags>>9&1,c.head.done=!0),a.adler=c.check=0,c.mode=V;break;case T:for(;32>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}a.adler=c.check=d(m),m=0,n=0,c.mode=U;case U:if(0===c.havedict)return a.next_out=h,a.avail_out=j,a.next_in=g,a.avail_in=i,c.hold=m,c.bits=n,E;a.adler=c.check=1,c.mode=V;case V:if(b===A||b===B)break a;case W:if(c.last){m>>>=7&n,n-=7&n,c.mode=ib;break}for(;3>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}switch(c.last=1&m,m>>>=1,n-=1,3&m){case 0:c.mode=X;break;case 1:if(k(c),c.mode=bb,b===B){m>>>=2,n-=2;break a}break;case 2:c.mode=$;break;case 3:a.msg="invalid block type",c.mode=lb}m>>>=2,n-=2;break;case X:for(m>>>=7&n,n-=7&n;32>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if((65535&m)!==(m>>>16^65535)){a.msg="invalid stored block lengths",c.mode=lb;break}if(c.length=65535&m,m=0,n=0,c.mode=Y,b===B)break a;case Y:c.mode=Z;case Z:if(q=c.length){if(q>i&&(q=i),q>j&&(q=j),0===q)break a;r.arraySet(f,e,g,q,h),i-=q,g+=q,j-=q,h+=q,c.length-=q;break}c.mode=V;break;case $:for(;14>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(c.nlen=(31&m)+257,m>>>=5,n-=5,c.ndist=(31&m)+1,m>>>=5,n-=5,c.ncode=(15&m)+4,m>>>=4,n-=4,c.nlen>286||c.ndist>30){a.msg="too many length or distance symbols",c.mode=lb;break}c.have=0,c.mode=_;case _:for(;c.have<c.ncode;){for(;3>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}c.lens[Cb[c.have++]]=7&m,m>>>=3,n-=3}for(;c.have<19;)c.lens[Cb[c.have++]]=0;if(c.lencode=c.lendyn,c.lenbits=7,yb={bits:c.lenbits},xb=v(w,c.lens,0,19,c.lencode,0,c.work,yb),c.lenbits=yb.bits,xb){a.msg="invalid code lengths set",c.mode=lb;break}c.have=0,c.mode=ab;case ab:for(;c.have<c.nlen+c.ndist;){for(;Ab=c.lencode[m&(1<<c.lenbits)-1],qb=Ab>>>24,rb=Ab>>>16&255,sb=65535&Ab,!(n>=qb);){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(16>sb)m>>>=qb,n-=qb,c.lens[c.have++]=sb;else{if(16===sb){for(zb=qb+2;zb>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(m>>>=qb,n-=qb,0===c.have){a.msg="invalid bit length repeat",c.mode=lb;break}wb=c.lens[c.have-1],q=3+(3&m),m>>>=2,n-=2}else if(17===sb){for(zb=qb+3;zb>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}m>>>=qb,n-=qb,wb=0,q=3+(7&m),m>>>=3,n-=3}else{for(zb=qb+7;zb>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}m>>>=qb,n-=qb,wb=0,q=11+(127&m),m>>>=7,n-=7}if(c.have+q>c.nlen+c.ndist){a.msg="invalid bit length repeat",c.mode=lb;break}for(;q--;)c.lens[c.have++]=wb}}if(c.mode===lb)break;if(0===c.lens[256]){a.msg="invalid code -- missing end-of-block",c.mode=lb;break}if(c.lenbits=9,yb={bits:c.lenbits},xb=v(x,c.lens,0,c.nlen,c.lencode,0,c.work,yb),c.lenbits=yb.bits,xb){a.msg="invalid literal/lengths set",c.mode=lb;break}if(c.distbits=6,c.distcode=c.distdyn,yb={bits:c.distbits},xb=v(y,c.lens,c.nlen,c.ndist,c.distcode,0,c.work,yb),c.distbits=yb.bits,xb){a.msg="invalid distances set",c.mode=lb;break}if(c.mode=bb,b===B)break a;case bb:c.mode=cb;case cb:if(i>=6&&j>=258){a.next_out=h,a.avail_out=j,a.next_in=g,a.avail_in=i,c.hold=m,c.bits=n,u(a,p),h=a.next_out,f=a.output,j=a.avail_out,g=a.next_in,e=a.input,i=a.avail_in,m=c.hold,n=c.bits,c.mode===V&&(c.back=-1);break}for(c.back=0;Ab=c.lencode[m&(1<<c.lenbits)-1],qb=Ab>>>24,rb=Ab>>>16&255,sb=65535&Ab,!(n>=qb);){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(rb&&0===(240&rb)){for(tb=qb,ub=rb,vb=sb;Ab=c.lencode[vb+((m&(1<<tb+ub)-1)>>tb)],qb=Ab>>>24,rb=Ab>>>16&255,sb=65535&Ab,!(n>=tb+qb);){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}m>>>=tb,n-=tb,c.back+=tb}if(m>>>=qb,n-=qb,c.back+=qb,c.length=sb,0===rb){c.mode=hb;break}if(32&rb){c.back=-1,c.mode=V;break}if(64&rb){a.msg="invalid literal/length code",c.mode=lb;break}c.extra=15&rb,c.mode=db;case db:if(c.extra){for(zb=c.extra;zb>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}c.length+=m&(1<<c.extra)-1,m>>>=c.extra,n-=c.extra,c.back+=c.extra}c.was=c.length,c.mode=eb;case eb:for(;Ab=c.distcode[m&(1<<c.distbits)-1],qb=Ab>>>24,rb=Ab>>>16&255,sb=65535&Ab,!(n>=qb);){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(0===(240&rb)){for(tb=qb,ub=rb,vb=sb;Ab=c.distcode[vb+((m&(1<<tb+ub)-1)>>tb)],qb=Ab>>>24,rb=Ab>>>16&255,sb=65535&Ab,!(n>=tb+qb);){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}m>>>=tb,n-=tb,c.back+=tb}if(m>>>=qb,n-=qb,c.back+=qb,64&rb){a.msg="invalid distance code",c.mode=lb;break}c.offset=sb,c.extra=15&rb,c.mode=fb;case fb:if(c.extra){for(zb=c.extra;zb>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}c.offset+=m&(1<<c.extra)-1,m>>>=c.extra,n-=c.extra,c.back+=c.extra}if(c.offset>c.dmax){a.msg="invalid distance too far back",c.mode=lb;break}c.mode=gb;case gb:if(0===j)break a;
+if(q=p-j,c.offset>q){if(q=c.offset-q,q>c.whave&&c.sane){a.msg="invalid distance too far back",c.mode=lb;break}q>c.wnext?(q-=c.wnext,ob=c.wsize-q):ob=c.wnext-q,q>c.length&&(q=c.length),pb=c.window}else pb=f,ob=h-c.offset,q=c.length;q>j&&(q=j),j-=q,c.length-=q;do f[h++]=pb[ob++];while(--q);0===c.length&&(c.mode=cb);break;case hb:if(0===j)break a;f[h++]=c.length,j--,c.mode=cb;break;case ib:if(c.wrap){for(;32>n;){if(0===i)break a;i--,m|=e[g++]<<n,n+=8}if(p-=j,a.total_out+=p,c.total+=p,p&&(a.adler=c.check=c.flags?t(c.check,f,p,h-p):s(c.check,f,p,h-p)),p=j,(c.flags?m:d(m))!==c.check){a.msg="incorrect data check",c.mode=lb;break}m=0,n=0}c.mode=jb;case jb:if(c.wrap&&c.flags){for(;32>n;){if(0===i)break a;i--,m+=e[g++]<<n,n+=8}if(m!==(4294967295&c.total)){a.msg="incorrect length check",c.mode=lb;break}m=0,n=0}c.mode=kb;case kb:xb=D;break a;case lb:xb=G;break a;case mb:return H;case nb:default:return F}return a.next_out=h,a.avail_out=j,a.next_in=g,a.avail_in=i,c.hold=m,c.bits=n,(c.wsize||p!==a.avail_out&&c.mode<lb&&(c.mode<ib||b!==z))&&l(a,a.output,a.next_out,p-a.avail_out)?(c.mode=mb,H):(o-=a.avail_in,p-=a.avail_out,a.total_in+=o,a.total_out+=p,c.total+=p,c.wrap&&p&&(a.adler=c.check=c.flags?t(c.check,f,p,a.next_out-p):s(c.check,f,p,a.next_out-p)),a.data_type=c.bits+(c.last?64:0)+(c.mode===V?128:0)+(c.mode===bb||c.mode===Y?256:0),(0===o&&0===p||b===z)&&xb===C&&(xb=I),xb)}function n(a){if(!a||!a.state)return F;var b=a.state;return b.window&&(b.window=null),a.state=null,C}function o(a,b){var c;return a&&a.state?(c=a.state,0===(2&c.wrap)?F:(c.head=b,b.done=!1,C)):F}var p,q,r=a("../utils/common"),s=a("./adler32"),t=a("./crc32"),u=a("./inffast"),v=a("./inftrees"),w=0,x=1,y=2,z=4,A=5,B=6,C=0,D=1,E=2,F=-2,G=-3,H=-4,I=-5,J=8,K=1,L=2,M=3,N=4,O=5,P=6,Q=7,R=8,S=9,T=10,U=11,V=12,W=13,X=14,Y=15,Z=16,$=17,_=18,ab=19,bb=20,cb=21,db=22,eb=23,fb=24,gb=25,hb=26,ib=27,jb=28,kb=29,lb=30,mb=31,nb=32,ob=852,pb=592,qb=15,rb=qb,sb=!0;c.inflateReset=g,c.inflateReset2=h,c.inflateResetKeep=f,c.inflateInit=j,c.inflateInit2=i,c.inflate=m,c.inflateEnd=n,c.inflateGetHeader=o,c.inflateInfo="pako inflate (from Nodeca project)"},{"../utils/common":27,"./adler32":29,"./crc32":31,"./inffast":34,"./inftrees":36}],36:[function(a,b){"use strict";var c=a("../utils/common"),d=15,e=852,f=592,g=0,h=1,i=2,j=[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,0,0],k=[16,16,16,16,16,16,16,16,17,17,17,17,18,18,18,18,19,19,19,19,20,20,20,20,21,21,21,21,16,72,78],l=[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0],m=[16,16,16,16,17,17,18,18,19,19,20,20,21,21,22,22,23,23,24,24,25,25,26,26,27,27,28,28,29,29,64,64];b.exports=function(a,b,n,o,p,q,r,s){var t,u,v,w,x,y,z,A,B,C=s.bits,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=null,O=0,P=new c.Buf16(d+1),Q=new c.Buf16(d+1),R=null,S=0;for(D=0;d>=D;D++)P[D]=0;for(E=0;o>E;E++)P[b[n+E]]++;for(H=C,G=d;G>=1&&0===P[G];G--);if(H>G&&(H=G),0===G)return p[q++]=20971520,p[q++]=20971520,s.bits=1,0;for(F=1;G>F&&0===P[F];F++);for(F>H&&(H=F),K=1,D=1;d>=D;D++)if(K<<=1,K-=P[D],0>K)return-1;if(K>0&&(a===g||1!==G))return-1;for(Q[1]=0,D=1;d>D;D++)Q[D+1]=Q[D]+P[D];for(E=0;o>E;E++)0!==b[n+E]&&(r[Q[b[n+E]]++]=E);if(a===g?(N=R=r,y=19):a===h?(N=j,O-=257,R=k,S-=257,y=256):(N=l,R=m,y=-1),M=0,E=0,D=F,x=q,I=H,J=0,v=-1,L=1<<H,w=L-1,a===h&&L>e||a===i&&L>f)return 1;for(var T=0;;){T++,z=D-J,r[E]<y?(A=0,B=r[E]):r[E]>y?(A=R[S+r[E]],B=N[O+r[E]]):(A=96,B=0),t=1<<D-J,u=1<<I,F=u;do u-=t,p[x+(M>>J)+u]=z<<24|A<<16|B|0;while(0!==u);for(t=1<<D-1;M&t;)t>>=1;if(0!==t?(M&=t-1,M+=t):M=0,E++,0===--P[D]){if(D===G)break;D=b[n+r[E]]}if(D>H&&(M&w)!==v){for(0===J&&(J=H),x+=F,I=D-J,K=1<<I;G>I+J&&(K-=P[I+J],!(0>=K));)I++,K<<=1;if(L+=1<<I,a===h&&L>e||a===i&&L>f)return 1;v=M&w,p[v]=H<<24|I<<16|x-q|0}}return 0!==M&&(p[x+M]=D-J<<24|64<<16|0),s.bits=H,0}},{"../utils/common":27}],37:[function(a,b){"use strict";b.exports={2:"need dictionary",1:"stream end",0:"","-1":"file error","-2":"stream error","-3":"data error","-4":"insufficient memory","-5":"buffer error","-6":"incompatible version"}},{}],38:[function(a,b,c){"use strict";function d(a){for(var b=a.length;--b>=0;)a[b]=0}function e(a){return 256>a?gb[a]:gb[256+(a>>>7)]}function f(a,b){a.pending_buf[a.pending++]=255&b,a.pending_buf[a.pending++]=b>>>8&255}function g(a,b,c){a.bi_valid>V-c?(a.bi_buf|=b<<a.bi_valid&65535,f(a,a.bi_buf),a.bi_buf=b>>V-a.bi_valid,a.bi_valid+=c-V):(a.bi_buf|=b<<a.bi_valid&65535,a.bi_valid+=c)}function h(a,b,c){g(a,c[2*b],c[2*b+1])}function i(a,b){var c=0;do c|=1&a,a>>>=1,c<<=1;while(--b>0);return c>>>1}function j(a){16===a.bi_valid?(f(a,a.bi_buf),a.bi_buf=0,a.bi_valid=0):a.bi_valid>=8&&(a.pending_buf[a.pending++]=255&a.bi_buf,a.bi_buf>>=8,a.bi_valid-=8)}function k(a,b){var c,d,e,f,g,h,i=b.dyn_tree,j=b.max_code,k=b.stat_desc.static_tree,l=b.stat_desc.has_stree,m=b.stat_desc.extra_bits,n=b.stat_desc.extra_base,o=b.stat_desc.max_length,p=0;for(f=0;U>=f;f++)a.bl_count[f]=0;for(i[2*a.heap[a.heap_max]+1]=0,c=a.heap_max+1;T>c;c++)d=a.heap[c],f=i[2*i[2*d+1]+1]+1,f>o&&(f=o,p++),i[2*d+1]=f,d>j||(a.bl_count[f]++,g=0,d>=n&&(g=m[d-n]),h=i[2*d],a.opt_len+=h*(f+g),l&&(a.static_len+=h*(k[2*d+1]+g)));if(0!==p){do{for(f=o-1;0===a.bl_count[f];)f--;a.bl_count[f]--,a.bl_count[f+1]+=2,a.bl_count[o]--,p-=2}while(p>0);for(f=o;0!==f;f--)for(d=a.bl_count[f];0!==d;)e=a.heap[--c],e>j||(i[2*e+1]!==f&&(a.opt_len+=(f-i[2*e+1])*i[2*e],i[2*e+1]=f),d--)}}function l(a,b,c){var d,e,f=new Array(U+1),g=0;for(d=1;U>=d;d++)f[d]=g=g+c[d-1]<<1;for(e=0;b>=e;e++){var h=a[2*e+1];0!==h&&(a[2*e]=i(f[h]++,h))}}function m(){var a,b,c,d,e,f=new Array(U+1);for(c=0,d=0;O-1>d;d++)for(ib[d]=c,a=0;a<1<<_[d];a++)hb[c++]=d;for(hb[c-1]=d,e=0,d=0;16>d;d++)for(jb[d]=e,a=0;a<1<<ab[d];a++)gb[e++]=d;for(e>>=7;R>d;d++)for(jb[d]=e<<7,a=0;a<1<<ab[d]-7;a++)gb[256+e++]=d;for(b=0;U>=b;b++)f[b]=0;for(a=0;143>=a;)eb[2*a+1]=8,a++,f[8]++;for(;255>=a;)eb[2*a+1]=9,a++,f[9]++;for(;279>=a;)eb[2*a+1]=7,a++,f[7]++;for(;287>=a;)eb[2*a+1]=8,a++,f[8]++;for(l(eb,Q+1,f),a=0;R>a;a++)fb[2*a+1]=5,fb[2*a]=i(a,5);kb=new nb(eb,_,P+1,Q,U),lb=new nb(fb,ab,0,R,U),mb=new nb(new Array(0),bb,0,S,W)}function n(a){var b;for(b=0;Q>b;b++)a.dyn_ltree[2*b]=0;for(b=0;R>b;b++)a.dyn_dtree[2*b]=0;for(b=0;S>b;b++)a.bl_tree[2*b]=0;a.dyn_ltree[2*X]=1,a.opt_len=a.static_len=0,a.last_lit=a.matches=0}function o(a){a.bi_valid>8?f(a,a.bi_buf):a.bi_valid>0&&(a.pending_buf[a.pending++]=a.bi_buf),a.bi_buf=0,a.bi_valid=0}function p(a,b,c,d){o(a),d&&(f(a,c),f(a,~c)),E.arraySet(a.pending_buf,a.window,b,c,a.pending),a.pending+=c}function q(a,b,c,d){var e=2*b,f=2*c;return a[e]<a[f]||a[e]===a[f]&&d[b]<=d[c]}function r(a,b,c){for(var d=a.heap[c],e=c<<1;e<=a.heap_len&&(e<a.heap_len&&q(b,a.heap[e+1],a.heap[e],a.depth)&&e++,!q(b,d,a.heap[e],a.depth));)a.heap[c]=a.heap[e],c=e,e<<=1;a.heap[c]=d}function s(a,b,c){var d,f,i,j,k=0;if(0!==a.last_lit)do d=a.pending_buf[a.d_buf+2*k]<<8|a.pending_buf[a.d_buf+2*k+1],f=a.pending_buf[a.l_buf+k],k++,0===d?h(a,f,b):(i=hb[f],h(a,i+P+1,b),j=_[i],0!==j&&(f-=ib[i],g(a,f,j)),d--,i=e(d),h(a,i,c),j=ab[i],0!==j&&(d-=jb[i],g(a,d,j)));while(k<a.last_lit);h(a,X,b)}function t(a,b){var c,d,e,f=b.dyn_tree,g=b.stat_desc.static_tree,h=b.stat_desc.has_stree,i=b.stat_desc.elems,j=-1;for(a.heap_len=0,a.heap_max=T,c=0;i>c;c++)0!==f[2*c]?(a.heap[++a.heap_len]=j=c,a.depth[c]=0):f[2*c+1]=0;for(;a.heap_len<2;)e=a.heap[++a.heap_len]=2>j?++j:0,f[2*e]=1,a.depth[e]=0,a.opt_len--,h&&(a.static_len-=g[2*e+1]);for(b.max_code=j,c=a.heap_len>>1;c>=1;c--)r(a,f,c);e=i;do c=a.heap[1],a.heap[1]=a.heap[a.heap_len--],r(a,f,1),d=a.heap[1],a.heap[--a.heap_max]=c,a.heap[--a.heap_max]=d,f[2*e]=f[2*c]+f[2*d],a.depth[e]=(a.depth[c]>=a.depth[d]?a.depth[c]:a.depth[d])+1,f[2*c+1]=f[2*d+1]=e,a.heap[1]=e++,r(a,f,1);while(a.heap_len>=2);a.heap[--a.heap_max]=a.heap[1],k(a,b),l(f,j,a.bl_count)}function u(a,b,c){var d,e,f=-1,g=b[1],h=0,i=7,j=4;for(0===g&&(i=138,j=3),b[2*(c+1)+1]=65535,d=0;c>=d;d++)e=g,g=b[2*(d+1)+1],++h<i&&e===g||(j>h?a.bl_tree[2*e]+=h:0!==e?(e!==f&&a.bl_tree[2*e]++,a.bl_tree[2*Y]++):10>=h?a.bl_tree[2*Z]++:a.bl_tree[2*$]++,h=0,f=e,0===g?(i=138,j=3):e===g?(i=6,j=3):(i=7,j=4))}function v(a,b,c){var d,e,f=-1,i=b[1],j=0,k=7,l=4;for(0===i&&(k=138,l=3),d=0;c>=d;d++)if(e=i,i=b[2*(d+1)+1],!(++j<k&&e===i)){if(l>j){do h(a,e,a.bl_tree);while(0!==--j)}else 0!==e?(e!==f&&(h(a,e,a.bl_tree),j--),h(a,Y,a.bl_tree),g(a,j-3,2)):10>=j?(h(a,Z,a.bl_tree),g(a,j-3,3)):(h(a,$,a.bl_tree),g(a,j-11,7));j=0,f=e,0===i?(k=138,l=3):e===i?(k=6,l=3):(k=7,l=4)}}function w(a){var b;for(u(a,a.dyn_ltree,a.l_desc.max_code),u(a,a.dyn_dtree,a.d_desc.max_code),t(a,a.bl_desc),b=S-1;b>=3&&0===a.bl_tree[2*cb[b]+1];b--);return a.opt_len+=3*(b+1)+5+5+4,b}function x(a,b,c,d){var e;for(g(a,b-257,5),g(a,c-1,5),g(a,d-4,4),e=0;d>e;e++)g(a,a.bl_tree[2*cb[e]+1],3);v(a,a.dyn_ltree,b-1),v(a,a.dyn_dtree,c-1)}function y(a){var b,c=4093624447;for(b=0;31>=b;b++,c>>>=1)if(1&c&&0!==a.dyn_ltree[2*b])return G;if(0!==a.dyn_ltree[18]||0!==a.dyn_ltree[20]||0!==a.dyn_ltree[26])return H;for(b=32;P>b;b++)if(0!==a.dyn_ltree[2*b])return H;return G}function z(a){pb||(m(),pb=!0),a.l_desc=new ob(a.dyn_ltree,kb),a.d_desc=new ob(a.dyn_dtree,lb),a.bl_desc=new ob(a.bl_tree,mb),a.bi_buf=0,a.bi_valid=0,n(a)}function A(a,b,c,d){g(a,(J<<1)+(d?1:0),3),p(a,b,c,!0)}function B(a){g(a,K<<1,3),h(a,X,eb),j(a)}function C(a,b,c,d){var e,f,h=0;a.level>0?(a.strm.data_type===I&&(a.strm.data_type=y(a)),t(a,a.l_desc),t(a,a.d_desc),h=w(a),e=a.opt_len+3+7>>>3,f=a.static_len+3+7>>>3,e>=f&&(e=f)):e=f=c+5,e>=c+4&&-1!==b?A(a,b,c,d):a.strategy===F||f===e?(g(a,(K<<1)+(d?1:0),3),s(a,eb,fb)):(g(a,(L<<1)+(d?1:0),3),x(a,a.l_desc.max_code+1,a.d_desc.max_code+1,h+1),s(a,a.dyn_ltree,a.dyn_dtree)),n(a),d&&o(a)}function D(a,b,c){return a.pending_buf[a.d_buf+2*a.last_lit]=b>>>8&255,a.pending_buf[a.d_buf+2*a.last_lit+1]=255&b,a.pending_buf[a.l_buf+a.last_lit]=255&c,a.last_lit++,0===b?a.dyn_ltree[2*c]++:(a.matches++,b--,a.dyn_ltree[2*(hb[c]+P+1)]++,a.dyn_dtree[2*e(b)]++),a.last_lit===a.lit_bufsize-1}var E=a("../utils/common"),F=4,G=0,H=1,I=2,J=0,K=1,L=2,M=3,N=258,O=29,P=256,Q=P+1+O,R=30,S=19,T=2*Q+1,U=15,V=16,W=7,X=256,Y=16,Z=17,$=18,_=[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],ab=[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],bb=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7],cb=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],db=512,eb=new Array(2*(Q+2));d(eb);var fb=new Array(2*R);d(fb);var gb=new Array(db);d(gb);var hb=new Array(N-M+1);d(hb);var ib=new Array(O);d(ib);var jb=new Array(R);d(jb);var kb,lb,mb,nb=function(a,b,c,d,e){this.static_tree=a,this.extra_bits=b,this.extra_base=c,this.elems=d,this.max_length=e,this.has_stree=a&&a.length},ob=function(a,b){this.dyn_tree=a,this.max_code=0,this.stat_desc=b},pb=!1;c._tr_init=z,c._tr_stored_block=A,c._tr_flush_block=C,c._tr_tally=D,c._tr_align=B},{"../utils/common":27}],39:[function(a,b){"use strict";function c(){this.input=null,this.next_in=0,this.avail_in=0,this.total_in=0,this.output=null,this.next_out=0,this.avail_out=0,this.total_out=0,this.msg="",this.state=null,this.data_type=2,this.adler=0}b.exports=c},{}]},{},[9])(9)});
+// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
+if (!Object.keys) {
+  Object.keys = (function () {
+    var hasOwnProperty = Object.prototype.hasOwnProperty,
+        hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'),
+        dontEnums = [
+          'toString',
+          'toLocaleString',
+          'valueOf',
+          'hasOwnProperty',
+          'isPrototypeOf',
+          'propertyIsEnumerable',
+          'constructor'
+        ],
+        dontEnumsLength = dontEnums.length;
+
+    return function (obj) {
+      if (typeof obj !== 'object' && typeof obj !== 'function' || obj === null) throw new TypeError('Object.keys called on non-object');
+
+      var result = [];
+
+      for (var prop in obj) {
+        if (hasOwnProperty.call(obj, prop)) result.push(prop);
       }
-      return this.files[name];
-   };
-
-   /**
-    * Generate the data found in the local header of a zip file.
-    * Do not create it now, as some parts are re-used later.
-    * @private
-    * @param {Object} file the file to use.
-    * @param {string} utfEncodedFileName the file name, utf8 encoded.
-    * @param {string} compressionType the compression to use.
-    * @return {Object} an object containing header and compressedData.
-    */
-   var prepareLocalHeaderData = function(file, utfEncodedFileName, compressionType) {
-      var useUTF8 = utfEncodedFileName !== file.name,
-          data    = file.asBinary(),
-          o       = file.options,
-          dosTime,
-          dosDate;
-
-      // date
-      // @see http://www.delorie.com/djgpp/doc/rbinter/it/52/13.html
-      // @see http://www.delorie.com/djgpp/doc/rbinter/it/65/16.html
-      // @see http://www.delorie.com/djgpp/doc/rbinter/it/66/16.html
-
-      dosTime = o.date.getHours();
-      dosTime = dosTime << 6;
-      dosTime = dosTime | o.date.getMinutes();
-      dosTime = dosTime << 5;
-      dosTime = dosTime | o.date.getSeconds() / 2;
-
-      dosDate = o.date.getFullYear() - 1980;
-      dosDate = dosDate << 4;
-      dosDate = dosDate | (o.date.getMonth() + 1);
-      dosDate = dosDate << 5;
-      dosDate = dosDate | o.date.getDate();
-
-      var hasData = data !== null && data.length !== 0;
-
-      var compression    = JSZip.compressions[compressionType];
-      var compressedData = hasData ? compression.compress(data) : '';
-
-      var header = "";
-
-      // version needed to extract
-      header += "\x0A\x00";
-      // general purpose bit flag
-      // set bit 11 if utf8
-      header += useUTF8 ? "\x00\x08" : "\x00\x00";
-      // compression method
-      header += hasData ? compression.magic : JSZip.compressions['STORE'].magic;
-      // last mod file time
-      header += decToHex(dosTime, 2);
-      // last mod file date
-      header += decToHex(dosDate, 2);
-      // crc-32
-      header += hasData ? decToHex(this.crc32(data), 4) : '\x00\x00\x00\x00';
-      // compressed size
-      header += hasData ? decToHex(compressedData.length, 4) : '\x00\x00\x00\x00';
-      // uncompressed size
-      header += hasData ? decToHex(data.length, 4) : '\x00\x00\x00\x00';
-      // file name length
-      header += decToHex(utfEncodedFileName.length, 2);
-      // extra field length
-      header += "\x00\x00";
-
-      return {
-         header:header,
-         compressedData:compressedData
-      };
-   };
-
-
-   // return the actual prototype of JSZip
-   return {
-      /**
-       * Read an existing zip and merge the data in the current JSZip object.
-       * The implementation is in jszip-load.js, don't forget to include it.
-       * @param {String|ArrayBuffer|Uint8Array} stream  The stream to load
-       * @param {Object} options Options for loading the stream.
-       *  options.base64 : is the stream in base64 ? default : false
-       * @return {JSZip} the current JSZip object
-       */
-      load : function (stream, options) {
-         throw new Error("Load method is not defined. Is the file jszip-load.js included ?");
-      },
-
-      /**
-       * Filter nested files/folders with the specified function.
-       * @param {Function} search the predicate to use :
-       * function (relativePath, file) {...}
-       * It takes 2 arguments : the relative path and the file.
-       * @return {Array} An array of matching elements.
-       */
-      filter : function (search) {
-         var result = [], filename, relativePath, file, fileClone;
-         for (filename in this.files) {
-            if ( !this.files.hasOwnProperty(filename) ) { continue; }
-            file = this.files[filename];
-            // return a new object, don't let the user mess with our internal objects :)
-            fileClone = new ZipObject(file.name, file.data, extend(file.options));
-            relativePath = filename.slice(this.root.length, filename.length);
-            if (filename.slice(0, this.root.length) === this.root && // the file is in the current root
-                search(relativePath, fileClone)) { // and the file matches the function
-               result.push(fileClone);
-            }
-         }
-         return result;
-      },
-
-      /**
-       * Add a file to the zip file, or search a file.
-       * @param   {string|RegExp} name The name of the file to add (if data is defined),
-       * the name of the file to find (if no data) or a regex to match files.
-       * @param   {String|ArrayBuffer|Uint8Array} data  The file data, either raw or base64 encoded
-       * @param   {Object} o     File options
-       * @return  {JSZip|Object|Array} this JSZip object (when adding a file),
-       * a file (when searching by string) or an array of files (when searching by regex).
-       */
-      file : function(name, data, o) {
-         if (arguments.length === 1) {
-            if (name instanceof RegExp) {
-               var regexp = name;
-               return this.filter(function(relativePath, file) {
-                  return !file.options.dir && regexp.test(relativePath);
-               });
-            } else { // text
-               return this.filter(function (relativePath, file) {
-                  return !file.options.dir && relativePath === name;
-               })[0]||null;
-            }
-         } else { // more than one argument : we have data !
-            name = this.root+name;
-            fileAdd.call(this, name, data, o);
-         }
-         return this;
-      },
-
-      /**
-       * Add a directory to the zip file, or search.
-       * @param   {String|RegExp} arg The name of the directory to add, or a regex to search folders.
-       * @return  {JSZip} an object with the new directory as the root, or an array containing matching folders.
-       */
-      folder : function(arg) {
-         if (!arg) {
-            return this;
-         }
-
-         if (arg instanceof RegExp) {
-            return this.filter(function(relativePath, file) {
-               return file.options.dir && arg.test(relativePath);
-            });
-         }
-
-         // else, name is a new folder
-         var name = this.root + arg;
-         var newFolder = folderAdd.call(this, name);
-
-         // Allow chaining by returning a new object with this folder as the root
-         var ret = this.clone();
-         ret.root = newFolder.name;
-         return ret;
-      },
-
-      /**
-       * Delete a file, or a directory and all sub-files, from the zip
-       * @param {string} name the name of the file to delete
-       * @return {JSZip} this JSZip object
-       */
-      remove : function(name) {
-         name = this.root + name;
-         var file = this.files[name];
-         if (!file) {
-            // Look for any folders
-            if (name.slice(-1) != "/") {
-               name += "/";
-            }
-            file = this.files[name];
-         }
-
-         if (file) {
-            if (!file.options.dir) {
-               // file
-               delete this.files[name];
-            } else {
-               // folder
-               var kids = this.filter(function (relativePath, file) {
-                  return file.name.slice(0, name.length) === name;
-               });
-               for (var i = 0; i < kids.length; i++) {
-                  delete this.files[kids[i].name];
-               }
-            }
-         }
-
-         return this;
-      },
-
-      /**
-       * Generate the complete zip file
-       * @param {Object} options the options to generate the zip file :
-       * - base64, (deprecated, use type instead) true to generate base64.
-       * - compression, "STORE" by default.
-       * - type, "base64" by default. Values are : string, base64, uint8array, arraybuffer, blob.
-       * @return {String|Uint8Array|ArrayBuffer|Blob} the zip file
-       */
-      generate : function(options) {
-         options = extend(options || {}, {
-            base64 : true,
-            compression : "STORE",
-            type : "base64"
-         });
-         var compression = options.compression.toUpperCase();
-
-         // The central directory, and files data
-         var directory = [], files = [], fileOffset = 0;
-
-         if (!JSZip.compressions[compression]) {
-            throw compression + " is not a valid compression method !";
-         }
-
-         for (var name in this.files) {
-            if ( !this.files.hasOwnProperty(name) ) { continue; }
-
-            var file = this.files[name];
-
-            var utfEncodedFileName = this.utf8encode(file.name);
-
-            var fileRecord = "",
-            dirRecord = "",
-            data = prepareLocalHeaderData.call(this, file, utfEncodedFileName, compression);
-            fileRecord = JSZip.signature.LOCAL_FILE_HEADER + data.header + utfEncodedFileName + data.compressedData;
-
-            dirRecord = JSZip.signature.CENTRAL_FILE_HEADER +
-            // version made by (00: DOS)
-            "\x14\x00" +
-            // file header (common to file and central directory)
-            data.header +
-            // file comment length
-            "\x00\x00" +
-            // disk number start
-            "\x00\x00" +
-            // internal file attributes TODO
-            "\x00\x00" +
-            // external file attributes
-            (this.files[name].options.dir===true?"\x10\x00\x00\x00":"\x00\x00\x00\x00")+
-            // relative offset of local header
-            decToHex(fileOffset, 4) +
-            // file name
-            utfEncodedFileName;
-
-            fileOffset += fileRecord.length;
-
-            files.push(fileRecord);
-            directory.push(dirRecord);
-         }
-
-         var fileData = files.join("");
-         var dirData = directory.join("");
-
-         var dirEnd = "";
-
-         // end of central dir signature
-         dirEnd = JSZip.signature.CENTRAL_DIRECTORY_END +
-         // number of this disk
-         "\x00\x00" +
-         // number of the disk with the start of the central directory
-         "\x00\x00" +
-         // total number of entries in the central directory on this disk
-         decToHex(files.length, 2) +
-         // total number of entries in the central directory
-         decToHex(files.length, 2) +
-         // size of the central directory   4 bytes
-         decToHex(dirData.length, 4) +
-         // offset of start of central directory with respect to the starting disk number
-         decToHex(fileData.length, 4) +
-         // .ZIP file comment length
-         "\x00\x00";
-
-         var zip = fileData + dirData + dirEnd;
-
-
-         switch(options.type.toLowerCase()) {
-            case "uint8array" :
-               return JSZip.utils.string2Uint8Array(zip);
-            case "arraybuffer" :
-               return JSZip.utils.string2Uint8Array(zip).buffer;
-            case "blob" :
-               return JSZip.utils.string2Blob(zip);
-            case "base64" :
-               return (options.base64) ? JSZipBase64.encode(zip) : zip;
-            default : // case "string" :
-               return zip;
-         }
-      },
-
-      /**
-       *
-       *  Javascript crc32
-       *  http://www.webtoolkit.info/
-       *
-       */
-      crc32 : function(str, crc) {
-
-         if (str === "" || typeof str === "undefined") {
-            return 0;
-         }
-
-         var table = [
-            0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
-            0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
-            0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
-            0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
-            0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
-            0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
-            0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
-            0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
-            0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
-            0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
-            0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
-            0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
-            0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
-            0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
-            0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
-            0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
-            0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
-            0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
-            0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
-            0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
-            0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
-            0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
-            0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
-            0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
-            0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
-            0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
-            0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
-            0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
-            0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
-            0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
-            0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
-            0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
-            0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
-            0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
-            0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
-            0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
-            0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
-            0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
-            0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
-            0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
-            0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
-            0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
-            0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
-            0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
-            0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
-            0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
-            0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
-            0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
-            0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
-            0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
-            0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
-            0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
-            0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
-            0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
-            0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
-            0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
-            0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
-            0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
-            0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
-            0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
-            0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
-            0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
-            0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
-            0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
-         ];
-
-         if (typeof(crc) == "undefined") { crc = 0; }
-         var x = 0;
-         var y = 0;
-
-         crc = crc ^ (-1);
-         for( var i = 0, iTop = str.length; i < iTop; i++ ) {
-            y = ( crc ^ str.charCodeAt( i ) ) & 0xFF;
-            x = table[y];
-            crc = ( crc >>> 8 ) ^ x;
-         }
-
-         return crc ^ (-1);
-      },
-
-      // Inspired by http://my.opera.com/GreyWyvern/blog/show.dml/1725165
-      clone : function() {
-         var newObj = new JSZip();
-         for (var i in this) {
-            if (typeof this[i] !== "function") {
-               newObj[i] = this[i];
-            }
-         }
-         return newObj;
-      },
-
-
-      /**
-       * http://www.webtoolkit.info/javascript-utf8.html
-       */
-      utf8encode : function (string) {
-         string = string.replace(/\r\n/g,"\n");
-         var utftext = "";
-
-         for (var n = 0; n < string.length; n++) {
-
-            var c = string.charCodeAt(n);
-
-            if (c < 128) {
-               utftext += String.fromCharCode(c);
-            } else if ((c > 127) && (c < 2048)) {
-               utftext += String.fromCharCode((c >> 6) | 192);
-               utftext += String.fromCharCode((c & 63) | 128);
-            } else {
-               utftext += String.fromCharCode((c >> 12) | 224);
-               utftext += String.fromCharCode(((c >> 6) & 63) | 128);
-               utftext += String.fromCharCode((c & 63) | 128);
-            }
-
-         }
-
-         return utftext;
-      },
-
-      /**
-       * http://www.webtoolkit.info/javascript-utf8.html
-       */
-      utf8decode : function (utftext) {
-         var string = "";
-         var i = 0;
-         var c = 0, c1 = 0, c2 = 0, c3 = 0;
-
-         while ( i < utftext.length ) {
-
-            c = utftext.charCodeAt(i);
-
-            if (c < 128) {
-               string += String.fromCharCode(c);
-               i++;
-            } else if ((c > 191) && (c < 224)) {
-               c2 = utftext.charCodeAt(i+1);
-               string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
-               i += 2;
-            } else {
-               c2 = utftext.charCodeAt(i+1);
-               c3 = utftext.charCodeAt(i+2);
-               string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
-               i += 3;
-            }
-
-         }
-
-         return string;
-      }
-   };
-}());
-
-/*
- * Compression methods
- * This object is filled in as follow :
- * name : {
- *    magic // the 2 bytes indentifying the compression method
- *    compress // function, take the uncompressed content and return it compressed.
- *    uncompress // function, take the compressed content and return it uncompressed.
- * }
- *
- * STORE is the default compression method, so it's included in this file.
- * Other methods should go to separated files : the user wants modularity.
- */
-JSZip.compressions = {
-   "STORE" : {
-      magic : "\x00\x00",
-      compress : function (content) {
-         return content; // no compression
-      },
-      uncompress : function (content) {
-         return content; // no compression
-      }
-   }
-};
-
-/*
- * List features that require a modern browser, and if the current browser support them.
- */
-JSZip.support = {
-   // contains true if JSZip can read/generate ArrayBuffer, false otherwise.
-   arraybuffer : (function(){
-      return typeof ArrayBuffer !== "undefined" && typeof Uint8Array !== "undefined";
-   })(),
-   // contains true if JSZip can read/generate Uint8Array, false otherwise.
-   uint8array : (function(){
-      return typeof Uint8Array !== "undefined";
-   })(),
-   // contains true if JSZip can read/generate Blob, false otherwise.
-   blob : (function(){
-      // the spec started with BlobBuilder then replaced it with a construtor for Blob.
-      // Result : we have browsers that :
-      // * know the BlobBuilder (but with prefix)
-      // * know the Blob constructor
-      // * know about Blob but not about how to build them
-      // About the "=== 0" test : if given the wrong type, it may be converted to a string.
-      // Instead of an empty content, we will get "[object Uint8Array]" for example.
-      if (typeof ArrayBuffer === "undefined") {
-         return false;
-      }
-      var buffer = new ArrayBuffer(0);
-      try {
-         return new Blob([buffer], { type: "application/zip" }).size === 0;
-      }
-      catch(e) {}
-
-      try {
-         var builder = new (window.BlobBuilder || window.WebKitBlobBuilder ||
-                            window.MozBlobBuilder || window.MSBlobBuilder)();
-         builder.append(buffer);
-         return builder.getBlob('application/zip').size === 0;
-      }
-      catch(e) {}
-
-      return false;
-   })()
-};
-
-JSZip.utils = {
-   /**
-    * Convert a string to a "binary string" : a string containing only char codes between 0 and 255.
-    * @param {string} str the string to transform.
-    * @return {String} the binary string.
-    */
-   string2binary : function (str) {
-      var result = "";
-      for (var i = 0; i < str.length; i++) {
-         result += String.fromCharCode(str.charCodeAt(i) & 0xff);
+
+      if (hasDontEnumBug) {
+        for (var i=0; i < dontEnumsLength; i++) {
+          if (hasOwnProperty.call(obj, dontEnums[i])) result.push(dontEnums[i]);
+        }
       }
       return result;
-   },
-   /**
-    * Create a Uint8Array from the string.
-    * @param {string} str the string to transform.
-    * @return {Uint8Array} the typed array.
-    * @throws {Error} an Error if the browser doesn't support the requested feature.
-    */
-   string2Uint8Array : function (str) {
-      if (!JSZip.support.uint8array) {
-         throw new Error("Uint8Array is not supported by this browser");
-      }
-      var buffer = new ArrayBuffer(str.length);
-      var bufferView = new Uint8Array(buffer);
-      for(var i = 0; i < str.length; i++) {
-         bufferView[i] = str.charCodeAt(i);
-      }
-
-      return bufferView;
-   },
-
-   /**
-    * Create a string from the Uint8Array.
-    * @param {Uint8Array} array the array to transform.
-    * @return {string} the string.
-    * @throws {Error} an Error if the browser doesn't support the requested feature.
-    */
-   uint8Array2String : function (array) {
-      if (!JSZip.support.uint8array) {
-         throw new Error("Uint8Array is not supported by this browser");
-      }
-      var result = "";
-      for(var i = 0; i < array.length; i++) {
-         result += String.fromCharCode(array[i]);
-      }
-
-      return result;
-   },
-   /**
-    * Create a blob from the given string.
-    * @param {string} str the string to transform.
-    * @return {Blob} the string.
-    * @throws {Error} an Error if the browser doesn't support the requested feature.
-    */
-   string2Blob : function (str) {
-      if (!JSZip.support.blob) {
-         throw new Error("Blob is not supported by this browser");
-      }
-
-      var buffer = JSZip.utils.string2Uint8Array(str).buffer;
-      try {
-         // Blob constructor
-         return new Blob([buffer], { type: "application/zip" });
-      }
-      catch(e) {}
-
-      try {
-         // deprecated, browser only, old way
-         var builder = new (window.BlobBuilder || window.WebKitBlobBuilder ||
-                            window.MozBlobBuilder || window.MSBlobBuilder)();
-         builder.append(buffer);
-         return builder.getBlob('application/zip');
-      }
-      catch(e) {}
-
-      // well, fuck ?!
-      throw new Error("Bug : can't construct the Blob.");
-   }
-};
-
-/**
- *
- *  Base64 encode / decode
- *  http://www.webtoolkit.info/
- *
- *  Hacked so that it doesn't utf8 en/decode everything
- **/
-var JSZipBase64 = (function() {
-   // private property
-   var _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
-
-   return {
-      // public method for encoding
-      encode : function(input, utf8) {
-         var output = "";
-         var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
-         var i = 0;
-
-         while (i < input.length) {
-
-            chr1 = input.charCodeAt(i++);
-            chr2 = input.charCodeAt(i++);
-            chr3 = input.charCodeAt(i++);
-
-            enc1 = chr1 >> 2;
-            enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
-            enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
-            enc4 = chr3 & 63;
-
-            if (isNaN(chr2)) {
-               enc3 = enc4 = 64;
-            } else if (isNaN(chr3)) {
-               enc4 = 64;
-            }
-
-            output = output +
-               _keyStr.charAt(enc1) + _keyStr.charAt(enc2) +
-               _keyStr.charAt(enc3) + _keyStr.charAt(enc4);
-
-         }
-
-         return output;
-      },
-
-      // public method for decoding
-      decode : function(input, utf8) {
-         var output = "";
-         var chr1, chr2, chr3;
-         var enc1, enc2, enc3, enc4;
-         var i = 0;
-
-         input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
-
-         while (i < input.length) {
-
-            enc1 = _keyStr.indexOf(input.charAt(i++));
-            enc2 = _keyStr.indexOf(input.charAt(i++));
-            enc3 = _keyStr.indexOf(input.charAt(i++));
-            enc4 = _keyStr.indexOf(input.charAt(i++));
-
-            chr1 = (enc1 << 2) | (enc2 >> 4);
-            chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
-            chr3 = ((enc3 & 3) << 6) | enc4;
-
-            output = output + String.fromCharCode(chr1);
-
-            if (enc3 != 64) {
-               output = output + String.fromCharCode(chr2);
-            }
-            if (enc4 != 64) {
-               output = output + String.fromCharCode(chr3);
-            }
-
-         }
-
-         return output;
-
+    };
+  })();
+}
+
+// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
+if (!Array.prototype.filter)
+{
+  Array.prototype.filter = function(fun /*, thisp */)
+  {
+    "use strict";
+
+    if (this == null)
+      throw new TypeError();
+
+    var t = Object(this);
+    var len = t.length >>> 0;
+    if (typeof fun != "function")
+      throw new TypeError();
+
+    var res = [];
+    var thisp = arguments[1];
+    for (var i = 0; i < len; i++)
+    {
+      if (i in t)
+      {
+        var val = t[i]; // in case fun mutates this
+        if (fun.call(thisp, val, i, t))
+          res.push(val);
       }
-   };
-}());
-
-// enforcing Stuk's coding style
-// vim: set shiftwidth=3 softtabstop=3:
-/*
- * Port of a script by Masanao Izumo.
- *
- * Only changes : wrap all the variables in a function and add the 
- * main function to JSZip (DEFLATE compression method).
- * Everything else was written by M. Izumo.
- *
- * Original code can be found here: http://www.onicos.com/staff/iz/amuse/javascript/expert/deflate.txt
- */
-
-if(!JSZip) {
-   throw "JSZip not defined";
-}
-
-/*
- * Original:
- *   http://www.onicos.com/staff/iz/amuse/javascript/expert/deflate.txt
- */
-
-(function(){
-
-/* Copyright (C) 1999 Masanao Izumo <iz@onicos.co.jp>
- * Version: 1.0.1
- * LastModified: Dec 25 1999
- */
-
-/* Interface:
- * data = zip_deflate(src);
- */
-
-/* constant parameters */
-var zip_WSIZE = 32768;		// Sliding Window size
-var zip_STORED_BLOCK = 0;
-var zip_STATIC_TREES = 1;
-var zip_DYN_TREES    = 2;
-
-/* for deflate */
-var zip_DEFAULT_LEVEL = 6;
-var zip_FULL_SEARCH = true;
-var zip_INBUFSIZ = 32768;	// Input buffer size
-var zip_INBUF_EXTRA = 64;	// Extra buffer
-var zip_OUTBUFSIZ = 1024 * 8;
-var zip_window_size = 2 * zip_WSIZE;
-var zip_MIN_MATCH = 3;
-var zip_MAX_MATCH = 258;
-var zip_BITS = 16;
-// for SMALL_MEM
-var zip_LIT_BUFSIZE = 0x2000;
-var zip_HASH_BITS = 13;
-// for MEDIUM_MEM
-// var zip_LIT_BUFSIZE = 0x4000;
-// var zip_HASH_BITS = 14;
-// for BIG_MEM
-// var zip_LIT_BUFSIZE = 0x8000;
-// var zip_HASH_BITS = 15;
-if(zip_LIT_BUFSIZE > zip_INBUFSIZ)
-    alert("error: zip_INBUFSIZ is too small");
-if((zip_WSIZE<<1) > (1<<zip_BITS))
-    alert("error: zip_WSIZE is too large");
-if(zip_HASH_BITS > zip_BITS-1)
-    alert("error: zip_HASH_BITS is too large");
-if(zip_HASH_BITS < 8 || zip_MAX_MATCH != 258)
-    alert("error: Code too clever");
-var zip_DIST_BUFSIZE = zip_LIT_BUFSIZE;
-var zip_HASH_SIZE = 1 << zip_HASH_BITS;
-var zip_HASH_MASK = zip_HASH_SIZE - 1;
-var zip_WMASK = zip_WSIZE - 1;
-var zip_NIL = 0; // Tail of hash chains
-var zip_TOO_FAR = 4096;
-var zip_MIN_LOOKAHEAD = zip_MAX_MATCH + zip_MIN_MATCH + 1;
-var zip_MAX_DIST = zip_WSIZE - zip_MIN_LOOKAHEAD;
-var zip_SMALLEST = 1;
-var zip_MAX_BITS = 15;
-var zip_MAX_BL_BITS = 7;
-var zip_LENGTH_CODES = 29;
-var zip_LITERALS =256;
-var zip_END_BLOCK = 256;
-var zip_L_CODES = zip_LITERALS + 1 + zip_LENGTH_CODES;
-var zip_D_CODES = 30;
-var zip_BL_CODES = 19;
-var zip_REP_3_6 = 16;
-var zip_REPZ_3_10 = 17;
-var zip_REPZ_11_138 = 18;
-var zip_HEAP_SIZE = 2 * zip_L_CODES + 1;
-var zip_H_SHIFT = parseInt((zip_HASH_BITS + zip_MIN_MATCH - 1) /
-			   zip_MIN_MATCH);
-
-/* variables */
-var zip_free_queue;
-var zip_qhead, zip_qtail;
-var zip_initflag;
-var zip_outbuf = null;
-var zip_outcnt, zip_outoff;
-var zip_complete;
-var zip_window;
-var zip_d_buf;
-var zip_l_buf;
-var zip_prev;
-var zip_bi_buf;
-var zip_bi_valid;
-var zip_block_start;
-var zip_ins_h;
-var zip_hash_head;
-var zip_prev_match;
-var zip_match_available;
-var zip_match_length;
-var zip_prev_length;
-var zip_strstart;
-var zip_match_start;
-var zip_eofile;
-var zip_lookahead;
-var zip_max_chain_length;
-var zip_max_lazy_match;
-var zip_compr_level;
-var zip_good_match;
-var zip_nice_match;
-var zip_dyn_ltree;
-var zip_dyn_dtree;
-var zip_static_ltree;
-var zip_static_dtree;
-var zip_bl_tree;
-var zip_l_desc;
-var zip_d_desc;
-var zip_bl_desc;
-var zip_bl_count;
-var zip_heap;
-var zip_heap_len;
-var zip_heap_max;
-var zip_depth;
-var zip_length_code;
-var zip_dist_code;
-var zip_base_length;
-var zip_base_dist;
-var zip_flag_buf;
-var zip_last_lit;
-var zip_last_dist;
-var zip_last_flags;
-var zip_flags;
-var zip_flag_bit;
-var zip_opt_len;
-var zip_static_len;
-var zip_deflate_data;
-var zip_deflate_pos;
-
-/* objects (deflate) */
-
-var zip_DeflateCT = function() {
-    this.fc = 0; // frequency count or bit string
-    this.dl = 0; // father node in Huffman tree or length of bit string
-}
-
-var zip_DeflateTreeDesc = function() {
-    this.dyn_tree = null;	// the dynamic tree
-    this.static_tree = null;	// corresponding static tree or NULL
-    this.extra_bits = null;	// extra bits for each code or NULL
-    this.extra_base = 0;	// base index for extra_bits
-    this.elems = 0;		// max number of elements in the tree
-    this.max_length = 0;	// max bit length for the codes
-    this.max_code = 0;		// largest code with non zero frequency
-}
-
-/* Values for max_lazy_match, good_match and max_chain_length, depending on
- * the desired pack level (0..9). The values given below have been tuned to
- * exclude worst case performance for pathological files. Better values may be
- * found for specific files.
- */
-var zip_DeflateConfiguration = function(a, b, c, d) {
-    this.good_length = a; // reduce lazy search above this match length
-    this.max_lazy = b;    // do not perform lazy search above this match length
-    this.nice_length = c; // quit search above this match length
-    this.max_chain = d;
-}
-
-var zip_DeflateBuffer = function() {
-    this.next = null;
-    this.len = 0;
-    this.ptr = new Array(zip_OUTBUFSIZ);
-    this.off = 0;
-}
-
-/* constant tables */
-var zip_extra_lbits = new Array(
-    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);
-var zip_extra_dbits = new Array(
-    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);
-var zip_extra_blbits = new Array(
-    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7);
-var zip_bl_order = new Array(
-    16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15);
-var zip_configuration_table = new Array(
-	new zip_DeflateConfiguration(0,    0,   0,    0),
-	new zip_DeflateConfiguration(4,    4,   8,    4),
-	new zip_DeflateConfiguration(4,    5,  16,    8),
-	new zip_DeflateConfiguration(4,    6,  32,   32),
-	new zip_DeflateConfiguration(4,    4,  16,   16),
-	new zip_DeflateConfiguration(8,   16,  32,   32),
-	new zip_DeflateConfiguration(8,   16, 128,  128),
-	new zip_DeflateConfiguration(8,   32, 128,  256),
-	new zip_DeflateConfiguration(32, 128, 258, 1024),
-	new zip_DeflateConfiguration(32, 258, 258, 4096));
-
-
-/* routines (deflate) */
-
-var zip_deflate_start = function(level) {
-    var i;
-
-    if(!level)
-	level = zip_DEFAULT_LEVEL;
-    else if(level < 1)
-	level = 1;
-    else if(level > 9)
-	level = 9;
-
-    zip_compr_level = level;
-    zip_initflag = false;
-    zip_eofile = false;
-    if(zip_outbuf != null)
-	return;
-
-    zip_free_queue = zip_qhead = zip_qtail = null;
-    zip_outbuf = new Array(zip_OUTBUFSIZ);
-    zip_window = new Array(zip_window_size);
-    zip_d_buf = new Array(zip_DIST_BUFSIZE);
-    zip_l_buf = new Array(zip_INBUFSIZ + zip_INBUF_EXTRA);
-    zip_prev = new Array(1 << zip_BITS);
-    zip_dyn_ltree = new Array(zip_HEAP_SIZE);
-    for(i = 0; i < zip_HEAP_SIZE; i++)
-	zip_dyn_ltree[i] = new zip_DeflateCT();
-    zip_dyn_dtree = new Array(2*zip_D_CODES+1);
-    for(i = 0; i < 2*zip_D_CODES+1; i++)
-	zip_dyn_dtree[i] = new zip_DeflateCT();
-    zip_static_ltree = new Array(zip_L_CODES+2);
-    for(i = 0; i < zip_L_CODES+2; i++)
-	zip_static_ltree[i] = new zip_DeflateCT();
-    zip_static_dtree = new Array(zip_D_CODES);
-    for(i = 0; i < zip_D_CODES; i++)
-	zip_static_dtree[i] = new zip_DeflateCT();
-    zip_bl_tree = new Array(2*zip_BL_CODES+1);
-    for(i = 0; i < 2*zip_BL_CODES+1; i++)
-	zip_bl_tree[i] = new zip_DeflateCT();
-    zip_l_desc = new zip_DeflateTreeDesc();
-    zip_d_desc = new zip_DeflateTreeDesc();
-    zip_bl_desc = new zip_DeflateTreeDesc();
-    zip_bl_count = new Array(zip_MAX_BITS+1);
-    zip_heap = new Array(2*zip_L_CODES+1);
-    zip_depth = new Array(2*zip_L_CODES+1);
-    zip_length_code = new Array(zip_MAX_MATCH-zip_MIN_MATCH+1);
-    zip_dist_code = new Array(512);
-    zip_base_length = new Array(zip_LENGTH_CODES);
-    zip_base_dist = new Array(zip_D_CODES);
-    zip_flag_buf = new Array(parseInt(zip_LIT_BUFSIZE / 8));
-}
-
-var zip_deflate_end = function() {
-    zip_free_queue = zip_qhead = zip_qtail = null;
-    zip_outbuf = null;
-    zip_window = null;
-    zip_d_buf = null;
-    zip_l_buf = null;
-    zip_prev = null;
-    zip_dyn_ltree = null;
-    zip_dyn_dtree = null;
-    zip_static_ltree = null;
-    zip_static_dtree = null;
-    zip_bl_tree = null;
-    zip_l_desc = null;
-    zip_d_desc = null;
-    zip_bl_desc = null;
-    zip_bl_count = null;
-    zip_heap = null;
-    zip_depth = null;
-    zip_length_code = null;
-    zip_dist_code = null;
-    zip_base_length = null;
-    zip_base_dist = null;
-    zip_flag_buf = null;
-}
-
-var zip_reuse_queue = function(p) {
-    p.next = zip_free_queue;
-    zip_free_queue = p;
-}
-
-var zip_new_queue = function() {
-    var p;
-
-    if(zip_free_queue != null)
+    }
+
+    return res;
+  };
+}
+
+// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/Trim
+if (!String.prototype.trim) {
+  String.prototype.trim = function () {
+    return this.replace(/^\s+|\s+$/g, '');
+  };
+}
+
+// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
+if (!Array.prototype.forEach)
+{
+  Array.prototype.forEach = function(fun /*, thisArg */)
+  {
+    "use strict";
+
+    if (this === void 0 || this === null)
+      throw new TypeError();
+
+    var t = Object(this);
+    var len = t.length >>> 0;
+    if (typeof fun !== "function")
+      throw new TypeError();
+
+    var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
+    for (var i = 0; i < len; i++)
     {
-	p = zip_free_queue;
-	zip_free_queue = zip_free_queue.next;
-    }
-    else
-	p = new zip_DeflateBuffer();
-    p.next = null;
-    p.len = p.off = 0;
-
-    return p;
-}
-
-var zip_head1 = function(i) {
-    return zip_prev[zip_WSIZE + i];
-}
-
-var zip_head2 = function(i, val) {
-    return zip_prev[zip_WSIZE + i] = val;
-}
-
-/* put_byte is used for the compressed output, put_ubyte for the
- * uncompressed output. However unlzw() uses window for its
- * suffix table instead of its output buffer, so it does not use put_ubyte
- * (to be cleaned up).
- */
-var zip_put_byte = function(c) {
-    zip_outbuf[zip_outoff + zip_outcnt++] = c;
-    if(zip_outoff + zip_outcnt == zip_OUTBUFSIZ)
-	zip_qoutbuf();
-}
-
-/* Output a 16 bit value, lsb first */
-var zip_put_short = function(w) {
-    w &= 0xffff;
-    if(zip_outoff + zip_outcnt < zip_OUTBUFSIZ - 2) {
-	zip_outbuf[zip_outoff + zip_outcnt++] = (w & 0xff);
-	zip_outbuf[zip_outoff + zip_outcnt++] = (w >>> 8);
-    } else {
-	zip_put_byte(w & 0xff);
-	zip_put_byte(w >>> 8);
-    }
-}
-
-/* ==========================================================================
- * Insert string s in the dictionary and set match_head to the previous head
- * of the hash chain (the most recent string with same hash key). Return
- * the previous length of the hash chain.
- * IN  assertion: all calls to to INSERT_STRING are made with consecutive
- *    input characters and the first MIN_MATCH bytes of s are valid
- *    (except for the last MIN_MATCH-1 bytes of the input file).
- */
-var zip_INSERT_STRING = function() {
-    zip_ins_h = ((zip_ins_h << zip_H_SHIFT)
-		 ^ (zip_window[zip_strstart + zip_MIN_MATCH - 1] & 0xff))
-	& zip_HASH_MASK;
-    zip_hash_head = zip_head1(zip_ins_h);
-    zip_prev[zip_strstart & zip_WMASK] = zip_hash_head;
-    zip_head2(zip_ins_h, zip_strstart);
-}
-
-/* Send a code of the given tree. c and tree must not have side effects */
-var zip_SEND_CODE = function(c, tree) {
-    zip_send_bits(tree[c].fc, tree[c].dl);
-}
-
-/* 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.
- */
-var zip_D_CODE = function(dist) {
-    return (dist < 256 ? zip_dist_code[dist]
-	    : zip_dist_code[256 + (dist>>7)]) & 0xff;
-}
-
-/* ==========================================================================
- * Compares to subtrees, using the tree depth as tie breaker when
- * the subtrees have equal frequency. This minimizes the worst case length.
- */
-var zip_SMALLER = function(tree, n, m) {
-    return tree[n].fc < tree[m].fc ||
-      (tree[n].fc == tree[m].fc && zip_depth[n] <= zip_depth[m]);
-}
-
-/* ==========================================================================
- * read string data
- */
-var zip_read_buff = function(buff, offset, n) {
-    var i;
-    for(i = 0; i < n && zip_deflate_pos < zip_deflate_data.length; i++)
-	buff[offset + i] =
-	    zip_deflate_data.charCodeAt(zip_deflate_pos++) & 0xff;
-    return i;
-}
-
-/* ==========================================================================
- * Initialize the "longest match" routines for a new file
- */
-var zip_lm_init = function() {
-    var j;
-
-    /* Initialize the hash table. */
-    for(j = 0; j < zip_HASH_SIZE; j++)
-//	zip_head2(j, zip_NIL);
-	zip_prev[zip_WSIZE + j] = 0;
-    /* prev will be initialized on the fly */
-
-    /* Set the default configuration parameters:
-     */
-    zip_max_lazy_match = zip_configuration_table[zip_compr_level].max_lazy;
-    zip_good_match     = zip_configuration_table[zip_compr_level].good_length;
-    if(!zip_FULL_SEARCH)
-	zip_nice_match = zip_configuration_table[zip_compr_level].nice_length;
-    zip_max_chain_length = zip_configuration_table[zip_compr_level].max_chain;
-
-    zip_strstart = 0;
-    zip_block_start = 0;
-
-    zip_lookahead = zip_read_buff(zip_window, 0, 2 * zip_WSIZE);
-    if(zip_lookahead <= 0) {
-	zip_eofile = true;
-	zip_lookahead = 0;
-	return;
-    }
-    zip_eofile = false;
-    /* Make sure that we always have enough lookahead. This is important
-     * if input comes from a device such as a tty.
-     */
-    while(zip_lookahead < zip_MIN_LOOKAHEAD && !zip_eofile)
-	zip_fill_window();
-
-    /* If lookahead < MIN_MATCH, ins_h is garbage, but this is
-     * not important since only literal bytes will be emitted.
-     */
-    zip_ins_h = 0;
-    for(j = 0; j < zip_MIN_MATCH - 1; j++) {
-//      UPDATE_HASH(ins_h, window[j]);
-	zip_ins_h = ((zip_ins_h << zip_H_SHIFT) ^ (zip_window[j] & 0xff)) & zip_HASH_MASK;
-    }
-}
-
-/* ==========================================================================
- * Set match_start to the longest match starting at the given string and
- * return its length. Matches shorter or equal to prev_length are discarded,
- * in which case the result is equal to prev_length and match_start is
- * garbage.
- * IN assertions: cur_match is the head of the hash chain for the current
- *   string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1
- */
-var zip_longest_match = function(cur_match) {
-    var chain_length = zip_max_chain_length; // max hash chain length
-    var scanp = zip_strstart; // current string
-    var matchp;		// matched string
-    var len;		// length of current match
-    var best_len = zip_prev_length;	// best match length so far
-
-    /* Stop when cur_match becomes <= limit. To simplify the code,
-     * we prevent matches with the string of window index 0.
-     */
-    var limit = (zip_strstart > zip_MAX_DIST ? zip_strstart - zip_MAX_DIST : zip_NIL);
-
-    var strendp = zip_strstart + zip_MAX_MATCH;
-    var scan_end1 = zip_window[scanp + best_len - 1];
-    var scan_end  = zip_window[scanp + best_len];
-
-    /* Do not waste too much time if we already have a good match: */
-    if(zip_prev_length >= zip_good_match)
-	chain_length >>= 2;
-
-//  Assert(encoder->strstart <= window_size-MIN_LOOKAHEAD, "insufficient lookahead");
-
-    do {
-//    Assert(cur_match < encoder->strstart, "no future");
-	matchp = cur_match;
-
-	/* Skip to next match if the match length cannot increase
-	    * or if the match length is less than 2:
-	*/
-	if(zip_window[matchp + best_len]	!= scan_end  ||
-	   zip_window[matchp + best_len - 1]	!= scan_end1 ||
-	   zip_window[matchp]			!= zip_window[scanp] ||
-	   zip_window[++matchp]			!= zip_window[scanp + 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.
-         */
-	scanp += 2;
-	matchp++;
-
-	/* We check for insufficient lookahead only every 8th comparison;
-         * the 256th check will be made at strstart+258.
-         */
-	do {
-	} while(zip_window[++scanp] == zip_window[++matchp] &&
-		zip_window[++scanp] == zip_window[++matchp] &&
-		zip_window[++scanp] == zip_window[++matchp] &&
-		zip_window[++scanp] == zip_window[++matchp] &&
-		zip_window[++scanp] == zip_window[++matchp] &&
-		zip_window[++scanp] == zip_window[++matchp] &&
-		zip_window[++scanp] == zip_window[++matchp] &&
-		zip_window[++scanp] == zip_window[++matchp] &&
-		scanp < strendp);
-
-      len = zip_MAX_MATCH - (strendp - scanp);
-      scanp = strendp - zip_MAX_MATCH;
-
-      if(len > best_len) {
-	  zip_match_start = cur_match;
-	  best_len = len;
-	  if(zip_FULL_SEARCH) {
-	      if(len >= zip_MAX_MATCH) break;
-	  } else {
-	      if(len >= zip_nice_match) break;
-	  }
-
-	  scan_end1  = zip_window[scanp + best_len-1];
-	  scan_end   = zip_window[scanp + best_len];
+      if (i in t)
+        fun.call(thisArg, t[i], i, t);
+    }
+  };
+}
+
+// Production steps of ECMA-262, Edition 5, 15.4.4.19
+// Reference: http://es5.github.com/#x15.4.4.19
+if (!Array.prototype.map) {
+  Array.prototype.map = function(callback, thisArg) {
+
+    var T, A, k;
+
+    if (this == null) {
+      throw new TypeError(" this is null or not defined");
+    }
+
+    // 1. Let O be the result of calling ToObject passing the |this| value as the argument.
+    var O = Object(this);
+
+    // 2. Let lenValue be the result of calling the Get internal method of O with the argument "length".
+    // 3. Let len be ToUint32(lenValue).
+    var len = O.length >>> 0;
+
+    // 4. If IsCallable(callback) is false, throw a TypeError exception.
+    // See: http://es5.github.com/#x9.11
+    if (typeof callback !== "function") {
+      throw new TypeError(callback + " is not a function");
+    }
+
+    // 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
+    if (thisArg) {
+      T = thisArg;
+    }
+
+    // 6. Let A be a new array created as if by the expression new Array(len) where Array is
+    // the standard built-in constructor with that name and len is the value of len.
+    A = new Array(len);
+
+    // 7. Let k be 0
+    k = 0;
+
+    // 8. Repeat, while k < len
+    while(k < len) {
+
+      var kValue, mappedValue;
+
+      // a. Let Pk be ToString(k).
+      //   This is implicit for LHS operands of the in operator
+      // b. Let kPresent be the result of calling the HasProperty internal method of O with argument Pk.
+      //   This step can be combined with c
+      // c. If kPresent is true, then
+      if (k in O) {
+
+        // i. Let kValue be the result of calling the Get internal method of O with argument Pk.
+        kValue = O[ k ];
+
+        // ii. Let mappedValue be the result of calling the Call internal method of callback
+        // with T as the this value and argument list containing kValue, k, and O.
+        mappedValue = callback.call(T, kValue, k, O);
+
+        // iii. Call the DefineOwnProperty internal method of A with arguments
+        // Pk, Property Descriptor {Value: mappedValue, : true, Enumerable: true, Configurable: true},
+        // and false.
+
+        // In browsers that support Object.defineProperty, use the following:
+        // Object.defineProperty(A, Pk, { value: mappedValue, writable: true, enumerable: true, configurable: true });
+
+        // For best browser support, use the following:
+        A[ k ] = mappedValue;
+      }
+      // d. Increase k by 1.
+      k++;
+    }
+
+    // 9. return A
+    return A;
+  };
+}
+
+// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf
+if (!Array.prototype.indexOf) {
+  Array.prototype.indexOf = function (searchElement, fromIndex) {
+    if ( this === undefined || this === null ) {
+      throw new TypeError( '"this" is null or not defined' );
+    }
+
+    var length = this.length >>> 0; // Hack to convert object.length to a UInt32
+
+    fromIndex = +fromIndex || 0;
+
+    if (Math.abs(fromIndex) === Infinity) {
+      fromIndex = 0;
+    }
+
+    if (fromIndex < 0) {
+      fromIndex += length;
+      if (fromIndex < 0) {
+        fromIndex = 0;
+      }
+    }
+
+    for (;fromIndex < length; fromIndex++) {
+      if (this[fromIndex] === searchElement) {
+        return fromIndex;
       }
-    } while((cur_match = zip_prev[cur_match & zip_WMASK]) > limit
-	    && --chain_length != 0);
-
-    return best_len;
-}
-
-/* ==========================================================================
- * Fill the window when the lookahead becomes insufficient.
- * Updates strstart and lookahead, and sets eofile if end of input file.
- * IN assertion: lookahead < MIN_LOOKAHEAD && strstart + lookahead > 0
- * OUT assertions: at least one byte has been read, or eofile is set;
- *    file reads are performed for at least two bytes (required for the
- *    translate_eol option).
- */
-var zip_fill_window = function() {
-    var n, m;
-
-    // Amount of free space at the end of the window.
-    var more = zip_window_size - zip_lookahead - zip_strstart;
-
-    /* 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.
-     */
-    if(more == -1) {
-	/* Very unlikely, but possible on 16 bit machine if strstart == 0
-         * and lookahead == 1 (input done one byte at time)
-         */
-	more--;
-    } else if(zip_strstart >= zip_WSIZE + zip_MAX_DIST) {
-	/* By the IN assertion, the window is not empty so we can't confuse
-         * more == 0 with more == 64K on a 16 bit machine.
-         */
-//	Assert(window_size == (ulg)2*WSIZE, "no sliding with BIG_MEM");
-
-//	System.arraycopy(window, WSIZE, window, 0, WSIZE);
-	for(n = 0; n < zip_WSIZE; n++)
-	    zip_window[n] = zip_window[n + zip_WSIZE];
-      
-	zip_match_start -= zip_WSIZE;
-	zip_strstart    -= zip_WSIZE; /* we now have strstart >= MAX_DIST: */
-	zip_block_start -= zip_WSIZE;
-
-	for(n = 0; n < zip_HASH_SIZE; n++) {
-	    m = zip_head1(n);
-	    zip_head2(n, m >= zip_WSIZE ? m - zip_WSIZE : zip_NIL);
-	}
-	for(n = 0; n < zip_WSIZE; n++) {
-	    /* If n is not on any hash chain, prev[n] is garbage but
-	     * its value will never be used.
-	     */
-	    m = zip_prev[n];
-	    zip_prev[n] = (m >= zip_WSIZE ? m - zip_WSIZE : zip_NIL);
-	}
-	more += zip_WSIZE;
-    }
-    // At this point, more >= 2
-    if(!zip_eofile) {
-	n = zip_read_buff(zip_window, zip_strstart + zip_lookahead, more);
-	if(n <= 0)
-	    zip_eofile = true;
-	else
-	    zip_lookahead += n;
-    }
-}
-
-/* ==========================================================================
- * Processes a new input file and return its compressed length. This
- * function does not perform lazy evaluationof 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.
- */
-var zip_deflate_fast = function() {
-    while(zip_lookahead != 0 && zip_qhead == null) {
-	var flush; // set if current block must be flushed
-
-	/* Insert the string window[strstart .. strstart+2] in the
-	 * dictionary, and set hash_head to the head of the hash chain:
-	 */
-	zip_INSERT_STRING();
-
-	/* Find the longest match, discarding those <= prev_length.
-	 * At this point we have always match_length < MIN_MATCH
-	 */
-	if(zip_hash_head != zip_NIL &&
-	   zip_strstart - zip_hash_head <= zip_MAX_DIST) {
-	    /* 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).
-	     */
-	    zip_match_length = zip_longest_match(zip_hash_head);
-	    /* longest_match() sets match_start */
-	    if(zip_match_length > zip_lookahead)
-		zip_match_length = zip_lookahead;
-	}
-	if(zip_match_length >= zip_MIN_MATCH) {
-//	    check_match(strstart, match_start, match_length);
-
-	    flush = zip_ct_tally(zip_strstart - zip_match_start,
-				 zip_match_length - zip_MIN_MATCH);
-	    zip_lookahead -= zip_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(zip_match_length <= zip_max_lazy_match) {
-		zip_match_length--; // string at strstart already in hash table
-		do {
-		    zip_strstart++;
-		    zip_INSERT_STRING();
-		    /* strstart never exceeds WSIZE-MAX_MATCH, so there are
-		     * always MIN_MATCH bytes ahead. If lookahead < MIN_MATCH
-		     * these bytes are garbage, but it does not matter since
-		     * the next lookahead bytes will be emitted as literals.
-		     */
-		} while(--zip_match_length != 0);
-		zip_strstart++;
-	    } else {
-		zip_strstart += zip_match_length;
-		zip_match_length = 0;
-		zip_ins_h = zip_window[zip_strstart] & 0xff;
-//		UPDATE_HASH(ins_h, window[strstart + 1]);
-		zip_ins_h = ((zip_ins_h<<zip_H_SHIFT) ^ (zip_window[zip_strstart + 1] & 0xff)) & zip_HASH_MASK;
-
-//#if MIN_MATCH != 3
-//		Call UPDATE_HASH() MIN_MATCH-3 more times
-//#endif
-
-	    }
-	} else {
-	    /* No match, output a literal byte */
-	    flush = zip_ct_tally(0, zip_window[zip_strstart] & 0xff);
-	    zip_lookahead--;
-	    zip_strstart++;
-	}
-	if(flush) {
-	    zip_flush_block(0);
-	    zip_block_start = zip_strstart;
-	}
-
-	/* 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.
-	 */
-	while(zip_lookahead < zip_MIN_LOOKAHEAD && !zip_eofile)
-	    zip_fill_window();
-    }
-}
-
-var zip_deflate_better = function() {
-    /* Process the input block. */
-    while(zip_lookahead != 0 && zip_qhead == null) {
-	/* Insert the string window[strstart .. strstart+2] in the
-	 * dictionary, and set hash_head to the head of the hash chain:
-	 */
-	zip_INSERT_STRING();
-
-	/* Find the longest match, discarding those <= prev_length.
-	 */
-	zip_prev_length = zip_match_length;
-	zip_prev_match = zip_match_start;
-	zip_match_length = zip_MIN_MATCH - 1;
-
-	if(zip_hash_head != zip_NIL &&
-	   zip_prev_length < zip_max_lazy_match &&
-	   zip_strstart - zip_hash_head <= zip_MAX_DIST) {
-	    /* 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).
-	     */
-	    zip_match_length = zip_longest_match(zip_hash_head);
-	    /* longest_match() sets match_start */
-	    if(zip_match_length > zip_lookahead)
-		zip_match_length = zip_lookahead;
-
-	    /* Ignore a length 3 match if it is too distant: */
-	    if(zip_match_length == zip_MIN_MATCH &&
-	       zip_strstart - zip_match_start > zip_TOO_FAR) {
-		/* If prev_match is also MIN_MATCH, match_start is garbage
-		 * but we will ignore the current match anyway.
-		 */
-		zip_match_length--;
-	    }
-	}
-	/* If there was a match at the previous step and the current
-	 * match is not better, output the previous match:
-	 */
-	if(zip_prev_length >= zip_MIN_MATCH &&
-	   zip_match_length <= zip_prev_length) {
-	    var flush; // set if current block must be flushed
-
-//	    check_match(strstart - 1, prev_match, prev_length);
-	    flush = zip_ct_tally(zip_strstart - 1 - zip_prev_match,
-				 zip_prev_length - zip_MIN_MATCH);
-
-	    /* Insert in hash table all strings up to the end of the match.
-	     * strstart-1 and strstart are already inserted.
-	     */
-	    zip_lookahead -= zip_prev_length - 1;
-	    zip_prev_length -= 2;
-	    do {
-		zip_strstart++;
-		zip_INSERT_STRING();
-		/* strstart never exceeds WSIZE-MAX_MATCH, so there are
-		 * always MIN_MATCH bytes ahead. If lookahead < MIN_MATCH
-		 * these bytes are garbage, but it does not matter since the
-		 * next lookahead bytes will always be emitted as literals.
-		 */
-	    } while(--zip_prev_length != 0);
-	    zip_match_available = 0;
-	    zip_match_length = zip_MIN_MATCH - 1;
-	    zip_strstart++;
-	    if(flush) {
-		zip_flush_block(0);
-		zip_block_start = zip_strstart;
-	    }
-	} else if(zip_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.
-	     */
-	    if(zip_ct_tally(0, zip_window[zip_strstart - 1] & 0xff)) {
-		zip_flush_block(0);
-		zip_block_start = zip_strstart;
-	    }
-	    zip_strstart++;
-	    zip_lookahead--;
-	} else {
-	    /* There is no previous match to compare with, wait for
-	     * the next step to decide.
-	     */
-	    zip_match_available = 1;
-	    zip_strstart++;
-	    zip_lookahead--;
-	}
-
-	/* 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.
-	 */
-	while(zip_lookahead < zip_MIN_LOOKAHEAD && !zip_eofile)
-	    zip_fill_window();
-    }
-}
-
-var zip_init_deflate = function() {
-    if(zip_eofile)
-	return;
-    zip_bi_buf = 0;
-    zip_bi_valid = 0;
-    zip_ct_init();
-    zip_lm_init();
-
-    zip_qhead = null;
-    zip_outcnt = 0;
-    zip_outoff = 0;
-
-    if(zip_compr_level <= 3)
-    {
-	zip_prev_length = zip_MIN_MATCH - 1;
-	zip_match_length = 0;
-    }
-    else
-    {
-	zip_match_length = zip_MIN_MATCH - 1;
-	zip_match_available = 0;
-    }
-
-    zip_complete = false;
-}
-
-/* ==========================================================================
- * 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.
- */
-var zip_deflate_internal = function(buff, off, buff_size) {
-    var n;
-
-    if(!zip_initflag)
-    {
-	zip_init_deflate();
-	zip_initflag = true;
-	if(zip_lookahead == 0) { // empty
-	    zip_complete = true;
-	    return 0;
-	}
-    }
-
-    if((n = zip_qcopy(buff, off, buff_size)) == buff_size)
-	return buff_size;
-
-    if(zip_complete)
-	return n;
-
-    if(zip_compr_level <= 3) // optimized for speed
-	zip_deflate_fast();
-    else
-	zip_deflate_better();
-    if(zip_lookahead == 0) {
-	if(zip_match_available != 0)
-	    zip_ct_tally(0, zip_window[zip_strstart - 1] & 0xff);
-	zip_flush_block(1);
-	zip_complete = true;
-    }
-    return n + zip_qcopy(buff, n + off, buff_size - n);
-}
-
-var zip_qcopy = function(buff, off, buff_size) {
-    var n, i, j;
-
-    n = 0;
-    while(zip_qhead != null && n < buff_size)
-    {
-	i = buff_size - n;
-	if(i > zip_qhead.len)
-	    i = zip_qhead.len;
-//      System.arraycopy(qhead.ptr, qhead.off, buff, off + n, i);
-	for(j = 0; j < i; j++)
-	    buff[off + n + j] = zip_qhead.ptr[zip_qhead.off + j];
-	
-	zip_qhead.off += i;
-	zip_qhead.len -= i;
-	n += i;
-	if(zip_qhead.len == 0) {
-	    var p;
-	    p = zip_qhead;
-	    zip_qhead = zip_qhead.next;
-	    zip_reuse_queue(p);
-	}
-    }
-
-    if(n == buff_size)
-	return n;
-
-    if(zip_outoff < zip_outcnt) {
-	i = buff_size - n;
-	if(i > zip_outcnt - zip_outoff)
-	    i = zip_outcnt - zip_outoff;
-	// System.arraycopy(outbuf, outoff, buff, off + n, i);
-	for(j = 0; j < i; j++)
-	    buff[off + n + j] = zip_outbuf[zip_outoff + j];
-	zip_outoff += i;
-	n += i;
-	if(zip_outcnt == zip_outoff)
-	    zip_outcnt = zip_outoff = 0;
-    }
-    return n;
-}
-
-/* ==========================================================================
- * Allocate the match buffer, initialize the various tables and save the
- * location of the internal file attribute (ascii/binary) and method
- * (DEFLATE/STORE).
- */
-var zip_ct_init = function() {
-    var n;	// iterates over tree elements
-    var bits;	// bit counter
-    var length;	// length value
-    var code;	// code value
-    var dist;	// distance index
-
-    if(zip_static_dtree[0].dl != 0) return; // ct_init already called
-
-    zip_l_desc.dyn_tree		= zip_dyn_ltree;
-    zip_l_desc.static_tree	= zip_static_ltree;
-    zip_l_desc.extra_bits	= zip_extra_lbits;
-    zip_l_desc.extra_base	= zip_LITERALS + 1;
-    zip_l_desc.elems		= zip_L_CODES;
-    zip_l_desc.max_length	= zip_MAX_BITS;
-    zip_l_desc.max_code		= 0;
-
-    zip_d_desc.dyn_tree		= zip_dyn_dtree;
-    zip_d_desc.static_tree	= zip_static_dtree;
-    zip_d_desc.extra_bits	= zip_extra_dbits;
-    zip_d_desc.extra_base	= 0;
-    zip_d_desc.elems		= zip_D_CODES;
-    zip_d_desc.max_length	= zip_MAX_BITS;
-    zip_d_desc.max_code		= 0;
-
-    zip_bl_desc.dyn_tree	= zip_bl_tree;
-    zip_bl_desc.static_tree	= null;
-    zip_bl_desc.extra_bits	= zip_extra_blbits;
-    zip_bl_desc.extra_base	= 0;
-    zip_bl_desc.elems		= zip_BL_CODES;
-    zip_bl_desc.max_length	= zip_MAX_BL_BITS;
-    zip_bl_desc.max_code	= 0;
-
-    // Initialize the mapping length (0..255) -> length code (0..28)
-    length = 0;
-    for(code = 0; code < zip_LENGTH_CODES-1; code++) {
-	zip_base_length[code] = length;
-	for(n = 0; n < (1<<zip_extra_lbits[code]); n++)
-	    zip_length_code[length++] = code;
-    }
-    // Assert (length == 256, "ct_init: length != 256");
-
-    /* Note that the length 255 (match length 258) can be represented
-     * in two different ways: code 284 + 5 bits or code 285, so we
-     * overwrite length_code[255] to use the best encoding:
-     */
-    zip_length_code[length-1] = code;
-
-    /* Initialize the mapping dist (0..32K) -> dist code (0..29) */
-    dist = 0;
-    for(code = 0 ; code < 16; code++) {
-	zip_base_dist[code] = dist;
-	for(n = 0; n < (1<<zip_extra_dbits[code]); n++) {
-	    zip_dist_code[dist++] = code;
-	}
-    }
-    // Assert (dist == 256, "ct_init: dist != 256");
-    dist >>= 7; // from now on, all distances are divided by 128
-    for( ; code < zip_D_CODES; code++) {
-	zip_base_dist[code] = dist << 7;
-	for(n = 0; n < (1<<(zip_extra_dbits[code]-7)); n++)
-	    zip_dist_code[256 + dist++] = code;
-    }
-    // Assert (dist == 256, "ct_init: 256+dist != 512");
-
-    // Construct the codes of the static literal tree
-    for(bits = 0; bits <= zip_MAX_BITS; bits++)
-	zip_bl_count[bits] = 0;
-    n = 0;
-    while(n <= 143) { zip_static_ltree[n++].dl = 8; zip_bl_count[8]++; }
-    while(n <= 255) { zip_static_ltree[n++].dl = 9; zip_bl_count[9]++; }
-    while(n <= 279) { zip_static_ltree[n++].dl = 7; zip_bl_count[7]++; }
-    while(n <= 287) { zip_static_ltree[n++].dl = 8; zip_bl_count[8]++; }
-    /* Codes 286 and 287 do not exist, but we must include them in the
-     * tree construction to get a canonical Huffman tree (longest code
-     * all ones)
-     */
-    zip_gen_codes(zip_static_ltree, zip_L_CODES + 1);
-
-    /* The static distance tree is trivial: */
-    for(n = 0; n < zip_D_CODES; n++) {
-	zip_static_dtree[n].dl = 5;
-	zip_static_dtree[n].fc = zip_bi_reverse(n, 5);
-    }
-
-    // Initialize the first block of the first file:
-    zip_init_block();
-}
-
-/* ==========================================================================
- * Initialize a new block.
- */
-var zip_init_block = function() {
-    var n; // iterates over tree elements
-
-    // Initialize the trees.
-    for(n = 0; n < zip_L_CODES;  n++) zip_dyn_ltree[n].fc = 0;
-    for(n = 0; n < zip_D_CODES;  n++) zip_dyn_dtree[n].fc = 0;
-    for(n = 0; n < zip_BL_CODES; n++) zip_bl_tree[n].fc = 0;
-
-    zip_dyn_ltree[zip_END_BLOCK].fc = 1;
-    zip_opt_len = zip_static_len = 0;
-    zip_last_lit = zip_last_dist = zip_last_flags = 0;
-    zip_flags = 0;
-    zip_flag_bit = 1;
-}
-
-/* ==========================================================================
- * 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).
- */
-var zip_pqdownheap = function(
-    tree,	// the tree to restore
-    k) {	// node to move down
-    var v = zip_heap[k];
-    var j = k << 1;	// left son of k
-
-    while(j <= zip_heap_len) {
-	// Set j to the smallest of the two sons:
-	if(j < zip_heap_len &&
-	   zip_SMALLER(tree, zip_heap[j + 1], zip_heap[j]))
-	    j++;
-
-	// Exit if v is smaller than both sons
-	if(zip_SMALLER(tree, v, zip_heap[j]))
-	    break;
-
-	// Exchange v with the smallest son
-	zip_heap[k] = zip_heap[j];
-	k = j;
-
-	// And continue down the tree, setting j to the left son of k
-	j <<= 1;
-    }
-    zip_heap[k] = v;
-}
-
-/* ==========================================================================
- * 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.
- */
-var zip_gen_bitlen = function(desc) { // the tree descriptor
-    var tree		= desc.dyn_tree;
-    var extra		= desc.extra_bits;
-    var base		= desc.extra_base;
-    var max_code	= desc.max_code;
-    var max_length	= desc.max_length;
-    var stree		= desc.static_tree;
-    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 <= zip_MAX_BITS; bits++)
-	zip_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[zip_heap[zip_heap_max]].dl = 0; // root of the heap
-
-    for(h = zip_heap_max + 1; h < zip_HEAP_SIZE; h++) {
-	n = zip_heap[h];
-	bits = tree[tree[n].dl].dl + 1;
-	if(bits > max_length) {
-	    bits = max_length;
-	    overflow++;
-	}
-	tree[n].dl = bits;
-	// We overwrite tree[n].dl which is no longer needed
-
-	if(n > max_code)
-	    continue; // not a leaf node
-
-	zip_bl_count[bits]++;
-	xbits = 0;
-	if(n >= base)
-	    xbits = extra[n - base];
-	f = tree[n].fc;
-	zip_opt_len += f * (bits + xbits);
-	if(stree != null)
-	    zip_static_len += f * (stree[n].dl + 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(zip_bl_count[bits] == 0)
-	    bits--;
-	zip_bl_count[bits]--;		// move one leaf down the tree
-	zip_bl_count[bits + 1] += 2;	// move one overflow item as its brother
-	zip_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);
-
-    /* Now recompute all bit lengths, scanning in increasing frequency.
-     * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all
-     * lengths instead of fixing only the wrong ones. This idea is taken
-     * from 'ar' written by Haruhiko Okumura.)
-     */
-    for(bits = max_length; bits != 0; bits--) {
-	n = zip_bl_count[bits];
-	while(n != 0) {
-	    m = zip_heap[--h];
-	    if(m > max_code)
-		continue;
-	    if(tree[m].dl != bits) {
-		zip_opt_len += (bits - tree[m].dl) * tree[m].fc;
-		tree[m].fc = bits;
-	    }
-	    n--;
-	}
-    }
-}
-
-  /* ==========================================================================
-   * 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.
-   */
-var zip_gen_codes = function(tree,	// the tree to decorate
-		   max_code) {	// largest code with non zero frequency
-    var next_code = new Array(zip_MAX_BITS+1); // next code value for each bit length
-    var code = 0;		// running code value
-    var bits;			// bit index
-    var n;			// code index
-
-    /* The distribution counts are first used to generate the code values
-     * without bit reversal.
-     */
-    for(bits = 1; bits <= zip_MAX_BITS; bits++) {
-	code = ((code + zip_bl_count[bits-1]) << 1);
-	next_code[bits] = code;
-    }
-
-    /* Check that the bit counts in bl_count are consistent. The last code
-     * must be all ones.
-     */
-//    Assert (code + encoder->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++) {
-	var len = tree[n].dl;
-	if(len == 0)
-	    continue;
-	// Now reverse the bits
-	tree[n].fc = zip_bi_reverse(next_code[len]++, len);
-
-//      Tracec(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ",
-//	  n, (isgraph(n) ? n : ' '), len, tree[n].fc, next_code[len]-1));
-    }
-}
-
-/* ==========================================================================
- * 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.
- */
-var zip_build_tree = function(desc) { // the tree descriptor
-    var tree	= desc.dyn_tree;
-    var stree	= desc.static_tree;
-    var elems	= desc.elems;
-    var n, m;		// iterate over heap elements
-    var max_code = -1;	// largest code with non zero frequency
-    var node = elems;	// next internal node of the tree
-
-    /* Construct the initial heap, with least frequent element in
-     * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
-     * heap[0] is not used.
-     */
-    zip_heap_len = 0;
-    zip_heap_max = zip_HEAP_SIZE;
-
-    for(n = 0; n < elems; n++) {
-	if(tree[n].fc != 0) {
-	    zip_heap[++zip_heap_len] = max_code = n;
-	    zip_depth[n] = 0;
-	} else
-	    tree[n].dl = 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(zip_heap_len < 2) {
-	var xnew = zip_heap[++zip_heap_len] = (max_code < 2 ? ++max_code : 0);
-	tree[xnew].fc = 1;
-	zip_depth[xnew] = 0;
-	zip_opt_len--;
-	if(stree != null)
-	    zip_static_len -= stree[xnew].dl;
-	// new is 0 or 1 so it does not have extra bits
-    }
-    desc.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 = zip_heap_len >> 1; n >= 1; n--)
-	zip_pqdownheap(tree, n);
-
-    /* Construct the Huffman tree by repeatedly combining the least two
-     * frequent nodes.
-     */
-    do {
-	n = zip_heap[zip_SMALLEST];
-	zip_heap[zip_SMALLEST] = zip_heap[zip_heap_len--];
-	zip_pqdownheap(tree, zip_SMALLEST);
-
-	m = zip_heap[zip_SMALLEST];  // m = node of next least frequency
-
-	// keep the nodes sorted by frequency
-	zip_heap[--zip_heap_max] = n;
-	zip_heap[--zip_heap_max] = m;
-
-	// Create a new node father of n and m
-	tree[node].fc = tree[n].fc + tree[m].fc;
-//	depth[node] = (char)(MAX(depth[n], depth[m]) + 1);
-	if(zip_depth[n] > zip_depth[m] + 1)
-	    zip_depth[node] = zip_depth[n];
-	else
-	    zip_depth[node] = zip_depth[m] + 1;
-	tree[n].dl = tree[m].dl = node;
-
-	// and insert the new node in the heap
-	zip_heap[zip_SMALLEST] = node++;
-	zip_pqdownheap(tree, zip_SMALLEST);
-
-    } while(zip_heap_len >= 2);
-
-    zip_heap[--zip_heap_max] = zip_heap[zip_SMALLEST];
-
-    /* At this point, the fields freq and dad are set. We can now
-     * generate the bit lengths.
-     */
-    zip_gen_bitlen(desc);
-
-    // The field len is now set, we can generate the bit codes
-    zip_gen_codes(tree, max_code);
-}
-
-/* ==========================================================================
- * Scan a literal or distance tree to determine the frequencies of the codes
- * in the bit length tree. Updates opt_len to take into account the repeat
- * counts. (The contribution of the bit length codes will be added later
- * during the construction of bl_tree.)
- */
-var zip_scan_tree = function(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].dl;	// 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].dl = 0xffff; // guard
-
-    for(n = 0; n <= max_code; n++) {
-	curlen = nextlen;
-	nextlen = tree[n + 1].dl;
-	if(++count < max_count && curlen == nextlen)
-	    continue;
-	else if(count < min_count)
-	    zip_bl_tree[curlen].fc += count;
-	else if(curlen != 0) {
-	    if(curlen != prevlen)
-		zip_bl_tree[curlen].fc++;
-	    zip_bl_tree[zip_REP_3_6].fc++;
-	} else if(count <= 10)
-	    zip_bl_tree[zip_REPZ_3_10].fc++;
-	else
-	    zip_bl_tree[zip_REPZ_11_138].fc++;
-	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 a literal or distance tree in compressed form, using the codes in
-   * bl_tree.
-   */
-var zip_send_tree = function(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].dl;	// 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
-
-    /* tree[max_code+1].dl = -1; */  /* guard already set */
-    if(nextlen == 0) {
-      max_count = 138;
-      min_count = 3;
-    }
-
-    for(n = 0; n <= max_code; n++) {
-	curlen = nextlen;
-	nextlen = tree[n+1].dl;
-	if(++count < max_count && curlen == nextlen) {
-	    continue;
-	} else if(count < min_count) {
-	    do { zip_SEND_CODE(curlen, zip_bl_tree); } while(--count != 0);
-	} else if(curlen != 0) {
-	    if(curlen != prevlen) {
-		zip_SEND_CODE(curlen, zip_bl_tree);
-		count--;
-	    }
-	    // Assert(count >= 3 && count <= 6, " 3_6?");
-	    zip_SEND_CODE(zip_REP_3_6, zip_bl_tree);
-	    zip_send_bits(count - 3, 2);
-	} else if(count <= 10) {
-	    zip_SEND_CODE(zip_REPZ_3_10, zip_bl_tree);
-	    zip_send_bits(count-3, 3);
-	} else {
-	    zip_SEND_CODE(zip_REPZ_11_138, zip_bl_tree);
-	    zip_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;
+    }
+
+    return -1;
+  };
+}
+// Based on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray
+
+if (! Array.isArray) {
+    Array.isArray = function(obj) {
+        return Object.prototype.toString.call(obj) === "[object Array]";
+    };
+}
+
+// https://github.com/ttaubert/node-arraybuffer-slice
+// (c) 2013 Tim Taubert <tim@timtaubert.de>
+// arraybuffer-slice may be freely distributed under the MIT license.
+
+"use strict";
+
+if (typeof ArrayBuffer !== 'undefined' && !ArrayBuffer.prototype.slice) {
+  ArrayBuffer.prototype.slice = function (begin, end) {
+    begin = (begin|0) || 0;
+    var num = this.byteLength;
+    end = end === (void 0) ? num : (end|0);
+
+    // Handle negative values.
+    if (begin < 0) begin += num;
+    if (end < 0) end += num;
+
+    if (num === 0 || begin >= num || begin >= end) {
+      return new ArrayBuffer(0);
+    }
+
+    var length = Math.min(num - begin, end - begin);
+    var target = new ArrayBuffer(length);
+    var targetArray = new Uint8Array(target);
+    targetArray.set(new Uint8Array(this, begin, length));
+    return target;
+  };
+}
+/* xls.js (C) 2013-2014 SheetJS -- http://sheetjs.com */
+var XLS={};(function make_xls(XLS){XLS.version="0.7.1";var current_codepage=1252,current_cptable;if(typeof module!=="undefined"&&typeof require!=="undefined"){if(typeof cptable==="undefined")cptable=require("./dist/cpexcel");current_cptable=cptable[current_codepage]}function reset_cp(){set_cp(1252)}function set_cp(cp){current_codepage=cp;if(typeof cptable!=="undefined")current_cptable=cptable[cp]}var _getchar=function _gc1(x){return String.fromCharCode(x)};if(typeof cptable!=="undefined")_getchar=function _gc2(x){if(current_codepage===1200)return String.fromCharCode(x);return cptable.utils.decode(current_codepage,[x&255,x>>8])[0]};var has_buf=typeof Buffer!=="undefined";function new_buf(len){return new(has_buf?Buffer:Array)(len)}var Base64=function make_b64(){var map="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";return{decode:function b64_decode(input,utf8){var o="";var c1,c2,c3;var e1,e2,e3,e4;input=input.replace(/[^A-Za-z0-9\+\/\=]/g,"");for(var i=0;i<input.length;){e1=map.indexOf(input.charAt(i++));e2=map.indexOf(input.charAt(i++));e3=map.indexOf(input.charAt(i++));e4=map.indexOf(input.charAt(i++));c1=e1<<2|e2>>4;c2=(e2&15)<<4|e3>>2;c3=(e3&3)<<6|e4;o+=String.fromCharCode(c1);if(e3!=64){o+=String.fromCharCode(c2)}if(e4!=64){o+=String.fromCharCode(c3)}}return o}}}();function s2a(s){if(has_buf)return new Buffer(s,"binary");var w=s.split("").map(function(x){return x.charCodeAt(0)&255});return w}function readIEEE754(buf,idx,isLE,nl,ml){if(isLE===undefined)isLE=true;if(!nl)nl=8;if(!ml&&nl===8)ml=52;var e,m,el=nl*8-ml-1,eMax=(1<<el)-1,eBias=eMax>>1;var bits=-7,d=isLE?-1:1,i=isLE?nl-1:0,s=buf[idx+i];i+=d;e=s&(1<<-bits)-1;s>>>=-bits;bits+=el;for(;bits>0;e=e*256+buf[idx+i],i+=d,bits-=8);m=e&(1<<-bits)-1;e>>>=-bits;bits+=ml;for(;bits>0;m=m*256+buf[idx+i],i+=d,bits-=8);if(e===eMax)return m?NaN:(s?-1:1)*Infinity;else if(e===0)e=1-eBias;else{m=m+Math.pow(2,ml);e=e-eBias}return(s?-1:1)*m*Math.pow(2,e-ml)}var chr0=/\u0000/g,chr1=/[\u0001-\u0006]/;var __toBuffer,___toBuffer;__toBuffer=___toBuffer=function toBuffer_(bufs){var x=[];for(var i=0;i<bufs[0].length;++i){x.push.apply(x,bufs[0][i])}return x};var __utf16le,___utf16le;__utf16le=___utf16le=function utf16le_(b,s,e){var ss=[];for(var i=s;i<e;i+=2)ss.push(String.fromCharCode(__readUInt16LE(b,i)));return ss.join("")};var __hexlify,___hexlify;__hexlify=___hexlify=function hexlify_(b,s,l){return b.slice(s,s+l).map(function(x){return(x<16?"0":"")+x.toString(16)}).join("")};var __utf8,___utf8;__utf8=___utf8=function(b,s,e){var ss=[];for(var i=s;i<e;i++)ss.push(String.fromCharCode(__readUInt8(b,i)));return ss.join("")};var __lpstr,___lpstr;__lpstr=___lpstr=function lpstr_(b,i){var len=__readUInt32LE(b,i);return len>0?__utf8(b,i+4,i+4+len-1):""};var __lpwstr,___lpwstr;__lpwstr=___lpwstr=function lpwstr_(b,i){var len=2*__readUInt32LE(b,i);return len>0?__utf8(b,i+4,i+4+len-1):""};var __double,___double;__double=___double=function(b,idx){return readIEEE754(b,idx)};var bconcat=function(bufs){return[].concat.apply([],bufs)};if(typeof Buffer!=="undefined"){__utf16le=function utf16le_b(b,s,e){if(!Buffer.isBuffer(b))return ___utf16le(b,s,e);return b.toString("utf16le",s,e)};__hexlify=function(b,s,l){return Buffer.isBuffer(b)?b.toString("hex",s,s+l):___hexlify(b,s,l)};__lpstr=function lpstr_b(b,i){if(!Buffer.isBuffer(b))return ___lpstr(b,i);var len=b.readUInt32LE(i);return len>0?b.toString("utf8",i+4,i+4+len-1):""};__lpwstr=function lpwstr_b(b,i){if(!Buffer.isBuffer(b))return ___lpwstr(b,i);var len=2*b.readUInt32LE(i);return b.toString("utf16le",i+4,i+4+len-1)};__utf8=function utf8_b(s,e){return this.toString("utf8",s,e)};__toBuffer=function(bufs){return bufs[0].length>0&&Buffer.isBuffer(bufs[0][0])?Buffer.concat(bufs[0]):___toBuffer(bufs)};bconcat=function(bufs){return Buffer.isBuffer(bufs[0])?Buffer.concat(bufs):[].concat.apply([],bufs)};__double=function double_(b,i){if(Buffer.isBuffer(b))return b.readDoubleLE(i);return ___double(b,i)}}var __readUInt8=function(b,idx){return b[idx]};var __readUInt16LE=function(b,idx){return b[idx+1]*(1<<8)+b[idx]};var __readInt16LE=function(b,idx){var u=b[idx+1]*(1<<8)+b[idx];return u<32768?u:(65535-u+1)*-1};var __readUInt32LE=function(b,idx){return b[idx+3]*(1<<24)+(b[idx+2]<<16)+(b[idx+1]<<8)+b[idx]};var __readInt32LE=function(b,idx){return b[idx+3]<<24|b[idx+2]<<16|b[idx+1]<<8|b[idx]};var ___unhexlify=function(s){return s.match(/../g).map(function(x){return parseInt(x,16)})};var __unhexlify=typeof Buffer!=="undefined"?function(s){return Buffer.isBuffer(s)?new Buffer(s,"hex"):___unhexlify(s)}:___unhexlify;if(typeof cptable!=="undefined"){__utf16le=function(b,s,e){return cptable.utils.decode(1200,b.slice(s,e))};__utf8=function(b,s,e){return cptable.utils.decode(65001,b.slice(s,e))};__lpstr=function(b,i){var len=__readUInt32LE(b,i);return len>0?cptable.utils.decode(current_codepage,b.slice(i+4,i+4+len-1)):""};__lpwstr=function(b,i){var len=2*__readUInt32LE(b,i);return len>0?cptable.utils.decode(1200,b.slice(i+4,i+4+len-1)):""}}function ReadShift(size,t){var o,oI,oR,oo=[],w,vv,i,loc;switch(t){case"lpstr":o=__lpstr(this,this.l);size=5+o.length;break;case"lpwstr":o=__lpwstr(this,this.l);size=5+o.length;if(o[o.length-1]=="\x00")size+=2;break;case"cstr":size=0;o="";while((w=__readUInt8(this,this.l+size++))!==0)oo.push(_getchar(w));o=oo.join("");break;case"wstr":size=0;o="";while((w=__readUInt16LE(this,this.l+size))!==0){oo.push(_getchar(w));size+=2}size+=2;o=oo.join("");break;case"dbcs":o="";loc=this.l;for(i=0;i!=size;++i){if(this.lens&&this.lens.indexOf(loc)!==-1){w=__readUInt8(this,loc);this.l=loc+1;vv=ReadShift.call(this,size-i,w?"dbcs":"sbcs");return oo.join("")+vv}oo.push(_getchar(__readUInt16LE(this,loc)));loc+=2}o=oo.join("");size*=2;break;case"sbcs":o="";loc=this.l;for(i=0;i!=size;++i){if(this.lens&&this.lens.indexOf(loc)!==-1){w=__readUInt8(this,loc);this.l=loc+1;vv=ReadShift.call(this,size-i,w?"dbcs":"sbcs");return oo.join("")+vv}oo.push(_getchar(__readUInt8(this,loc)));loc+=1}o=oo.join("");break;case"utf8":o=__utf8(this,this.l,this.l+size);break;case"utf16le":size*=2;o=__utf16le(this,this.l,this.l+size);break;default:switch(size){case 1:oI=__readUInt8(this,this.l);this.l++;return oI;case 2:oI=t!=="i"?__readUInt16LE(this,this.l):__readInt16LE(this,this.l);this.l+=2;return oI;case 4:if(t==="i"||(this[this.l+3]&128)===0){oI=__readInt32LE(this,this.l);this.l+=4;return oI}else{oR=__readUInt32LE(this,this.l);this.l+=4;return oR}break;case 8:if(t==="f"){oR=__double(this,this.l);this.l+=8;return oR}case 16:o=__hexlify(this,this.l,size);break}}this.l+=size;return o}function CheckField(hexstr,fld){var m=__hexlify(this,this.l,hexstr.length>>1);if(m!==hexstr)throw fld+"Expected "+hexstr+" saw "+m;this.l+=hexstr.length>>1}function prep_blob(blob,pos){blob.l=pos;blob.read_shift=ReadShift;blob.chk=CheckField}var SSF={};var make_ssf=function make_ssf(SSF){SSF.version="0.8.1";function _strrev(x){var o="",i=x.length-1;while(i>=0)o+=x.charAt(i--);return o}function fill(c,l){var o="";while(o.length<l)o+=c;return o}function pad0(v,d){var t=""+v;return t.length>=d?t:fill("0",d-t.length)+t}function pad_(v,d){var t=""+v;return t.length>=d?t:fill(" ",d-t.length)+t}function rpad_(v,d){var t=""+v;return t.length>=d?t:t+fill(" ",d-t.length)}function pad0r1(v,d){var t=""+Math.round(v);return t.length>=d?t:fill("0",d-t.length)+t}function pad0r2(v,d){var t=""+v;return t.length>=d?t:fill("0",d-t.length)+t}var p2_32=Math.pow(2,32);function pad0r(v,d){if(v>p2_32||v<-p2_32)return pad0r1(v,d);var i=Math.round(v);return pad0r2(i,d)}function isgeneral(s,i){return s.length>=7+i&&(s.charCodeAt(i)|32)===103&&(s.charCodeAt(i+1)|32)===101&&(s.charCodeAt(i+2)|32)===110&&(s.charCodeAt(i+3)|32)===101&&(s.charCodeAt(i+4)|32)===114&&(s.charCodeAt(i+5)|32)===97&&(s.charCodeAt(i+6)|32)===108}var opts_fmt=[["date1904",0],["output",""],["WTF",false]];function fixopts(o){for(var y=0;y!=opts_fmt.length;++y)if(o[opts_fmt[y][0]]===undefined)o[opts_fmt[y][0]]=opts_fmt[y][1]}SSF.opts=opts_fmt;var table_fmt={0:"General",1:"0",2:"0.00",3:"#,##0",4:"#,##0.00",9:"0%",10:"0.00%",11:"0.00E+00",12:"# ?/?",13:"# ??/??",14:"m/d/yy",15:"d-mmm-yy",16:"d-mmm",17:"mmm-yy",18:"h:mm AM/PM",19:"h:mm:ss AM/PM",20:"h:mm",21:"h:mm:ss",22:"m/d/yy h:mm",37:"#,##0 ;(#,##0)",38:"#,##0 ;[Red](#,##0)",39:"#,##0.00;(#,##0.00)",40:"#,##0.00;[Red](#,##0.00)",45:"mm:ss",46:"[h]:mm:ss",47:"mmss.0",48:"##0.0E+0",49:"@",56:'"上午/下午 "hh"時"mm"分"ss"秒 "',65535:"General"};var days=[["Sun","Sunday"],["Mon","Monday"],["Tue","Tuesday"],["Wed","Wednesday"],["Thu","Thursday"],["Fri","Friday"],["Sat","Saturday"]];var months=[["J","Jan","January"],["F","Feb","February"],["M","Mar","March"],["A","Apr","April"],["M","May","May"],["J","Jun","June"],["J","Jul","July"],["A","Aug","August"],["S","Sep","September"],["O","Oct","October"],["N","Nov","November"],["D","Dec","December"]];function frac(x,D,mixed){var sgn=x<0?-1:1;var B=x*sgn;var P_2=0,P_1=1,P=0;var Q_2=1,Q_1=0,Q=0;var A=Math.floor(B);while(Q_1<D){A=Math.floor(B);P=A*P_1+P_2;Q=A*Q_1+Q_2;if(B-A<5e-10)break;B=1/(B-A);P_2=P_1;P_1=P;Q_2=Q_1;Q_1=Q}if(Q>D){Q=Q_1;P=P_1}if(Q>D){Q=Q_2;P=P_2}if(!mixed)return[0,sgn*P,Q];if(Q===0)throw"Unexpected state: "+P+" "+P_1+" "+P_2+" "+Q+" "+Q_1+" "+Q_2;var q=Math.floor(sgn*P/Q);return[q,sgn*P-q*Q,Q]}function general_fmt_int(v,opts){return""+v}SSF._general_int=general_fmt_int;var general_fmt_num=function make_general_fmt_num(){var gnr1=/\.(\d*[1-9])0+$/,gnr2=/\.0*$/,gnr4=/\.(\d*[1-9])0+/,gnr5=/\.0*[Ee]/,gnr6=/(E[+-])(\d)$/;function gfn2(v){var w=v<0?12:11;var o=gfn5(v.toFixed(12));if(o.length<=w)return o;o=v.toPrecision(10);if(o.length<=w)return o;return v.toExponential(5)}function gfn3(v){var o=v.toFixed(11).replace(gnr1,".$1");if(o.length>(v<0?12:11))o=v.toPrecision(6);return o}function gfn4(o){for(var i=0;i!=o.length;++i)if((o.charCodeAt(i)|32)===101)return o.replace(gnr4,".$1").replace(gnr5,"E").replace("e","E").replace(gnr6,"$10$2");return o}function gfn5(o){return o.indexOf(".")>-1?o.replace(gnr2,"").replace(gnr1,".$1"):o}return function general_fmt_num(v,opts){var V=Math.floor(Math.log(Math.abs(v))*Math.LOG10E),o;if(V>=-4&&V<=-1)o=v.toPrecision(10+V);else if(Math.abs(V)<=9)o=gfn2(v);else if(V===10)o=v.toFixed(10).substr(0,12);else o=gfn3(v);return gfn5(gfn4(o))}}();SSF._general_num=general_fmt_num;function general_fmt(v,opts){switch(typeof v){case"string":return v;case"boolean":return v?"TRUE":"FALSE";case"number":return(v|0)===v?general_fmt_int(v,opts):general_fmt_num(v,opts)}throw new Error("unsupported value in General format: "+v)}SSF._general=general_fmt;function fix_hijri(date,o){return 0}function parse_date_code(v,opts,b2){if(v>2958465||v<0)return null;var date=v|0,time=Math.floor(86400*(v-date)),dow=0;var dout=[];var out={D:date,T:time,u:86400*(v-date)-time,y:0,m:0,d:0,H:0,M:0,S:0,q:0};if(Math.abs(out.u)<1e-6)out.u=0;fixopts(opts!=null?opts:opts=[]);if(opts.date1904)date+=1462;if(out.u>.999){out.u=0;if(++time==86400){time=0;++date}}if(date===60){dout=b2?[1317,10,29]:[1900,2,29];dow=3}else if(date===0){dout=b2?[1317,8,29]:[1900,1,0];dow=6}else{if(date>60)--date;var d=new Date(1900,0,1);d.setDate(d.getDate()+date-1);dout=[d.getFullYear(),d.getMonth()+1,d.getDate()];dow=d.getDay();if(date<60)dow=(dow+6)%7;if(b2)dow=fix_hijri(d,dout)}out.y=dout[0];out.m=dout[1];out.d=dout[2];out.S=time%60;time=Math.floor(time/60);out.M=time%60;time=Math.floor(time/60);out.H=time;out.q=dow;return out}SSF.parse_date_code=parse_date_code;function write_date(type,fmt,val,ss0){var o="",ss=0,tt=0,y=val.y,out,outl=0;switch(type){case 98:y=val.y+543;case 121:switch(fmt.length){case 1:case 2:out=y%100;outl=2;break;default:out=y%1e4;outl=4;break}break;case 109:switch(fmt.length){case 1:case 2:out=val.m;outl=fmt.length;break;case 3:return months[val.m-1][1];case 5:return months[val.m-1][0];default:return months[val.m-1][2]}break;case 100:switch(fmt.length){case 1:case 2:out=val.d;outl=fmt.length;break;case 3:return days[val.q][0];default:return days[val.q][1]}break;case 104:switch(fmt.length){case 1:case 2:out=1+(val.H+11)%12;outl=fmt.length;break;default:throw"bad hour format: "+fmt}break;case 72:switch(fmt.length){case 1:case 2:out=val.H;outl=fmt.length;break;default:throw"bad hour format: "+fmt}break;case 77:switch(fmt.length){case 1:case 2:out=val.M;outl=fmt.length;break;default:throw"bad minute format: "+fmt}break;case 115:if(val.u===0)switch(fmt){case"s":case"ss":return pad0(val.S,fmt.length);case".0":case".00":case".000":}switch(fmt){case"s":case"ss":case".0":case".00":case".000":if(ss0>=2)tt=ss0===3?1e3:100;else tt=ss0===1?10:1;ss=Math.round(tt*(val.S+val.u));if(ss>=60*tt)ss=0;if(fmt==="s")return ss===0?"0":""+ss/tt;o=pad0(ss,2+ss0);if(fmt==="ss")return o.substr(0,2);return"."+o.substr(2,fmt.length-1);default:throw"bad second format: "+fmt}case 90:switch(fmt){case"[h]":case"[hh]":out=val.D*24+val.H;break;case"[m]":case"[mm]":out=(val.D*24+val.H)*60+val.M;break;case"[s]":case"[ss]":out=((val.D*24+val.H)*60+val.M)*60+Math.round(val.S+val.u);break;default:throw"bad abstime format: "+fmt}outl=fmt.length===3?1:2;break;case 101:out=y;outl=1}if(outl>0)return pad0(out,outl);else return""}function commaify(s){if(s.length<=3)return s;var j=s.length%3,o=s.substr(0,j);for(;j!=s.length;j+=3)o+=(o.length>0?",":"")+s.substr(j,3);return o}var write_num=function make_write_num(){var pct1=/%/g;function write_num_pct(type,fmt,val){var sfmt=fmt.replace(pct1,""),mul=fmt.length-sfmt.length;return write_num(type,sfmt,val*Math.pow(10,2*mul))+fill("%",mul)}function write_num_cm(type,fmt,val){var idx=fmt.length-1;while(fmt.charCodeAt(idx-1)===44)--idx;return write_num(type,fmt.substr(0,idx),val/Math.pow(10,3*(fmt.length-idx)))}function write_num_exp(fmt,val){var o;var idx=fmt.indexOf("E")-fmt.indexOf(".")-1;if(fmt.match(/^#+0.0E\+0$/)){var period=fmt.indexOf(".");if(period===-1)period=fmt.indexOf("E");var ee=Math.floor(Math.log(Math.abs(val))*Math.LOG10E)%period;if(ee<0)ee+=period;o=(val/Math.pow(10,ee)).toPrecision(idx+1+(period+ee)%period);if(o.indexOf("e")===-1){var fakee=Math.floor(Math.log(Math.abs(val))*Math.LOG10E);if(o.indexOf(".")===-1)o=o[0]+"."+o.substr(1)+"E+"+(fakee-o.length+ee);else o+="E+"+(fakee-ee);while(o.substr(0,2)==="0."){o=o[0]+o.substr(2,period)+"."+o.substr(2+period);o=o.replace(/^0+([1-9])/,"$1").replace(/^0+\./,"0.")}o=o.replace(/\+-/,"-")}o=o.replace(/^([+-]?)(\d*)\.(\d*)[Ee]/,function($$,$1,$2,$3){return $1+$2+$3.substr(0,(period+ee)%period)+"."+$3.substr(ee)+"E"})}else o=val.toExponential(idx);if(fmt.match(/E\+00$/)&&o.match(/e[+-]\d$/))o=o.substr(0,o.length-1)+"0"+o[o.length-1];if(fmt.match(/E\-/)&&o.match(/e\+/))o=o.replace(/e\+/,"e");return o.replace("e","E")}var frac1=/# (\?+)( ?)\/( ?)(\d+)/;function write_num_f1(r,aval,sign){var den=parseInt(r[4]),rr=Math.round(aval*den),base=Math.floor(rr/den);var myn=rr-base*den,myd=den;return sign+(base===0?"":""+base)+" "+(myn===0?fill(" ",r[1].length+1+r[4].length):pad_(myn,r[1].length)+r[2]+"/"+r[3]+pad0(myd,r[4].length))}function write_num_f2(r,aval,sign){return sign+(aval===0?"":""+aval)+fill(" ",r[1].length+2+r[4].length)}var dec1=/^#*0*\.(0+)/;var closeparen=/\).*[0#]/;var phone=/\(###\) ###\\?-####/;function hashq(str){var o="",cc;for(var i=0;i!=str.length;++i)switch(cc=str.charCodeAt(i)){case 35:break;case 63:o+=" ";break;case 48:o+="0";break;default:o+=String.fromCharCode(cc)}return o}function rnd(val,d){var dd=Math.pow(10,d);return""+Math.round(val*dd)/dd}function dec(val,d){return Math.round((val-Math.floor(val))*Math.pow(10,d))}function flr(val){if(val<2147483647&&val>-2147483648)return""+(val>=0?val|0:val-1|0);return""+Math.floor(val)}function write_num_flt(type,fmt,val){if(type.charCodeAt(0)===40&&!fmt.match(closeparen)){var ffmt=fmt.replace(/\( */,"").replace(/ \)/,"").replace(/\)/,"");if(val>=0)return write_num_flt("n",ffmt,val);return"("+write_num_flt("n",ffmt,-val)+")"}if(fmt.charCodeAt(fmt.length-1)===44)return write_num_cm(type,fmt,val);if(fmt.indexOf("%")!==-1)return write_num_pct(type,fmt,val);if(fmt.indexOf("E")!==-1)return write_num_exp(fmt,val);if(fmt.charCodeAt(0)===36)return"$"+write_num_flt(type,fmt.substr(fmt[1]==" "?2:1),val);var o,oo;var r,ri,ff,aval=Math.abs(val),sign=val<0?"-":"";if(fmt.match(/^00+$/))return sign+pad0r(aval,fmt.length);if(fmt.match(/^[#?]+$/)){o=pad0r(val,0);if(o==="0")o="";return o.length>fmt.length?o:hashq(fmt.substr(0,fmt.length-o.length))+o}if((r=fmt.match(frac1))!==null)return write_num_f1(r,aval,sign);if(fmt.match(/^#+0+$/)!==null)return sign+pad0r(aval,fmt.length-fmt.indexOf("0"));if((r=fmt.match(dec1))!==null){o=rnd(val,r[1].length).replace(/^([^\.]+)$/,"$1."+r[1]).replace(/\.$/,"."+r[1]).replace(/\.(\d*)$/,function($$,$1){return"."+$1+fill("0",r[1].length-$1.length)});return fmt.indexOf("0.")!==-1?o:o.replace(/^0\./,".")}fmt=fmt.replace(/^#+([0.])/,"$1");if((r=fmt.match(/^(0*)\.(#*)$/))!==null){return sign+rnd(aval,r[2].length).replace(/\.(\d*[1-9])0*$/,".$1").replace(/^(-?\d*)$/,"$1.").replace(/^0\./,r[1].length?"0.":".")}if((r=fmt.match(/^#,##0(\.?)$/))!==null)return sign+commaify(pad0r(aval,0));if((r=fmt.match(/^#,##0\.([#0]*0)$/))!==null){return val<0?"-"+write_num_flt(type,fmt,-val):commaify(""+Math.floor(val))+"."+pad0(dec(val,r[1].length),r[1].length)}if((r=fmt.match(/^#,#*,#0/))!==null)return write_num_flt(type,fmt.replace(/^#,#*,/,""),val);if((r=fmt.match(/^([0#]+)(\\?-([0#]+))+$/))!==null){o=_strrev(write_num_flt(type,fmt.replace(/[\\-]/g,""),val));ri=0;return _strrev(_strrev(fmt.replace(/\\/g,"")).replace(/[0#]/g,function(x){return ri<o.length?o[ri++]:x==="0"?"0":""}))}if(fmt.match(phone)!==null){o=write_num_flt(type,"##########",val);return"("+o.substr(0,3)+") "+o.substr(3,3)+"-"+o.substr(6)}var oa="";if((r=fmt.match(/^([#0?]+)( ?)\/( ?)([#0?]+)/))!==null){ri=Math.min(r[4].length,7);ff=frac(aval,Math.pow(10,ri)-1,false);o=""+sign;oa=write_num("n",r[1],ff[1]);if(oa[oa.length-1]==" ")oa=oa.substr(0,oa.length-1)+"0";o+=oa+r[2]+"/"+r[3];oa=rpad_(ff[2],ri);if(oa.length<r[4].length)oa=hashq(r[4].substr(r[4].length-oa.length))+oa;o+=oa;return o}if((r=fmt.match(/^# ([#0?]+)( ?)\/( ?)([#0?]+)/))!==null){ri=Math.min(Math.max(r[1].length,r[4].length),7);ff=frac(aval,Math.pow(10,ri)-1,true);return sign+(ff[0]||(ff[1]?"":"0"))+" "+(ff[1]?pad_(ff[1],ri)+r[2]+"/"+r[3]+rpad_(ff[2],ri):fill(" ",2*ri+1+r[2].length+r[3].length))}if((r=fmt.match(/^[#0?]+$/))!==null){o=pad0r(val,0);if(fmt.length<=o.length)return o;return hashq(fmt.substr(0,fmt.length-o.length))+o}if((r=fmt.match(/^([#0?]+)\.([#0]+)$/))!==null){o=""+val.toFixed(Math.min(r[2].length,10)).replace(/([^0])0+$/,"$1");ri=o.indexOf(".");var lres=fmt.indexOf(".")-ri,rres=fmt.length-o.length-lres;return hashq(fmt.substr(0,lres)+o+fmt.substr(fmt.length-rres))}if((r=fmt.match(/^00,000\.([#0]*0)$/))!==null){ri=dec(val,r[1].length);return val<0?"-"+write_num_flt(type,fmt,-val):commaify(flr(val)).replace(/^\d,\d{3}$/,"0$&").replace(/^\d*$/,function($$){return"00,"+($$.length<3?pad0(0,3-$$.length):"")+$$})+"."+pad0(ri,r[1].length)}switch(fmt){case"#,###":var x=commaify(pad0r(aval,0));return x!=="0"?sign+x:"";default:}throw new Error("unsupported format |"+fmt+"|")}function write_num_cm2(type,fmt,val){var idx=fmt.length-1;while(fmt.charCodeAt(idx-1)===44)--idx;return write_num(type,fmt.substr(0,idx),val/Math.pow(10,3*(fmt.length-idx)))}function write_num_pct2(type,fmt,val){var sfmt=fmt.replace(pct1,""),mul=fmt.length-sfmt.length;return write_num(type,sfmt,val*Math.pow(10,2*mul))+fill("%",mul)}function write_num_exp2(fmt,val){var o;var idx=fmt.indexOf("E")-fmt.indexOf(".")-1;if(fmt.match(/^#+0.0E\+0$/)){var period=fmt.indexOf(".");if(period===-1)period=fmt.indexOf("E");var ee=Math.floor(Math.log(Math.abs(val))*Math.LOG10E)%period;if(ee<0)ee+=period;o=(val/Math.pow(10,ee)).toPrecision(idx+1+(period+ee)%period);if(!o.match(/[Ee]/)){var fakee=Math.floor(Math.log(Math.abs(val))*Math.LOG10E);if(o.indexOf(".")===-1)o=o[0]+"."+o.substr(1)+"E+"+(fakee-o.length+ee);else o+="E+"+(fakee-ee);o=o.replace(/\+-/,"-")}o=o.replace(/^([+-]?)(\d*)\.(\d*)[Ee]/,function($$,$1,$2,$3){return $1+$2+$3.substr(0,(period+ee)%period)+"."+$3.substr(ee)+"E"})}else o=val.toExponential(idx);if(fmt.match(/E\+00$/)&&o.match(/e[+-]\d$/))o=o.substr(0,o.length-1)+"0"+o[o.length-1];if(fmt.match(/E\-/)&&o.match(/e\+/))o=o.replace(/e\+/,"e");return o.replace("e","E")}function write_num_int(type,fmt,val){if(type.charCodeAt(0)===40&&!fmt.match(closeparen)){var ffmt=fmt.replace(/\( */,"").replace(/ \)/,"").replace(/\)/,"");if(val>=0)return write_num_int("n",ffmt,val);return"("+write_num_int("n",ffmt,-val)+")"}if(fmt.charCodeAt(fmt.length-1)===44)return write_num_cm2(type,fmt,val);if(fmt.indexOf("%")!==-1)return write_num_pct2(type,fmt,val);if(fmt.indexOf("E")!==-1)return write_num_exp2(fmt,val);if(fmt.charCodeAt(0)===36)return"$"+write_num_int(type,fmt.substr(fmt[1]==" "?2:1),val);var o;var r,ri,ff,aval=Math.abs(val),sign=val<0?"-":"";if(fmt.match(/^00+$/))return sign+pad0(aval,fmt.length);if(fmt.match(/^[#?]+$/)){o=""+val;if(val===0)o="";return o.length>fmt.length?o:hashq(fmt.substr(0,fmt.length-o.length))+o}if((r=fmt.match(frac1))!==null)return write_num_f2(r,aval,sign);if(fmt.match(/^#+0+$/)!==null)return sign+pad0(aval,fmt.length-fmt.indexOf("0"));if((r=fmt.match(dec1))!==null){o=(""+val).replace(/^([^\.]+)$/,"$1."+r[1]).replace(/\.$/,"."+r[1]).replace(/\.(\d*)$/,function($$,$1){return"."+$1+fill("0",r[1].length-$1.length)});return fmt.indexOf("0.")!==-1?o:o.replace(/^0\./,".")}fmt=fmt.replace(/^#+([0.])/,"$1");if((r=fmt.match(/^(0*)\.(#*)$/))!==null){return sign+(""+aval).replace(/\.(\d*[1-9])0*$/,".$1").replace(/^(-?\d*)$/,"$1.").replace(/^0\./,r[1].length?"0.":".")}if((r=fmt.match(/^#,##0(\.?)$/))!==null)return sign+commaify(""+aval);if((r=fmt.match(/^#,##0\.([#0]*0)$/))!==null){return val<0?"-"+write_num_int(type,fmt,-val):commaify(""+val)+"."+fill("0",r[1].length)}if((r=fmt.match(/^#,#*,#0/))!==null)return write_num_int(type,fmt.replace(/^#,#*,/,""),val);if((r=fmt.match(/^([0#]+)(\\?-([0#]+))+$/))!==null){o=_strrev(write_num_int(type,fmt.replace(/[\\-]/g,""),val));ri=0;return _strrev(_strrev(fmt.replace(/\\/g,"")).replace(/[0#]/g,function(x){return ri<o.length?o[ri++]:x==="0"?"0":""}))}if(fmt.match(phone)!==null){o=write_num_int(type,"##########",val);return"("+o.substr(0,3)+") "+o.substr(3,3)+"-"+o.substr(6)}var oa="";if((r=fmt.match(/^([#0?]+)( ?)\/( ?)([#0?]+)/))!==null){ri=Math.min(r[4].length,7);ff=frac(aval,Math.pow(10,ri)-1,false);o=""+sign;oa=write_num("n",r[1],ff[1]);if(oa[oa.length-1]==" ")oa=oa.substr(0,oa.length-1)+"0";o+=oa+r[2]+"/"+r[3];oa=rpad_(ff[2],ri);if(oa.length<r[4].length)oa=hashq(r[4].substr(r[4].length-oa.length))+oa;o+=oa;return o}if((r=fmt.match(/^# ([#0?]+)( ?)\/( ?)([#0?]+)/))!==null){ri=Math.min(Math.max(r[1].length,r[4].length),7);ff=frac(aval,Math.pow(10,ri)-1,true);return sign+(ff[0]||(ff[1]?"":"0"))+" "+(ff[1]?pad_(ff[1],ri)+r[2]+"/"+r[3]+rpad_(ff[2],ri):fill(" ",2*ri+1+r[2].length+r[3].length))}if((r=fmt.match(/^[#0?]+$/))!==null){o=""+val;if(fmt.length<=o.length)return o;return hashq(fmt.substr(0,fmt.length-o.length))+o}if((r=fmt.match(/^([#0]+)\.([#0]+)$/))!==null){o=""+val.toFixed(Math.min(r[2].length,10)).replace(/([^0])0+$/,"$1");ri=o.indexOf(".");var lres=fmt.indexOf(".")-ri,rres=fmt.length-o.length-lres;return hashq(fmt.substr(0,lres)+o+fmt.substr(fmt.length-rres))}if((r=fmt.match(/^00,000\.([#0]*0)$/))!==null){return val<0?"-"+write_num_int(type,fmt,-val):commaify(""+val).replace(/^\d,\d{3}$/,"0$&").replace(/^\d*$/,function($$){return"00,"+($$.length<3?pad0(0,3-$$.length):"")+$$})+"."+pad0(0,r[1].length)}switch(fmt){case"#,###":var x=commaify(""+aval);return x!=="0"?sign+x:"";default:}throw new Error("unsupported format |"+fmt+"|")}return function write_num(type,fmt,val){return(val|0)===val?write_num_int(type,fmt,val):write_num_flt(type,fmt,val)}}();function split_fmt(fmt){var out=[];var in_str=false,cc;for(var i=0,j=0;i<fmt.length;++i)switch(cc=fmt.charCodeAt(i)){case 34:in_str=!in_str;break;case 95:case 42:case 92:++i;break;case 59:out[out.length]=fmt.substr(j,i-j);j=i+1}out[out.length]=fmt.substr(j);if(in_str===true)throw new Error("Format |"+fmt+"| unterminated string ");return out}SSF._split=split_fmt;var abstime=/\[[HhMmSs]*\]/;function eval_fmt(fmt,v,opts,flen){var out=[],o="",i=0,c="",lst="t",q,dt,j,cc;var hr="H";while(i<fmt.length){switch(c=fmt[i]){case"G":if(!isgeneral(fmt,i))throw new Error("unrecognized character "+c+" in "+fmt);out[out.length]={t:"G",v:"General"};i+=7;break;case'"':for(o="";(cc=fmt.charCodeAt(++i))!==34&&i<fmt.length;)o+=String.fromCharCode(cc);out[out.length]={t:"t",v:o};++i;break;case"\\":var w=fmt[++i],t=w==="("||w===")"?w:"t";out[out.length]={t:t,v:w};++i;break;case"_":out[out.length]={t:"t",v:" "};i+=2;break;case"@":out[out.length]={t:"T",v:v};++i;break;case"B":case"b":if(fmt[i+1]==="1"||fmt[i+1]==="2"){if(dt==null){dt=parse_date_code(v,opts,fmt[i+1]==="2");if(dt==null)return""}out[out.length]={t:"X",v:fmt.substr(i,2)};lst=c;i+=2;break}case"M":case"D":case"Y":case"H":case"S":case"E":c=c.toLowerCase();case"m":case"d":case"y":case"h":case"s":case"e":case"g":if(v<0)return"";if(dt==null){dt=parse_date_code(v,opts);if(dt==null)return""}o=c;while(++i<fmt.length&&fmt[i].toLowerCase()===c)o+=c;if(c==="m"&&lst.toLowerCase()==="h")c="M";if(c==="h")c=hr;out[out.length]={t:c,v:o};lst=c;break;case"A":q={t:c,v:"A"};if(dt==null)dt=parse_date_code(v,opts);if(fmt.substr(i,3)==="A/P"){if(dt!=null)q.v=dt.H>=12?"P":"A";q.t="T";hr="h";i+=3}else if(fmt.substr(i,5)==="AM/PM"){if(dt!=null)q.v=dt.H>=12?"PM":"AM";q.t="T";i+=5;hr="h"}else{q.t="t";++i}if(dt==null&&q.t==="T")return"";out[out.length]=q;lst=c;break;case"[":o=c;while(fmt[i++]!=="]"&&i<fmt.length)o+=fmt[i];if(o.substr(-1)!=="]")throw'unterminated "[" block: |'+o+"|";if(o.match(abstime)){if(dt==null){dt=parse_date_code(v,opts);if(dt==null)return""}out[out.length]={t:"Z",v:o.toLowerCase()}}else{o=""}break;case".":if(dt!=null){o=c;while((c=fmt[++i])==="0")o+=c;out[out.length]={t:"s",v:o};break}case"0":case"#":o=c;while("0#?.,E+-%".indexOf(c=fmt[++i])>-1||c=="\\"&&fmt[i+1]=="-"&&"0#".indexOf(fmt[i+2])>-1)o+=c;out[out.length]={t:"n",v:o};break;case"?":o=c;while(fmt[++i]===c)o+=c;q={t:c,v:o};out[out.length]=q;lst=c;break;case"*":++i;if(fmt[i]==" "||fmt[i]=="*")++i;break;case"(":case")":out[out.length]={t:flen===1?"t":c,v:c};++i;break;case"1":case"2":case"3":case"4":case"5":case"6":case"7":case"8":case"9":o=c;while("0123456789".indexOf(fmt[++i])>-1)o+=fmt[i];out[out.length]={t:"D",v:o};break;case" ":out[out.length]={t:c,v:c};++i;break;default:if(",$-+/():!^&'~{}<>=€acfijklopqrtuvwxz".indexOf(c)===-1)throw new Error("unrecognized character "+c+" in "+fmt);out[out.length]={t:"t",v:c};++i;break}}var bt=0,ss0=0,ssm;for(i=out.length-1,lst="t";i>=0;--i){switch(out[i].t){case"h":case"H":out[i].t=hr;lst="h";if(bt<1)bt=1;break;case"s":if(ssm=out[i].v.match(/\.0+$/))ss0=Math.max(ss0,ssm[0].length-1);if(bt<3)bt=3;case"d":case"y":case"M":case"e":lst=out[i].t;break;case"m":if(lst==="s"){out[i].t="M";if(bt<2)bt=2}break;case"X":if(out[i].v==="B2");break;case"Z":if(bt<1&&out[i].v.match(/[Hh]/))bt=1;if(bt<2&&out[i].v.match(/[Mm]/))bt=2;if(bt<3&&out[i].v.match(/[Ss]/))bt=3}}switch(bt){case 0:break;case 1:if(dt.u>=.5){dt.u=0;++dt.S}if(dt.S>=60){dt.S=0;++dt.M}if(dt.M>=60){dt.M=0;++dt.H}break;case 2:if(dt.u>=.5){dt.u=0;++dt.S}if(dt.S>=60){dt.S=0;++dt.M}break}var nstr="",jj;for(i=0;i<out.length;++i){switch(out[i].t){case"t":case"T":case" ":case"D":break;case"X":out[i]=undefined;break;case"d":case"m":case"y":case"h":case"H":case"M":case"s":case"e":case"b":case"Z":out[i].v=write_date(out[i].t.charCodeAt(0),out[i].v,dt,ss0);out[i].t="t";break;case"n":case"(":case"?":jj=i+1;while(out[jj]!=null&&((c=out[jj].t)==="?"||c==="D"||(c===" "||c==="t")&&out[jj+1]!=null&&(out[jj+1].t==="?"||out[jj+1].t==="t"&&out[jj+1].v==="/")||out[i].t==="("&&(c===" "||c==="n"||c===")")||c==="t"&&(out[jj].v==="/"||"$€".indexOf(out[jj].v)>-1||out[jj].v===" "&&out[jj+1]!=null&&out[jj+1].t=="?"))){out[i].v+=out[jj].v;out[jj]=undefined;++jj}nstr+=out[i].v;i=jj-1;break;case"G":out[i].t="t";out[i].v=general_fmt(v,opts);break}}var vv="",myv,ostr;if(nstr.length>0){myv=v<0&&nstr.charCodeAt(0)===45?-v:v;ostr=write_num(nstr.charCodeAt(0)===40?"(":"n",nstr,myv);jj=ostr.length-1;var decpt=out.length;for(i=0;i<out.length;++i)if(out[i]!=null&&out[i].v.indexOf(".")>-1){decpt=i;break}var lasti=out.length;if(decpt===out.length&&ostr.indexOf("E")===-1){for(i=out.length-1;i>=0;--i){if(out[i]==null||"n?(".indexOf(out[i].t)===-1)continue;if(jj>=out[i].v.length-1){jj-=out[i].v.length;out[i].v=ostr.substr(jj+1,out[i].v.length)}else if(jj<0)out[i].v="";else{out[i].v=ostr.substr(0,jj+1);jj=-1}out[i].t="t";lasti=i}if(jj>=0&&lasti<out.length)out[lasti].v=ostr.substr(0,jj+1)+out[lasti].v}else if(decpt!==out.length&&ostr.indexOf("E")===-1){jj=ostr.indexOf(".")-1;for(i=decpt;i>=0;--i){if(out[i]==null||"n?(".indexOf(out[i].t)===-1)continue;j=out[i].v.indexOf(".")>-1&&i===decpt?out[i].v.indexOf(".")-1:out[i].v.length-1;vv=out[i].v.substr(j+1);for(;j>=0;--j){if(jj>=0&&(out[i].v[j]==="0"||out[i].v[j]==="#"))vv=ostr[jj--]+vv}out[i].v=vv;out[i].t="t";lasti=i}if(jj>=0&&lasti<out.length)out[lasti].v=ostr.substr(0,jj+1)+out[lasti].v;jj=ostr.indexOf(".")+1;for(i=decpt;i<out.length;++i){if(out[i]==null||"n?(".indexOf(out[i].t)===-1&&i!==decpt)continue;j=out[i].v.indexOf(".")>-1&&i===decpt?out[i].v.indexOf(".")+1:0;vv=out[i].v.substr(0,j);for(;j<out[i].v.length;++j){if(jj<ostr.length)vv+=ostr[jj++]}out[i].v=vv;out[i].t="t";lasti=i}}}for(i=0;i<out.length;++i)if(out[i]!=null&&"n(?".indexOf(out[i].t)>-1){myv=flen>1&&v<0&&i>0&&out[i-1].v==="-"?-v:v;out[i].v=write_num(out[i].t,out[i].v,myv);out[i].t="t"}var retval="";for(i=0;i!==out.length;++i)if(out[i]!=null)retval+=out[i].v;return retval}SSF._eval=eval_fmt;var cfregex=/\[[=<>]/;var cfregex2=/\[([=<>]*)(-?\d+\.?\d*)\]/;function chkcond(v,rr){if(rr==null)return false;var thresh=parseFloat(rr[2]);switch(rr[1]){case"=":if(v==thresh)return true;break;case">":if(v>thresh)return true;break;case"<":if(v<thresh)return true;break;case"<>":if(v!=thresh)return true;break;case">=":if(v>=thresh)return true;break;case"<=":if(v<=thresh)return true;break}return false}function choose_fmt(f,v){var fmt=split_fmt(f);var l=fmt.length,lat=fmt[l-1].indexOf("@");if(l<4&&lat>-1)--l;if(fmt.length>4)throw"cannot find right format for |"+fmt+"|";if(typeof v!=="number")return[4,fmt.length===4||lat>-1?fmt[fmt.length-1]:"@"];switch(fmt.length){case 1:fmt=lat>-1?["General","General","General",fmt[0]]:[fmt[0],fmt[0],fmt[0],"@"];break;case 2:fmt=lat>-1?[fmt[0],fmt[0],fmt[0],fmt[1]]:[fmt[0],fmt[1],fmt[0],"@"];break;case 3:fmt=lat>-1?[fmt[0],fmt[1],fmt[0],fmt[2]]:[fmt[0],fmt[1],fmt[2],"@"];break;case 4:break}var ff=v>0?fmt[0]:v<0?fmt[1]:fmt[2];if(fmt[0].indexOf("[")===-1&&fmt[1].indexOf("[")===-1)return[l,ff];if(fmt[0].match(cfregex)!=null||fmt[1].match(cfregex)!=null){var m1=fmt[0].match(cfregex2);var m2=fmt[1].match(cfregex2);return chkcond(v,m1)?[l,fmt[0]]:chkcond(v,m2)?[l,fmt[1]]:[l,fmt[m1!=null&&m2!=null?2:1]]}return[l,ff]}function format(fmt,v,o){fixopts(o!=null?o:o=[]);var sfmt="";switch(typeof fmt){case"string":sfmt=fmt;break;case"number":sfmt=(o.table!=null?o.table:table_fmt)[fmt];break}if(isgeneral(sfmt,0))return general_fmt(v,o);var f=choose_fmt(sfmt,v);if(isgeneral(f[1]))return general_fmt(v,o);if(v===true)v="TRUE";else if(v===false)v="FALSE";else if(v===""||v==null)return"";return eval_fmt(f[1],v,o,f[0])}SSF._table=table_fmt;SSF.load=function load_entry(fmt,idx){table_fmt[idx]=fmt};SSF.format=format;SSF.get_table=function get_table(){return table_fmt};SSF.load_table=function load_table(tbl){for(var i=0;i!=392;++i)if(tbl[i]!==undefined)SSF.load(tbl[i],i)}};make_ssf(SSF);{var VT_EMPTY=0;var VT_NULL=1;var VT_I2=2;var VT_I4=3;var VT_R4=4;var VT_R8=5;var VT_CY=6;var VT_DATE=7;var VT_BSTR=8;var VT_ERROR=10;var VT_BOOL=11;var VT_VARIANT=12;var VT_DECIMAL=14;var VT_I1=16;var VT_UI1=17;var VT_UI2=18;var VT_UI4=19;var VT_I8=20;var VT_UI8=21;var VT_INT=22;var VT_UINT=23;var VT_LPSTR=30;var VT_LPWSTR=31;var VT_FILETIME=64;var VT_BLOB=65;var VT_STREAM=66;var VT_STORAGE=67;var VT_STREAMED_Object=68;
+var VT_STORED_Object=69;var VT_BLOB_Object=70;var VT_CF=71;var VT_CLSID=72;var VT_VERSIONED_STREAM=73;var VT_VECTOR=4096;var VT_ARRAY=8192;var VT_STRING=80;var VT_USTR=81;var VT_CUSTOM=[VT_STRING,VT_USTR]}var DocSummaryPIDDSI={1:{n:"CodePage",t:VT_I2},2:{n:"Category",t:VT_STRING},3:{n:"PresentationFormat",t:VT_STRING},4:{n:"ByteCount",t:VT_I4},5:{n:"LineCount",t:VT_I4},6:{n:"ParagraphCount",t:VT_I4},7:{n:"SlideCount",t:VT_I4},8:{n:"NoteCount",t:VT_I4},9:{n:"HiddenCount",t:VT_I4},10:{n:"MultimediaClipCount",t:VT_I4},11:{n:"Scale",t:VT_BOOL},12:{n:"HeadingPair",t:VT_VECTOR|VT_VARIANT},13:{n:"DocParts",t:VT_VECTOR|VT_LPSTR},14:{n:"Manager",t:VT_STRING},15:{n:"Company",t:VT_STRING},16:{n:"LinksDirty",t:VT_BOOL},17:{n:"CharacterCount",t:VT_I4},19:{n:"SharedDoc",t:VT_BOOL},22:{n:"HLinksChanged",t:VT_BOOL},23:{n:"AppVersion",t:VT_I4,p:"version"},26:{n:"ContentType",t:VT_STRING},27:{n:"ContentStatus",t:VT_STRING},28:{n:"Language",t:VT_STRING},29:{n:"Version",t:VT_STRING},255:{}};var SummaryPIDSI={1:{n:"CodePage",t:VT_I2},2:{n:"Title",t:VT_STRING},3:{n:"Subject",t:VT_STRING},4:{n:"Author",t:VT_STRING},5:{n:"Keywords",t:VT_STRING},6:{n:"Comments",t:VT_STRING},7:{n:"Template",t:VT_STRING},8:{n:"LastAuthor",t:VT_STRING},9:{n:"RevNumber",t:VT_STRING},10:{n:"EditTime",t:VT_FILETIME},11:{n:"LastPrinted",t:VT_FILETIME},12:{n:"CreatedDate",t:VT_FILETIME},13:{n:"ModifiedDate",t:VT_FILETIME},14:{n:"PageCount",t:VT_I4},15:{n:"WordCount",t:VT_I4},16:{n:"CharCount",t:VT_I4},17:{n:"Thumbnail",t:VT_CF},18:{n:"ApplicationName",t:VT_LPSTR},19:{n:"DocumentSecurity",t:VT_I4},255:{}};var SpecialProperties={2147483648:{n:"Locale",t:VT_UI4},2147483651:{n:"Behavior",t:VT_UI4},1768515945:{}};(function(){for(var y in SpecialProperties)if(SpecialProperties.hasOwnProperty(y))DocSummaryPIDDSI[y]=SummaryPIDSI[y]=SpecialProperties[y]})();function parse_FILETIME(blob){var dwLowDateTime=blob.read_shift(4),dwHighDateTime=blob.read_shift(4);return new Date((dwHighDateTime/1e7*Math.pow(2,32)+dwLowDateTime/1e7-11644473600)*1e3).toISOString().replace(/\.000/,"")}function parse_lpstr(blob,type,pad){var str=blob.read_shift(0,"lpstr");if(pad)blob.l+=4-(str.length+1&3)&3;return str}function parse_lpwstr(blob,type,pad){var str=blob.read_shift(0,"lpwstr");if(pad)blob.l+=4-(str.length+1&3)&3;return str}function parse_VtStringBase(blob,stringType,pad){if(stringType===31)return parse_lpwstr(blob);return parse_lpstr(blob,stringType,pad)}function parse_VtString(blob,t,pad){return parse_VtStringBase(blob,t,pad===false?0:4)}function parse_VtUnalignedString(blob,t){if(!t)throw new Error("dafuq?");return parse_VtStringBase(blob,t,0)}function parse_VtVecUnalignedLpstrValue(blob){var length=blob.read_shift(4);var ret=[];for(var i=0;i!=length;++i)ret[i]=blob.read_shift(0,"lpstr");return ret}function parse_VtVecUnalignedLpstr(blob){return parse_VtVecUnalignedLpstrValue(blob)}function parse_VtHeadingPair(blob){var headingString=parse_TypedPropertyValue(blob,VT_USTR);var headerParts=parse_TypedPropertyValue(blob,VT_I4);return[headingString,headerParts]}function parse_VtVecHeadingPairValue(blob){var cElements=blob.read_shift(4);var out=[];for(var i=0;i!=cElements/2;++i)out.push(parse_VtHeadingPair(blob));return out}function parse_VtVecHeadingPair(blob){return parse_VtVecHeadingPairValue(blob)}function parse_dictionary(blob,CodePage){var cnt=blob.read_shift(4);var dict={};for(var j=0;j!=cnt;++j){var pid=blob.read_shift(4);var len=blob.read_shift(4);dict[pid]=blob.read_shift(len,CodePage===1200?"utf16le":"utf8").replace(chr0,"").replace(chr1,"!")}if(blob.l&3)blob.l=blob.l>>2+1<<2;return dict}function parse_BLOB(blob){var size=blob.read_shift(4);var bytes=blob.slice(blob.l,blob.l+size);if(size&3>0)blob.l+=4-(size&3)&3;return bytes}function parse_ClipboardData(blob){var o={};o.Size=blob.read_shift(4);blob.l+=o.Size;return o}function parse_VtVector(blob,cb){}function parse_TypedPropertyValue(blob,type,_opts){var t=blob.read_shift(2),ret,opts=_opts||{};blob.l+=2;if(type!==VT_VARIANT)if(t!==type&&VT_CUSTOM.indexOf(type)===-1)throw new Error("Expected type "+type+" saw "+t);switch(type===VT_VARIANT?t:type){case 2:ret=blob.read_shift(2,"i");if(!opts.raw)blob.l+=2;return ret;case 3:ret=blob.read_shift(4,"i");return ret;case 11:return blob.read_shift(4)!==0;case 19:ret=blob.read_shift(4);return ret;case 30:return parse_lpstr(blob,t,4).replace(chr0,"");case 31:return parse_lpwstr(blob);case 64:return parse_FILETIME(blob);case 65:return parse_BLOB(blob);case 71:return parse_ClipboardData(blob);case 80:return parse_VtString(blob,t,!opts.raw&&4).replace(chr0,"");case 81:return parse_VtUnalignedString(blob,t,4).replace(chr0,"");case 4108:return parse_VtVecHeadingPair(blob);case 4126:return parse_VtVecUnalignedLpstr(blob);default:throw new Error("TypedPropertyValue unrecognized type "+type+" "+t)}}function parse_PropertySet(blob,PIDSI){var start_addr=blob.l;var size=blob.read_shift(4);var NumProps=blob.read_shift(4);var Props=[],i=0;var CodePage=0;var Dictionary=-1,DictObj;for(i=0;i!=NumProps;++i){var PropID=blob.read_shift(4);var Offset=blob.read_shift(4);Props[i]=[PropID,Offset+start_addr]}var PropH={};for(i=0;i!=NumProps;++i){if(blob.l!==Props[i][1]){var fail=true;if(i>0&&PIDSI)switch(PIDSI[Props[i-1][0]].t){case 2:if(blob.l+2===Props[i][1]){blob.l+=2;fail=false}break;case 80:if(blob.l<=Props[i][1]){blob.l=Props[i][1];fail=false}break;case 4108:if(blob.l<=Props[i][1]){blob.l=Props[i][1];fail=false}break}if(!PIDSI&&blob.l<=Props[i][1]){fail=false;blob.l=Props[i][1]}if(fail)throw new Error("Read Error: Expected address "+Props[i][1]+" at "+blob.l+" :"+i)}if(PIDSI){var piddsi=PIDSI[Props[i][0]];PropH[piddsi.n]=parse_TypedPropertyValue(blob,piddsi.t,{raw:true});if(piddsi.p==="version")PropH[piddsi.n]=String(PropH[piddsi.n]>>16)+"."+String(PropH[piddsi.n]&65535);if(piddsi.n=="CodePage")switch(PropH[piddsi.n]){case 0:PropH[piddsi.n]=1252;case 1e4:case 1252:case 874:case 1250:case 1251:case 1253:case 1254:case 1255:case 1256:case 1257:case 1258:case 932:case 936:case 949:case 950:case 1200:case 1201:case 65e3:case-536:case 65001:case-535:set_cp(CodePage=PropH[piddsi.n]);break;default:throw new Error("Unsupported CodePage: "+PropH[piddsi.n])}}else{if(Props[i][0]===1){CodePage=PropH.CodePage=parse_TypedPropertyValue(blob,VT_I2);set_cp(CodePage);if(Dictionary!==-1){var oldpos=blob.l;blob.l=Props[Dictionary][1];DictObj=parse_dictionary(blob,CodePage);blob.l=oldpos}}else if(Props[i][0]===0){if(CodePage===0){Dictionary=i;blob.l=Props[i+1][1];continue}DictObj=parse_dictionary(blob,CodePage)}else{var name=DictObj[Props[i][0]];var val;switch(blob[blob.l]){case 65:blob.l+=4;val=parse_BLOB(blob);break;case 30:blob.l+=4;val=parse_VtString(blob,blob[blob.l-4]);break;case 31:blob.l+=4;val=parse_VtString(blob,blob[blob.l-4]);break;case 3:blob.l+=4;val=blob.read_shift(4,"i");break;case 19:blob.l+=4;val=blob.read_shift(4);break;case 5:blob.l+=4;val=blob.read_shift(8,"f");break;case 11:blob.l+=4;val=parsebool(blob,4);break;case 64:blob.l+=4;val=new Date(parse_FILETIME(blob));break;default:throw new Error("unparsed value: "+blob[blob.l])}PropH[name]=val}}}blob.l=start_addr+size;return PropH}function parse_PropertySetStream(file,PIDSI){var blob=file.content;prep_blob(blob,0);var NumSets,FMTID0,FMTID1,Offset0,Offset1;blob.chk("feff","Byte Order: ");var vers=blob.read_shift(2);var SystemIdentifier=blob.read_shift(4);blob.chk(CFB.utils.consts.HEADER_CLSID,"CLSID: ");NumSets=blob.read_shift(4);if(NumSets!==1&&NumSets!==2)throw"Unrecognized #Sets: "+NumSets;FMTID0=blob.read_shift(16);Offset0=blob.read_shift(4);if(NumSets===1&&Offset0!==blob.l)throw"Length mismatch";else if(NumSets===2){FMTID1=blob.read_shift(16);Offset1=blob.read_shift(4)}var PSet0=parse_PropertySet(blob,PIDSI);var rval={SystemIdentifier:SystemIdentifier};for(var y in PSet0)rval[y]=PSet0[y];rval.FMTID=FMTID0;if(NumSets===1)return rval;if(blob.l!==Offset1)throw"Length mismatch 2: "+blob.l+" !== "+Offset1;var PSet1;try{PSet1=parse_PropertySet(blob,null)}catch(e){}for(y in PSet1)rval[y]=PSet1[y];rval.FMTID=[FMTID0,FMTID1];return rval}var DO_NOT_EXPORT_CFB=true;var CFB=function _CFB(){var exports={};exports.version="0.10.0";function parse(file){var mver=3;var ssz=512;var nmfs=0;var ndfs=0;var dir_start=0;var minifat_start=0;var difat_start=0;var fat_addrs=[];var blob=file.slice(0,512);prep_blob(blob,0);mver=check_get_mver(blob);switch(mver){case 3:ssz=512;break;case 4:ssz=4096;break;default:throw"Major Version: Expected 3 or 4 saw "+mver}if(ssz!==512){blob=file.slice(0,ssz);prep_blob(blob,28)}var header=file.slice(0,ssz);check_shifts(blob,mver);var nds=blob.read_shift(4,"i");if(mver===3&&nds!==0)throw"# Directory Sectors: Expected 0 saw "+nds;blob.l+=4;dir_start=blob.read_shift(4,"i");blob.l+=4;blob.chk("00100000","Mini Stream Cutoff Size: ");minifat_start=blob.read_shift(4,"i");nmfs=blob.read_shift(4,"i");difat_start=blob.read_shift(4,"i");ndfs=blob.read_shift(4,"i");for(var q,j=0;j<109;++j){q=blob.read_shift(4,"i");if(q<0)break;fat_addrs[j]=q}var sectors=sectorify(file,ssz);sleuth_fat(difat_start,ndfs,sectors,ssz,fat_addrs);var sector_list=make_sector_list(sectors,dir_start,fat_addrs,ssz);sector_list[dir_start].name="!Directory";if(nmfs>0&&minifat_start!==ENDOFCHAIN)sector_list[minifat_start].name="!MiniFAT";sector_list[fat_addrs[0]].name="!FAT";var files={},Paths=[],FileIndex=[],FullPaths=[],FullPathDir={};read_directory(dir_start,sector_list,sectors,Paths,nmfs,files,FileIndex);build_full_paths(FileIndex,FullPathDir,FullPaths,Paths);var root_name=Paths.shift();Paths.root=root_name;var find_path=make_find_path(FullPaths,Paths,FileIndex,files,root_name);return{raw:{header:header,sectors:sectors},FileIndex:FileIndex,FullPaths:FullPaths,FullPathDir:FullPathDir,find:find_path}}function check_get_mver(blob){blob.chk(HEADER_SIGNATURE,"Header Signature: ");blob.chk(HEADER_CLSID,"CLSID: ");blob.l+=2;return blob.read_shift(2,"u")}function check_shifts(blob,mver){var shift=9;blob.chk("feff","Byte Order: ");switch(shift=blob.read_shift(2)){case 9:if(mver!==3)throw"MajorVersion/SectorShift Mismatch";break;case 12:if(mver!==4)throw"MajorVersion/SectorShift Mismatch";break;default:throw"Sector Shift: Expected 9 or 12 saw "+shift}blob.chk("0600","Mini Sector Shift: ");blob.chk("000000000000","Reserved: ")}function sectorify(file,ssz){var nsectors=Math.ceil(file.length/ssz)-1;var sectors=new Array(nsectors);for(var i=1;i<nsectors;++i)sectors[i-1]=file.slice(i*ssz,(i+1)*ssz);sectors[nsectors-1]=file.slice(nsectors*ssz);return sectors}function build_full_paths(FI,FPD,FP,Paths){var i=0,L=0,R=0,C=0,j=0,pl=Paths.length;var dad=new Array(pl),q=new Array(pl);for(;i<pl;++i){dad[i]=q[i]=i;FP[i]=Paths[i]}for(;j<q.length;++j){i=q[j];L=FI[i].L;R=FI[i].R;C=FI[i].C;if(dad[i]===i){if(L!==-1&&dad[L]!==L)dad[i]=dad[L];if(R!==-1&&dad[R]!==R)dad[i]=dad[R]}if(C!==-1)dad[C]=i;if(L!==-1){dad[L]=dad[i];q.push(L)}if(R!==-1){dad[R]=dad[i];q.push(R)}}for(i=1;i!==pl;++i)if(dad[i]===i){if(R!==-1&&dad[R]!==R)dad[i]=dad[R];else if(L!==-1&&dad[L]!==L)dad[i]=dad[L]}for(i=1;i<pl;++i){if(FI[i].type===0)continue;j=dad[i];if(j===0)FP[i]=FP[0]+"/"+FP[i];else while(j!==0){FP[i]=FP[j]+"/"+FP[i];j=dad[j]}dad[i]=0}FP[0]+="/";for(i=1;i<pl;++i){if(FI[i].type!==2)FP[i]+="/";FPD[FP[i]]=FI[i]}}function make_find_path(FullPaths,Paths,FileIndex,files,root_name){var UCFullPaths=new Array(FullPaths.length);var UCPaths=new Array(Paths.length),i;for(i=0;i<FullPaths.length;++i)UCFullPaths[i]=FullPaths[i].toUpperCase();for(i=0;i<Paths.length;++i)UCPaths[i]=Paths[i].toUpperCase();return function find_path(path){var k;if(path.charCodeAt(0)===47){k=true;path=root_name+path}else k=path.indexOf("/")!==-1;var UCPath=path.toUpperCase();var w=k===true?UCFullPaths.indexOf(UCPath):UCPaths.indexOf(UCPath);if(w===-1)return null;return k===true?FileIndex[w]:files[Paths[w]]}}function sleuth_fat(idx,cnt,sectors,ssz,fat_addrs){var q;if(idx===ENDOFCHAIN){if(cnt!==0)throw"DIFAT chain shorter than expected"}else if(idx!==-1){var sector=sectors[idx],m=(ssz>>>2)-1;for(var i=0;i<m;++i){if((q=__readInt32LE(sector,i*4))===ENDOFCHAIN)break;fat_addrs.push(q)}sleuth_fat(__readInt32LE(sector,ssz-4),cnt-1,sectors,ssz,fat_addrs)}}function make_sector_list(sectors,dir_start,fat_addrs,ssz){var sl=sectors.length,sector_list=new Array(sl);var chkd=new Array(sl),buf,buf_chain;var modulus=ssz-1,i,j,k,jj;for(i=0;i<sl;++i){buf=[];k=i+dir_start;if(k>=sl)k-=sl;if(chkd[k]===true)continue;buf_chain=[];for(j=k;j>=0;){chkd[j]=true;buf[buf.length]=j;buf_chain.push(sectors[j]);var addr=fat_addrs[Math.floor(j*4/ssz)];jj=j*4&modulus;if(ssz<4+jj)throw"FAT boundary crossed: "+j+" 4 "+ssz;j=__readInt32LE(sectors[addr],jj)}sector_list[k]={nodes:buf,data:__toBuffer([buf_chain])}}return sector_list}function read_directory(dir_start,sector_list,sectors,Paths,nmfs,files,FileIndex){var blob;var minifat_store=0,pl=Paths.length?2:0;var sector=sector_list[dir_start].data;var i=0,namelen=0,name,o,ctime,mtime;for(;i<sector.length;i+=128){blob=sector.slice(i,i+128);prep_blob(blob,64);namelen=blob.read_shift(2);if(namelen===0)continue;name=__utf16le(blob,0,namelen-pl).replace(chr0,"").replace(chr1,"!");Paths.push(name);o={name:name,type:blob.read_shift(1),color:blob.read_shift(1),L:blob.read_shift(4,"i"),R:blob.read_shift(4,"i"),C:blob.read_shift(4,"i"),clsid:blob.read_shift(16),state:blob.read_shift(4,"i")};ctime=blob.read_shift(2)+blob.read_shift(2)+blob.read_shift(2)+blob.read_shift(2);if(ctime!==0){o.ctime=ctime;o.ct=read_date(blob,blob.l-8)}mtime=blob.read_shift(2)+blob.read_shift(2)+blob.read_shift(2)+blob.read_shift(2);if(mtime!==0){o.mtime=mtime;o.mt=read_date(blob,blob.l-8)}o.start=blob.read_shift(4,"i");o.size=blob.read_shift(4,"i");if(o.type===5){minifat_store=o.start;if(nmfs>0&&minifat_store!==ENDOFCHAIN)sector_list[minifat_store].name="!StreamData"}else if(o.size>=4096){o.storage="fat";if(sector_list[o.start]===undefined)if((o.start+=dir_start)>=sectors.length)o.start-=sectors.length;sector_list[o.start].name=o.name;o.content=sector_list[o.start].data.slice(0,o.size);prep_blob(o.content,0)}else{o.storage="minifat";if(minifat_store!==ENDOFCHAIN&&o.start!==ENDOFCHAIN){o.content=sector_list[minifat_store].data.slice(o.start*MSSZ,o.start*MSSZ+o.size);prep_blob(o.content,0)}}files[name]=o;FileIndex.push(o)}}function read_date(blob,offset){return new Date((__readUInt32LE(blob,offset+4)/1e7*Math.pow(2,32)+__readUInt32LE(blob,offset)/1e7-11644473600)*1e3)}var fs;function readFileSync(filename){if(fs===undefined)fs=require("fs");return parse(fs.readFileSync(filename))}function readSync(blob,options){switch(options!==undefined&&options.type!==undefined?options.type:"base64"){case"file":return readFileSync(blob);case"base64":return parse(s2a(Base64.decode(blob)));case"binary":return parse(s2a(blob))}return parse(blob)}var MSSZ=64;var ENDOFCHAIN=-2;var HEADER_SIGNATURE="d0cf11e0a1b11ae1";var HEADER_CLSID="00000000000000000000000000000000";var consts={MAXREGSECT:-6,DIFSECT:-4,FATSECT:-3,ENDOFCHAIN:ENDOFCHAIN,FREESECT:-1,HEADER_SIGNATURE:HEADER_SIGNATURE,HEADER_MINOR_VERSION:"3e00",MAXREGSID:-6,NOSTREAM:-1,HEADER_CLSID:HEADER_CLSID,EntryTypes:["unknown","storage","stream","lockbytes","property","root"]};exports.read=readSync;exports.parse=parse;exports.utils={ReadShift:ReadShift,CheckField:CheckField,prep_blob:prep_blob,bconcat:bconcat,consts:consts};return exports}();if(typeof require!=="undefined"&&typeof module!=="undefined"&&typeof DO_NOT_EXPORT_CFB==="undefined"){module.exports=CFB}function parsenoop(blob,length){blob.read_shift(length);return}function parsenoop2(blob,length){blob.read_shift(length);return null}function parslurp(blob,length,cb){var arr=[],target=blob.l+length;while(blob.l<target)arr.push(cb(blob,target-blob.l));if(target!==blob.l)throw new Error("Slurp error");return arr}function parslurp2(blob,length,cb){var arr=[],target=blob.l+length,len=blob.read_shift(2);while(len--!==0)arr.push(cb(blob,target-blob.l));if(target!==blob.l)throw new Error("Slurp error");return arr}function parsebool(blob,length){return blob.read_shift(length)===1}function parseuint16(blob){return blob.read_shift(2,"u")}function parseuint16a(blob,length){return parslurp(blob,length,parseuint16)}var parse_Boolean=parsebool;function parse_Bes(blob){var v=blob.read_shift(1),t=blob.read_shift(1);return t===1?BERR[v]:v===1}function parse_ShortXLUnicodeString(blob,length,opts){var cch=blob.read_shift(1);var width=1,encoding="sbcs";if(opts===undefined||opts.biff!==5){var fHighByte=blob.read_shift(1);if(fHighByte){width=2;encoding="dbcs"}}return cch?blob.read_shift(cch,encoding):""}function parse_XLUnicodeRichExtendedString(blob){var cch=blob.read_shift(2),flags=blob.read_shift(1);var fHighByte=flags&1,fExtSt=flags&4,fRichSt=flags&8;var width=1+(flags&1);var cRun,cbExtRst;var z={};if(fRichSt)cRun=blob.read_shift(2);if(fExtSt)cbExtRst=blob.read_shift(4);var encoding=flags&1?"dbcs":"sbcs";var msg=cch===0?"":blob.read_shift(cch,encoding);if(fRichSt)blob.l+=4*cRun;if(fExtSt)blob.l+=cbExtRst;z.t=msg;if(!fRichSt){z.raw="<t>"+z.t+"</t>";z.r=z.t}return z}function parse_XLUnicodeStringNoCch(blob,cch,opts){var retval;var fHighByte=blob.read_shift(1);if(fHighByte===0){retval=blob.read_shift(cch,"sbcs")}else{retval=blob.read_shift(cch,"dbcs")}return retval}function parse_XLUnicodeString(blob,length,opts){var cch=blob.read_shift(opts!==undefined&&opts.biff===5?1:2);if(cch===0){blob.l++;return""}return parse_XLUnicodeStringNoCch(blob,cch,opts)}function parse_XLUnicodeString2(blob,length,opts){if(opts.biff!==5)return parse_XLUnicodeString(blob,length,opts);var cch=blob.read_shift(1);if(cch===0){blob.l++;return""}return blob.read_shift(cch,"sbcs")}function parse_Xnum(blob){return blob.read_shift(8,"f")}var parse_ControlInfo=parsenoop;var parse_URLMoniker=function(blob,length){var len=blob.read_shift(4),start=blob.l;var extra=false;if(len>24){blob.l+=len-24;if(blob.read_shift(16)==="795881f43b1d7f48af2c825dc4852763")extra=true;blob.l=start}var url=blob.read_shift((extra?len-24:len)>>1,"utf16le").replace(chr0,"");if(extra)blob.l+=24;return url};var parse_FileMoniker=function(blob,length){var cAnti=blob.read_shift(2);var ansiLength=blob.read_shift(4);var ansiPath=blob.read_shift(ansiLength,"cstr");var endServer=blob.read_shift(2);var versionNumber=blob.read_shift(2);var cbUnicodePathSize=blob.read_shift(4);if(cbUnicodePathSize===0)return ansiPath.replace(/\\/g,"/");var cbUnicodePathBytes=blob.read_shift(4);var usKeyValue=blob.read_shift(2);var unicodePath=blob.read_shift(cbUnicodePathBytes>>1,"utf16le").replace(chr0,"");return unicodePath};var parse_HyperlinkMoniker=function(blob,length){var clsid=blob.read_shift(16);length-=16;switch(clsid){case"e0c9ea79f9bace118c8200aa004ba90b":return parse_URLMoniker(blob,length);case"0303000000000000c000000000000046":return parse_FileMoniker(blob,length);default:throw"unsupported moniker "+clsid}};var parse_HyperlinkString=function(blob,length){var len=blob.read_shift(4);var o=blob.read_shift(len,"utf16le").replace(chr0,"");return o};var parse_Hyperlink=function(blob,length){var end=blob.l+length;var sVer=blob.read_shift(4);if(sVer!==2)throw new Error("Unrecognized streamVersion: "+sVer);var flags=blob.read_shift(2);blob.l+=2;var displayName,targetFrameName,moniker,oleMoniker,location,guid,fileTime;if(flags&16)displayName=parse_HyperlinkString(blob,end-blob.l);if(flags&128)targetFrameName=parse_HyperlinkString(blob,end-blob.l);if((flags&257)===257)moniker=parse_HyperlinkString(blob,end-blob.l);if((flags&257)===1)oleMoniker=parse_HyperlinkMoniker(blob,end-blob.l);if(flags&8)location=parse_HyperlinkString(blob,end-blob.l);if(flags&32)guid=blob.read_shift(16);if(flags&64)fileTime=parse_FILETIME(blob,8);blob.l=end;var target=targetFrameName||moniker||oleMoniker;if(location)target+="#"+location;return{Target:target}};function isval(x){return x!==undefined&&x!==null}function keys(o){return Object.keys(o)}function evert(obj,arr){var o={};var K=keys(obj);for(var i=0;i<K.length;++i){var k=K[i];if(!arr)o[obj[k]]=k;else(o[obj[k]]=o[obj[k]]||[]).push(k)}return o}function parse_Cell(blob,length){var rw=blob.read_shift(2);var col=blob.read_shift(2);var ixfe=blob.read_shift(2);return{r:rw,c:col,ixfe:ixfe}}function parse_frtHeader(blob){var rt=blob.read_shift(2);var flags=blob.read_shift(2);blob.l+=8;return{type:rt,flags:flags}}function parse_OptXLUnicodeString(blob,length,opts){return length===0?"":parse_XLUnicodeString2(blob,length,opts)}var HIDEOBJENUM=["SHOWALL","SHOWPLACEHOLDER","HIDEALL"];var parse_HideObjEnum=parseuint16;function parse_XTI(blob,length){var iSupBook=blob.read_shift(2),itabFirst=blob.read_shift(2,"i"),itabLast=blob.read_shift(2,"i");return[iSupBook,itabFirst,itabLast]}function parse_RkNumber(blob){var b=blob.slice(blob.l,blob.l+4);var fX100=b[0]&1,fInt=b[0]&2;blob.l+=4;b[0]&=~3;var RK=fInt===0?__double([0,0,0,0,b[0],b[1],b[2],b[3]],0):__readInt32LE(b,0)>>2;return fX100?RK/100:RK}function parse_RkRec(blob,length){var ixfe=blob.read_shift(2);var RK=parse_RkNumber(blob);return[ixfe,RK]}function parse_AddinUdf(blob,length){blob.l+=4;length-=4;var l=blob.l+length;var udfName=parse_ShortXLUnicodeString(blob,length);var cb=blob.read_shift(2);l-=blob.l;if(cb!==l)throw"Malformed AddinUdf: padding = "+l+" != "+cb;blob.l+=cb;return udfName}function parse_Ref8U(blob,length){var rwFirst=blob.read_shift(2);var rwLast=blob.read_shift(2);var colFirst=blob.read_shift(2);var colLast=blob.read_shift(2);return{s:{c:colFirst,r:rwFirst},e:{c:colLast,r:rwLast}}}function parse_RefU(blob,length){var rwFirst=blob.read_shift(2);var rwLast=blob.read_shift(2);var colFirst=blob.read_shift(1);var colLast=blob.read_shift(1);return{s:{c:colFirst,r:rwFirst},e:{c:colLast,r:rwLast}}}var parse_Ref=parse_RefU;function parse_FtCmo(blob,length){blob.l+=4;var ot=blob.read_shift(2);var id=blob.read_shift(2);var flags=blob.read_shift(2);blob.l+=12;return[id,ot,flags]}function parse_FtNts(blob,length){var out={};blob.l+=4;blob.l+=16;out.fSharedNote=blob.read_shift(2);blob.l+=4;return out}function parse_FtCf(blob,length){var out={};blob.l+=4;blob.cf=blob.read_shift(2);return out}var FtTab={21:parse_FtCmo,19:parsenoop,18:function(blob,length){blob.l+=12},17:function(blob,length){blob.l+=8},16:parsenoop,15:parsenoop,13:parse_FtNts,12:function(blob,length){blob.l+=24},11:function(blob,length){blob.l+=10},10:function(blob,length){blob.l+=16},9:parsenoop,8:function(blob,length){blob.l+=6},7:parse_FtCf,6:function(blob,length){blob.l+=6},4:parsenoop,0:function(blob,length){blob.l+=4}};function parse_FtArray(blob,length,ot){var s=blob.l;var fts=[];while(blob.l<s+length){var ft=blob.read_shift(2);blob.l-=2;try{fts.push(FtTab[ft](blob,s+length-blob.l))}catch(e){blob.l=s+length;return fts}}if(blob.l!=s+length)blob.l=s+length;return fts}var parse_FontIndex=parseuint16;function parse_BOF(blob,length){var o={};o.BIFFVer=blob.read_shift(2);length-=2;if(o.BIFFVer!==1536&&o.BIFFVer!==1280)throw"Unexpected BIFF Ver "+o.BIFFVer;blob.read_shift(length);return o}function parse_InterfaceHdr(blob,length){if(length===0)return 1200;var q;if((q=blob.read_shift(2))!==1200)throw"InterfaceHdr codePage "+q;return 1200}function parse_WriteAccess(blob,length,opts){if(opts.enc){blob.l+=length;return""}var l=blob.l;var UserName=parse_XLUnicodeString(blob,0,opts);blob.read_shift(length+l-blob.l);return UserName}function parse_BoundSheet8(blob,length,opts){var pos=blob.read_shift(4);var hidden=blob.read_shift(1)>>6;var dt=blob.read_shift(1);switch(dt){case 0:dt="Worksheet";break;case 1:dt="Macrosheet";break;case 2:dt="Chartsheet";break;case 6:dt="VBAModule";break}var name=parse_ShortXLUnicodeString(blob,0,opts);return{pos:pos,hs:hidden,dt:dt,name:name}}function parse_SST(blob,length){var cnt=blob.read_shift(4);var ucnt=blob.read_shift(4);var strs=[];for(var i=0;i!=ucnt;++i){strs.push(parse_XLUnicodeRichExtendedString(blob))}strs.Count=cnt;strs.Unique=ucnt;return strs}function parse_ExtSST(blob,length){var extsst={};extsst.dsst=blob.read_shift(2);blob.l+=length-2;return extsst}function parse_Row(blob,length){var rw=blob.read_shift(2),col=blob.read_shift(2),Col=blob.read_shift(2),rht=blob.read_shift(2);blob.read_shift(4);var flags=blob.read_shift(1);blob.read_shift(1);blob.read_shift(2);return{r:rw,c:col,cnt:Col-col}}function parse_ForceFullCalculation(blob,length){var header=parse_frtHeader(blob);if(header.type!=2211)throw"Invalid Future Record "+header.type;var fullcalc=blob.read_shift(4);return fullcalc!==0}var parse_CompressPictures=parsenoop2;function parse_RecalcId(blob,length){blob.read_shift(2);return blob.read_shift(4)}function parse_DefaultRowHeight(blob,length){var f=blob.read_shift(2),miyRw;miyRw=blob.read_shift(2);var fl={Unsynced:f&1,DyZero:(f&2)>>1,ExAsc:(f&4)>>2,ExDsc:(f&8)>>3};return[fl,miyRw]}function parse_Window1(blob,length){var xWn=blob.read_shift(2),yWn=blob.read_shift(2),dxWn=blob.read_shift(2),dyWn=blob.read_shift(2);var flags=blob.read_shift(2),iTabCur=blob.read_shift(2),iTabFirst=blob.read_shift(2);var ctabSel=blob.read_shift(2),wTabRatio=blob.read_shift(2);return{Pos:[xWn,yWn],Dim:[dxWn,dyWn],Flags:flags,CurTab:iTabCur,FirstTab:iTabFirst,Selected:ctabSel,TabRatio:wTabRatio}}function parse_Font(blob,length,opts){blob.l+=14;var name=parse_ShortXLUnicodeString(blob,0,opts);return name}function parse_LabelSst(blob,length){var cell=parse_Cell(blob);cell.isst=blob.read_shift(4);return cell}function parse_Label(blob,length,opts){var cell=parse_Cell(blob,6);var str=parse_XLUnicodeString(blob,length-6,opts);cell.val=str;return cell}function parse_Format(blob,length,opts){var ifmt=blob.read_shift(2);var fmtstr=parse_XLUnicodeString2(blob,0,opts);return[ifmt,fmtstr]}function parse_Dimensions(blob,length){var w=length===10?2:4;var r=blob.read_shift(w),R=blob.read_shift(w),c=blob.read_shift(2),C=blob.read_shift(2);blob.l+=2;return{s:{r:r,c:c},e:{r:R,c:C}}}function parse_RK(blob,length){var rw=blob.read_shift(2),col=blob.read_shift(2);var rkrec=parse_RkRec(blob);return{r:rw,c:col,ixfe:rkrec[0],rknum:rkrec[1]}}function parse_MulRk(blob,length){var target=blob.l+length-2;var rw=blob.read_shift(2),col=blob.read_shift(2);var rkrecs=[];while(blob.l<target)rkrecs.push(parse_RkRec(blob));if(blob.l!==target)throw"MulRK read error";var lastcol=blob.read_shift(2);if(rkrecs.length!=lastcol-col+1)throw"MulRK length mismatch";return{r:rw,c:col,C:lastcol,rkrec:rkrecs}}var parse_CellXF=parsenoop;var parse_StyleXF=parsenoop;function parse_XF(blob,length){var o={};o.ifnt=blob.read_shift(2);o.ifmt=blob.read_shift(2);o.flags=blob.read_shift(2);o.fStyle=o.flags>>2&1;length-=6;o.data=o.fStyle?parse_StyleXF(blob,length):parse_CellXF(blob,length);return o}function parse_Guts(blob,length){blob.l+=4;var out=[blob.read_shift(2),blob.read_shift(2)];if(out[0]!==0)out[0]--;if(out[1]!==0)out[1]--;if(out[0]>7||out[1]>7)throw"Bad Gutters: "+out;return out}function parse_BoolErr(blob,length){var cell=parse_Cell(blob,6);var val=parse_Bes(blob,2);cell.val=val;cell.t=val===true||val===false?"b":"e";return cell}function parse_Number(blob,length){var cell=parse_Cell(blob,6);var xnum=parse_Xnum(blob,8);cell.val=xnum;return cell}var parse_XLHeaderFooter=parse_OptXLUnicodeString;function parse_SupBook(blob,length,opts){var end=blob.l+length;var ctab=blob.read_shift(2);var cch=blob.read_shift(2);var virtPath;if(cch>=1&&cch<=255)virtPath=parse_XLUnicodeStringNoCch(blob,cch);var rgst=blob.read_shift(end-blob.l);opts.sbcch=cch;return[cch,ctab,virtPath,rgst]}function parse_ExternName(blob,length,opts){var flags=blob.read_shift(2);var body;var o={fBuiltIn:flags&1,fWantAdvise:flags>>>1&1,fWantPict:flags>>>2&1,fOle:flags>>>3&1,fOleLink:flags>>>4&1,cf:flags>>>5&1023,fIcon:flags>>>15&1};if(opts.sbcch===14849)body=parse_AddinUdf(blob,length-2);o.body=body||blob.read_shift(length-2);return o}function parse_Lbl(blob,length,opts){if(opts.biff<8)return parse_Label(blob,length,opts);var target=blob.l+length;var flags=blob.read_shift(2);var chKey=blob.read_shift(1);var cch=blob.read_shift(1);var cce=blob.read_shift(2);blob.l+=2;var itab=blob.read_shift(2);blob.l+=4;var name=parse_XLUnicodeStringNoCch(blob,cch,opts);var rgce=parse_NameParsedFormula(blob,target-blob.l,opts,cce);return{chKey:chKey,Name:name,rgce:rgce}}function parse_ExternSheet(blob,length,opts){if(opts.biff<8)return parse_ShortXLUnicodeString(blob,length,opts);var o=parslurp2(blob,length,parse_XTI);var oo=[];if(opts.sbcch===1025){for(var i=0;i!=o.length;++i)oo.push(opts.snames[o[i][1]]);return oo}else return o}function parse_ShrFmla(blob,length,opts){var ref=parse_RefU(blob,6);blob.l++;var cUse=blob.read_shift(1);length-=8;return[parse_SharedParsedFormula(blob,length,opts),cUse]}function parse_Array(blob,length,opts){var ref=parse_Ref(blob,6);blob.l+=6;length-=12;return[ref,parse_ArrayParsedFormula(blob,length,opts,ref)]}function parse_MTRSettings(blob,length){var fMTREnabled=blob.read_shift(4)!==0;var fUserSetThreadCount=blob.read_shift(4)!==0;var cUserThreadCount=blob.read_shift(4);return[fMTREnabled,fUserSetThreadCount,cUserThreadCount]}function parse_NoteSh(blob,length,opts){if(opts.biff<8)return;var row=blob.read_shift(2),col=blob.read_shift(2);var flags=blob.read_shift(2),idObj=blob.read_shift(2);var stAuthor=parse_XLUnicodeString2(blob,0,opts);if(opts.biff<8)blob.read_shift(1);return[{r:row,c:col},stAuthor,idObj,flags]}function parse_Note(blob,length,opts){return parse_NoteSh(blob,length,opts)}function parse_MergeCells(blob,length){var merges=[];var cmcs=blob.read_shift(2);while(cmcs--)merges.push(parse_Ref8U(blob,length));return merges}function parse_Obj(blob,length){var cmo=parse_FtCmo(blob,22);var fts=parse_FtArray(blob,length-22,cmo[1]);return{cmo:cmo,ft:fts}}function parse_TxO(blob,length,opts){var s=blob.l;try{blob.l+=4;var ot=(opts.lastobj||{cmo:[0,0]}).cmo[1];var controlInfo;if([0,5,7,11,12,14].indexOf(ot)==-1)blob.l+=6;else controlInfo=parse_ControlInfo(blob,6,opts);var cchText=blob.read_shift(2);var cbRuns=blob.read_shift(2);var ifntEmpty=parse_FontIndex(blob,2);var len=blob.read_shift(2);blob.l+=len;var texts="";for(var i=1;i<blob.lens.length-1;++i){if(blob.l-s!=blob.lens[i])throw"TxO: bad continue record";var hdr=blob[blob.l];var t=parse_XLUnicodeStringNoCch(blob,blob.lens[i+1]-blob.lens[i]-1);texts+=t;if(texts.length>=(hdr?cchText:2*cchText))break}if(texts.length!==cchText&&texts.length!==cchText*2){throw"cchText: "+cchText+" != "+texts.length}blob.l=s+length;return{t:texts}}catch(e){blob.l=s+length;return{t:texts||""}}}var parse_HLink=function(blob,length){var ref=parse_Ref8U(blob,8);blob.l+=16;var hlink=parse_Hyperlink(blob,length-24);return[ref,hlink]};var parse_HLinkTooltip=function(blob,length){var end=blob.l+length;blob.read_shift(2);var ref=parse_Ref8U(blob,8);var wzTooltip=blob.read_shift((length-10)/2,"dbcs");wzTooltip=wzTooltip.replace(chr0,"");return[ref,wzTooltip]};function parse_Country(blob,length){var o=[],d;d=blob.read_shift(2);o[0]=CountryEnum[d]||d;d=blob.read_shift(2);o[1]=CountryEnum[d]||d;return o}var parse_Backup=parsebool;var parse_Blank=parse_Cell;var parse_BottomMargin=parse_Xnum;var parse_BuiltInFnGroupCount=parseuint16;var parse_CalcCount=parseuint16;var parse_CalcDelta=parse_Xnum;var parse_CalcIter=parsebool;var parse_CalcMode=parseuint16;var parse_CalcPrecision=parsebool;var parse_CalcRefMode=parsenoop2;var parse_CalcSaveRecalc=parsebool;var parse_CodePage=parseuint16;var parse_Compat12=parsebool;var parse_Date1904=parsebool;var parse_DefColWidth=parseuint16;var parse_DSF=parsenoop2;var parse_EntExU2=parsenoop2;var parse_EOF=parsenoop2;var parse_Excel9File=parsenoop2;var parse_FeatHdr=parsenoop2;var parse_FontX=parseuint16;var parse_Footer=parse_XLHeaderFooter;var parse_GridSet=parseuint16;var parse_HCenter=parsebool;var parse_Header=parse_XLHeaderFooter;var parse_HideObj=parse_HideObjEnum;var parse_InterfaceEnd=parsenoop2;var parse_LeftMargin=parse_Xnum;var parse_Mms=parsenoop2;var parse_ObjProtect=parsebool;var parse_Password=parseuint16;var parse_PrintGrid=parsebool;var parse_PrintRowCol=parsebool;var parse_PrintSize=parseuint16;var parse_Prot4Rev=parsebool;var parse_Prot4RevPass=parseuint16;var parse_Protect=parsebool;var parse_RefreshAll=parsebool;var parse_RightMargin=parse_Xnum;
+var parse_RRTabId=parseuint16a;var parse_ScenarioProtect=parsebool;var parse_Scl=parseuint16a;var parse_String=parse_XLUnicodeString;var parse_SxBool=parsebool;var parse_TopMargin=parse_Xnum;var parse_UsesELFs=parsebool;var parse_VCenter=parsebool;var parse_WinProtect=parsebool;var parse_WriteProtect=parsenoop;var parse_VerticalPageBreaks=parsenoop;var parse_HorizontalPageBreaks=parsenoop;var parse_Selection=parsenoop;var parse_Continue=parsenoop;var parse_Pane=parsenoop;var parse_Pls=parsenoop;var parse_DCon=parsenoop;var parse_DConRef=parsenoop;var parse_DConName=parsenoop;var parse_XCT=parsenoop;var parse_CRN=parsenoop;var parse_FileSharing=parsenoop;var parse_Uncalced=parsenoop;var parse_Template=parsenoop;var parse_Intl=parsenoop;var parse_ColInfo=parsenoop;var parse_WsBool=parsenoop;var parse_Sort=parsenoop;var parse_Palette=parsenoop;var parse_Sync=parsenoop;var parse_LPr=parsenoop;var parse_DxGCol=parsenoop;var parse_FnGroupName=parsenoop;var parse_FilterMode=parsenoop;var parse_AutoFilterInfo=parsenoop;var parse_AutoFilter=parsenoop;var parse_Setup=parsenoop;var parse_ScenMan=parsenoop;var parse_SCENARIO=parsenoop;var parse_SxView=parsenoop;var parse_Sxvd=parsenoop;var parse_SXVI=parsenoop;var parse_SxIvd=parsenoop;var parse_SXLI=parsenoop;var parse_SXPI=parsenoop;var parse_DocRoute=parsenoop;var parse_RecipName=parsenoop;var parse_MulBlank=parsenoop;var parse_SXDI=parsenoop;var parse_SXDB=parsenoop;var parse_SXFDB=parsenoop;var parse_SXDBB=parsenoop;var parse_SXNum=parsenoop;var parse_SxErr=parsenoop;var parse_SXInt=parsenoop;var parse_SXString=parsenoop;var parse_SXDtr=parsenoop;var parse_SxNil=parsenoop;var parse_SXTbl=parsenoop;var parse_SXTBRGIITM=parsenoop;var parse_SxTbpg=parsenoop;var parse_ObProj=parsenoop;var parse_SXStreamID=parsenoop;var parse_DBCell=parsenoop;var parse_SXRng=parsenoop;var parse_SxIsxoper=parsenoop;var parse_BookBool=parsenoop;var parse_DbOrParamQry=parsenoop;var parse_OleObjectSize=parsenoop;var parse_SXVS=parsenoop;var parse_BkHim=parsenoop;var parse_MsoDrawingGroup=parsenoop;var parse_MsoDrawing=parsenoop;var parse_MsoDrawingSelection=parsenoop;var parse_PhoneticInfo=parsenoop;var parse_SxRule=parsenoop;var parse_SXEx=parsenoop;var parse_SxFilt=parsenoop;var parse_SxDXF=parsenoop;var parse_SxItm=parsenoop;var parse_SxName=parsenoop;var parse_SxSelect=parsenoop;var parse_SXPair=parsenoop;var parse_SxFmla=parsenoop;var parse_SxFormat=parsenoop;var parse_SXVDEx=parsenoop;var parse_SXFormula=parsenoop;var parse_SXDBEx=parsenoop;var parse_RRDInsDel=parsenoop;var parse_RRDHead=parsenoop;var parse_RRDChgCell=parsenoop;var parse_RRDRenSheet=parsenoop;var parse_RRSort=parsenoop;var parse_RRDMove=parsenoop;var parse_RRFormat=parsenoop;var parse_RRAutoFmt=parsenoop;var parse_RRInsertSh=parsenoop;var parse_RRDMoveBegin=parsenoop;var parse_RRDMoveEnd=parsenoop;var parse_RRDInsDelBegin=parsenoop;var parse_RRDInsDelEnd=parsenoop;var parse_RRDConflict=parsenoop;var parse_RRDDefName=parsenoop;var parse_RRDRstEtxp=parsenoop;var parse_LRng=parsenoop;var parse_CUsr=parsenoop;var parse_CbUsr=parsenoop;var parse_UsrInfo=parsenoop;var parse_UsrExcl=parsenoop;var parse_FileLock=parsenoop;var parse_RRDInfo=parsenoop;var parse_BCUsrs=parsenoop;var parse_UsrChk=parsenoop;var parse_UserBView=parsenoop;var parse_UserSViewBegin=parsenoop;var parse_UserSViewEnd=parsenoop;var parse_RRDUserView=parsenoop;var parse_Qsi=parsenoop;var parse_CondFmt=parsenoop;var parse_CF=parsenoop;var parse_DVal=parsenoop;var parse_DConBin=parsenoop;var parse_Lel=parsenoop;var parse_CodeName=parse_XLUnicodeString;var parse_SXFDBType=parsenoop;var parse_ObNoMacros=parsenoop;var parse_Dv=parsenoop;var parse_Index=parsenoop;var parse_Table=parsenoop;var parse_Window2=parsenoop;var parse_Style=parsenoop;var parse_BigName=parsenoop;var parse_ContinueBigName=parsenoop;var parse_WebPub=parsenoop;var parse_QsiSXTag=parsenoop;var parse_DBQueryExt=parsenoop;var parse_ExtString=parsenoop;var parse_TxtQry=parsenoop;var parse_Qsir=parsenoop;var parse_Qsif=parsenoop;var parse_RRDTQSIF=parsenoop;var parse_OleDbConn=parsenoop;var parse_WOpt=parsenoop;var parse_SXViewEx=parsenoop;var parse_SXTH=parsenoop;var parse_SXPIEx=parsenoop;var parse_SXVDTEx=parsenoop;var parse_SXViewEx9=parsenoop;var parse_ContinueFrt=parsenoop;var parse_RealTimeData=parsenoop;var parse_ChartFrtInfo=parsenoop;var parse_FrtWrapper=parsenoop;var parse_StartBlock=parsenoop;var parse_EndBlock=parsenoop;var parse_StartObject=parsenoop;var parse_EndObject=parsenoop;var parse_CatLab=parsenoop;var parse_YMult=parsenoop;var parse_SXViewLink=parsenoop;var parse_PivotChartBits=parsenoop;var parse_FrtFontList=parsenoop;var parse_SheetExt=parsenoop;var parse_BookExt=parsenoop;var parse_SXAddl=parsenoop;var parse_CrErr=parsenoop;var parse_HFPicture=parsenoop;var parse_Feat=parsenoop;var parse_DataLabExt=parsenoop;var parse_DataLabExtContents=parsenoop;var parse_CellWatch=parsenoop;var parse_FeatHdr11=parsenoop;var parse_Feature11=parsenoop;var parse_DropDownObjIds=parsenoop;var parse_ContinueFrt11=parsenoop;var parse_DConn=parsenoop;var parse_List12=parsenoop;var parse_Feature12=parsenoop;var parse_CondFmt12=parsenoop;var parse_CF12=parsenoop;var parse_CFEx=parsenoop;var parse_XFCRC=parsenoop;var parse_XFExt=parsenoop;var parse_AutoFilter12=parsenoop;var parse_ContinueFrt12=parsenoop;var parse_MDTInfo=parsenoop;var parse_MDXStr=parsenoop;var parse_MDXTuple=parsenoop;var parse_MDXSet=parsenoop;var parse_MDXProp=parsenoop;var parse_MDXKPI=parsenoop;var parse_MDB=parsenoop;var parse_PLV=parsenoop;var parse_DXF=parsenoop;var parse_TableStyles=parsenoop;var parse_TableStyle=parsenoop;var parse_TableStyleElement=parsenoop;var parse_StyleExt=parsenoop;var parse_NamePublish=parsenoop;var parse_NameCmt=parsenoop;var parse_SortData=parsenoop;var parse_Theme=parsenoop;var parse_GUIDTypeLib=parsenoop;var parse_FnGrp12=parsenoop;var parse_NameFnGrp12=parsenoop;var parse_HeaderFooter=parsenoop;var parse_CrtLayout12=parsenoop;var parse_CrtMlFrt=parsenoop;var parse_CrtMlFrtContinue=parsenoop;var parse_ShapePropsStream=parsenoop;var parse_TextPropsStream=parsenoop;var parse_RichTextStream=parsenoop;var parse_CrtLayout12A=parsenoop;var parse_Units=parsenoop;var parse_Chart=parsenoop;var parse_Series=parsenoop;var parse_DataFormat=parsenoop;var parse_LineFormat=parsenoop;var parse_MarkerFormat=parsenoop;var parse_AreaFormat=parsenoop;var parse_PieFormat=parsenoop;var parse_AttachedLabel=parsenoop;var parse_SeriesText=parsenoop;var parse_ChartFormat=parsenoop;var parse_Legend=parsenoop;var parse_SeriesList=parsenoop;var parse_Bar=parsenoop;var parse_Line=parsenoop;var parse_Pie=parsenoop;var parse_Area=parsenoop;var parse_Scatter=parsenoop;var parse_CrtLine=parsenoop;var parse_Axis=parsenoop;var parse_Tick=parsenoop;var parse_ValueRange=parsenoop;var parse_CatSerRange=parsenoop;var parse_AxisLine=parsenoop;var parse_CrtLink=parsenoop;var parse_DefaultText=parsenoop;var parse_Text=parsenoop;var parse_ObjectLink=parsenoop;var parse_Frame=parsenoop;var parse_Begin=parsenoop;var parse_End=parsenoop;var parse_PlotArea=parsenoop;var parse_Chart3d=parsenoop;var parse_PicF=parsenoop;var parse_DropBar=parsenoop;var parse_Radar=parsenoop;var parse_Surf=parsenoop;var parse_RadarArea=parsenoop;var parse_AxisParent=parsenoop;var parse_LegendException=parsenoop;var parse_ShtProps=parsenoop;var parse_SerToCrt=parsenoop;var parse_AxesUsed=parsenoop;var parse_SBaseRef=parsenoop;var parse_SerParent=parsenoop;var parse_SerAuxTrend=parsenoop;var parse_IFmtRecord=parsenoop;var parse_Pos=parsenoop;var parse_AlRuns=parsenoop;var parse_BRAI=parsenoop;var parse_SerAuxErrBar=parsenoop;var parse_ClrtClient=parsenoop;var parse_SerFmt=parsenoop;var parse_Chart3DBarShape=parsenoop;var parse_Fbi=parsenoop;var parse_BopPop=parsenoop;var parse_AxcExt=parsenoop;var parse_Dat=parsenoop;var parse_PlotGrowth=parsenoop;var parse_SIIndex=parsenoop;var parse_GelFrame=parsenoop;var parse_BopPopCustom=parsenoop;var parse_Fbi2=parsenoop;function parse_BIFF5String(blob){var len=blob.read_shift(1);return blob.read_shift(len,"sbcs")}var _chr=function(c){return String.fromCharCode(c)};var attregexg=/([\w:]+)=((?:")([^"]*)(?:")|(?:')([^']*)(?:'))/g;var attregex=/([\w:]+)=((?:")(?:[^"]*)(?:")|(?:')(?:[^']*)(?:'))/;function parsexmltag(tag,skip_root){var words=tag.split(/\s+/);var z=[];if(!skip_root)z[0]=words[0];if(words.length===1)return z;var m=tag.match(attregexg),y,j,w,i;if(m)for(i=0;i!=m.length;++i){y=m[i].match(attregex);if((j=y[1].indexOf(":"))===-1)z[y[1]]=y[2].substr(1,y[2].length-2);else{if(y[1].substr(0,6)==="xmlns:")w="xmlns"+y[1].substr(6);else w=y[1].substr(j+1);z[w]=y[2].substr(1,y[2].length-2)}}return z}function parsexmltagobj(tag){var words=tag.split(/\s+/);var z={};if(words.length===1)return z;var m=tag.match(attregexg),y,j,w,i;if(m)for(i=0;i!=m.length;++i){y=m[i].match(attregex);if((j=y[1].indexOf(":"))===-1)z[y[1]]=y[2].substr(1,y[2].length-2);else{if(y[1].substr(0,6)==="xmlns:")w="xmlns"+y[1].substr(6);else w=y[1].substr(j+1);z[w]=y[2].substr(1,y[2].length-2)}}return z}var encodings={"&quot;":'"',"&apos;":"'","&gt;":">","&lt;":"<","&amp;":"&"};var rencoding=evert(encodings);var rencstr="&<>'\"".split("");var XML_HEADER='<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\r\n';var OFFCRYPTO={};var make_offcrypto=function(O,_crypto){var crypto;if(typeof _crypto!=="undefined")crypto=_crypto;else if(typeof require!=="undefined"){try{crypto=require("cry"+"pto")}catch(e){crypto=null}}O.rc4=function(key,data){var S=new Array(256);var c=0,i=0,j=0,t=0;for(i=0;i!=256;++i)S[i]=i;for(i=0;i!=256;++i){j=j+S[i]+key[i%key.length].charCodeAt(0)&255;t=S[i];S[i]=S[j];S[j]=t}i=j=0;out=Buffer(data.length);for(c=0;c!=data.length;++c){i=i+1&255;j=(j+S[i])%256;t=S[i];S[i]=S[j];S[j]=t;out[c]=data[c]^S[S[i]+S[j]&255]}return out};if(crypto){O.md5=function(hex){return crypto.createHash("md5").update(hex).digest("hex")}}else{O.md5=function(hex){throw"unimplemented"}}};make_offcrypto(OFFCRYPTO,typeof crypto!=="undefined"?crypto:undefined);function _JS2ANSI(str){if(typeof cptable!=="undefined")return cptable.utils.encode(1252,str);return str.split("").map(function(x){return x.charCodeAt(0)})}function parse_Version(blob,length){var o={};o.Major=blob.read_shift(2);o.Minor=blob.read_shift(2);return o}function parse_EncryptionHeader(blob,length){var o={};o.Flags=blob.read_shift(4);var tmp=blob.read_shift(4);if(tmp!==0)throw"Unrecognized SizeExtra: "+tmp;o.AlgID=blob.read_shift(4);switch(o.AlgID){case 0:case 26625:case 26126:case 26127:case 26128:break;default:throw"Unrecognized encryption algorithm: "+o.AlgID}parsenoop(blob,length-12);return o}function parse_EncryptionVerifier(blob,length){return parsenoop(blob,length)}function parse_RC4CryptoHeader(blob,length){var o={};var vers=o.EncryptionVersionInfo=parse_Version(blob,4);length-=4;if(vers.Minor!=2)throw"unrecognized minor version code: "+vers.Minor;if(vers.Major>4||vers.Major<2)throw"unrecognized major version code: "+vers.Major;o.Flags=blob.read_shift(4);length-=4;var sz=blob.read_shift(4);length-=4;o.EncryptionHeader=parse_EncryptionHeader(blob,sz);length-=sz;o.EncryptionVerifier=parse_EncryptionVerifier(blob,length);return o}function parse_RC4Header(blob,length){var o={};var vers=o.EncryptionVersionInfo=parse_Version(blob,4);length-=4;if(vers.Major!=1||vers.Minor!=1)throw"unrecognized version code "+vers.Major+" : "+vers.Minor;o.Salt=blob.read_shift(16);o.EncryptedVerifier=blob.read_shift(16);o.EncryptedVerifierHash=blob.read_shift(16);return o}function crypto_CreatePasswordVerifier_Method1(Password){var Verifier=0,PasswordArray;var PasswordDecoded=_JS2ANSI(Password);var len=PasswordDecoded.length+1,i,PasswordByte;var Intermediate1,Intermediate2,Intermediate3;PasswordArray=new_buf(len);PasswordArray[0]=PasswordDecoded.length;for(i=1;i!=len;++i)PasswordArray[i]=PasswordDecoded[i-1];for(i=len-1;i>=0;--i){PasswordByte=PasswordArray[i];Intermediate1=(Verifier&16384)===0?0:1;Intermediate2=Verifier<<1&32767;Intermediate3=Intermediate1|Intermediate2;Verifier=Intermediate3^PasswordByte}return Verifier^52811}var crypto_CreateXorArray_Method1=function(){var PadArray=[187,255,255,186,255,255,185,128,0,190,15,0,191,15,0];var InitialCode=[57840,7439,52380,33984,4364,3600,61902,12606,6258,57657,54287,34041,10252,43370,20163];var XorMatrix=[44796,19929,39858,10053,20106,40212,10761,31585,63170,64933,60267,50935,40399,11199,17763,35526,1453,2906,5812,11624,23248,885,1770,3540,7080,14160,28320,56640,55369,41139,20807,41614,21821,43642,17621,28485,56970,44341,19019,38038,14605,29210,60195,50791,40175,10751,21502,43004,24537,18387,36774,3949,7898,15796,31592,63184,47201,24803,49606,37805,14203,28406,56812,17824,35648,1697,3394,6788,13576,27152,43601,17539,35078,557,1114,2228,4456,30388,60776,51953,34243,7079,14158,28316,14128,28256,56512,43425,17251,34502,7597,13105,26210,52420,35241,883,1766,3532,4129,8258,16516,33032,4657,9314,18628];var Ror=function(Byte){return(Byte/2|Byte*128)&255};var XorRor=function(byte1,byte2){return Ror(byte1^byte2)};var CreateXorKey_Method1=function(Password){var XorKey=InitialCode[Password.length-1];var CurrentElement=104;for(var i=Password.length-1;i>=0;--i){var Char=Password[i];for(var j=0;j!=7;++j){if(Char&64)XorKey^=XorMatrix[CurrentElement];Char*=2;--CurrentElement}}return XorKey};return function(password){var Password=_JS2ANSI(password);var XorKey=CreateXorKey_Method1(Password);var Index=Password.length;var ObfuscationArray=new_buf(16);for(var i=0;i!=16;++i)ObfuscationArray[i]=0;var Temp,PasswordLastChar,PadIndex;if((Index&1)===1){Temp=XorKey>>8;ObfuscationArray[Index]=XorRor(PadArray[0],Temp);--Index;Temp=XorKey&255;PasswordLastChar=Password[Password.length-1];ObfuscationArray[Index]=XorRor(PasswordLastChar,Temp)}while(Index>0){--Index;Temp=XorKey>>8;ObfuscationArray[Index]=XorRor(Password[Index],Temp);--Index;Temp=XorKey&255;ObfuscationArray[Index]=XorRor(Password[Index],Temp)}Index=15;PadIndex=15-Password.length;while(PadIndex>0){Temp=XorKey>>8;ObfuscationArray[Index]=XorRor(PadArray[PadIndex],Temp);--Index;--PadIndex;Temp=XorKey&255;ObfuscationArray[Index]=XorRor(Password[Index],Temp);--Index;--PadIndex}return ObfuscationArray}}();var crypto_DecryptData_Method1=function(password,Data,XorArrayIndex,XorArray,O){if(!O)O=Data;if(!XorArray)XorArray=crypto_CreateXorArray_Method1(password);var Index,Value;for(Index=0;Index!=Data.length;++Index){Value=Data[Index];Value^=XorArray[XorArrayIndex];Value=(Value>>5|Value<<3)&255;O[Index]=Value;++XorArrayIndex}return[O,XorArrayIndex,XorArray]};var crypto_MakeXorDecryptor=function(password){var XorArrayIndex=0,XorArray=crypto_CreateXorArray_Method1(password);return function(Data){var O=crypto_DecryptData_Method1(null,Data,XorArrayIndex,XorArray);XorArrayIndex=O[1];return O[0]}};function parse_XORObfuscation(blob,length,opts,out){var o={key:parseuint16(blob),verificationBytes:parseuint16(blob)};if(opts.password)o.verifier=crypto_CreatePasswordVerifier_Method1(opts.password);out.valid=o.verificationBytes===o.verifier;if(out.valid)out.insitu_decrypt=crypto_MakeXorDecryptor(opts.password);return o}function parse_FilePassHeader(blob,length,oo){var o=oo||{};o.Info=blob.read_shift(2);blob.l-=2;if(o.Info===1)o.Data=parse_RC4Header(blob,length);else o.Data=parse_RC4CryptoHeader(blob,length);return o}function parse_FilePass(blob,length,opts){var o={Type:blob.read_shift(2)};if(o.Type)parse_FilePassHeader(blob,length-2,o);else parse_XORObfuscation(blob,length-2,opts,o);return o}function parseread(l){return function(blob,length){blob.l+=l;return}}function parseread1(blob,length){blob.l+=1;return}function parse_ColRelU(blob,length){var c=blob.read_shift(2);return[c&16383,c>>14&1,c>>15&1]}function parse_RgceArea(blob,length){var r=blob.read_shift(2),R=blob.read_shift(2);var c=parse_ColRelU(blob,2);var C=parse_ColRelU(blob,2);return{s:{r:r,c:c[0],cRel:c[1],rRel:c[2]},e:{r:R,c:C[0],cRel:C[1],rRel:C[2]}}}function parse_RgceAreaRel(blob,length){var r=blob.read_shift(2),R=blob.read_shift(2);var c=parse_ColRelU(blob,2);var C=parse_ColRelU(blob,2);return{s:{r:r,c:c[0],cRel:c[1],rRel:c[2]},e:{r:R,c:C[0],cRel:C[1],rRel:C[2]}}}function parse_RgceLoc(blob,length){var r=blob.read_shift(2);var c=parse_ColRelU(blob,2);return{r:r,c:c[0],cRel:c[1],rRel:c[2]}}function parse_RgceLocRel(blob,length){var r=blob.read_shift(2);var cl=blob.read_shift(2);var cRel=(cl&32768)>>15,rRel=(cl&16384)>>14;cl&=16383;if(cRel!==0)while(cl>=256)cl-=256;return{r:r,c:cl,cRel:cRel,rRel:rRel}}function parse_PtgArea(blob,length){var type=(blob[blob.l++]&96)>>5;var area=parse_RgceArea(blob,8);return[type,area]}function parse_PtgArea3d(blob,length){var type=(blob[blob.l++]&96)>>5;var ixti=blob.read_shift(2);var area=parse_RgceArea(blob,8);return[type,ixti,area]}function parse_PtgAreaErr(blob,length){var type=(blob[blob.l++]&96)>>5;blob.l+=8;return[type]}function parse_PtgAreaErr3d(blob,length){var type=(blob[blob.l++]&96)>>5;var ixti=blob.read_shift(2);blob.l+=8;return[type,ixti]}function parse_PtgAreaN(blob,length){var type=(blob[blob.l++]&96)>>5;var area=parse_RgceAreaRel(blob,8);return[type,area]}function parse_PtgArray(blob,length){var type=(blob[blob.l++]&96)>>5;blob.l+=7;return[type]}function parse_PtgAttrBaxcel(blob,length){var bitSemi=blob[blob.l+1]&1;var bitBaxcel=1;blob.l+=4;return[bitSemi,bitBaxcel]}function parse_PtgAttrChoose(blob,length){blob.l+=2;var offset=blob.read_shift(2);var o=[];for(var i=0;i<=offset;++i)o.push(blob.read_shift(2));return o}function parse_PtgAttrGoto(blob,length){var bitGoto=blob[blob.l+1]&255?1:0;blob.l+=2;return[bitGoto,blob.read_shift(2)]}function parse_PtgAttrIf(blob,length){var bitIf=blob[blob.l+1]&255?1:0;blob.l+=2;return[bitIf,blob.read_shift(2)]}function parse_PtgAttrSemi(blob,length){var bitSemi=blob[blob.l+1]&255?1:0;blob.l+=4;return[bitSemi]}function parse_PtgAttrSpaceType(blob,length){var type=blob.read_shift(1),cch=blob.read_shift(1);return[type,cch]}function parse_PtgAttrSpace(blob,length){blob.read_shift(2);return parse_PtgAttrSpaceType(blob,2)}function parse_PtgAttrSpaceSemi(blob,length){blob.read_shift(2);return parse_PtgAttrSpaceType(blob,2)}function parse_PtgRef(blob,length){var ptg=blob[blob.l]&31;var type=(blob[blob.l]&96)>>5;blob.l+=1;var loc=parse_RgceLoc(blob,4);return[type,loc]}function parse_PtgRefN(blob,length){var ptg=blob[blob.l]&31;var type=(blob[blob.l]&96)>>5;blob.l+=1;var loc=parse_RgceLocRel(blob,4);return[type,loc]}function parse_PtgRef3d(blob,length){var ptg=blob[blob.l]&31;var type=(blob[blob.l]&96)>>5;blob.l+=1;var ixti=blob.read_shift(2);var loc=parse_RgceLoc(blob,4);return[type,ixti,loc]}function parse_PtgFunc(blob,length){var ptg=blob[blob.l]&31;var type=(blob[blob.l]&96)>>5;blob.l+=1;var iftab=blob.read_shift(2);return[FtabArgc[iftab],Ftab[iftab]]}function parse_PtgFuncVar(blob,length){blob.l++;var cparams=blob.read_shift(1),tab=parsetab(blob);return[cparams,(tab[0]===0?Ftab:Cetab)[tab[1]]]}function parsetab(blob,length){return[blob[blob.l+1]>>7,blob.read_shift(2)&32767]}var parse_PtgAttrSum=parseread(4);var parse_PtgConcat=parseread1;function parse_PtgExp(blob,length){blob.l++;var row=blob.read_shift(2);var col=blob.read_shift(2);return[row,col]}function parse_PtgErr(blob,length){blob.l++;return BERR[blob.read_shift(1)]}function parse_PtgInt(blob,length){blob.l++;return blob.read_shift(2)}function parse_PtgBool(blob,length){blob.l++;return blob.read_shift(1)!==0}function parse_PtgNum(blob,length){blob.l++;return parse_Xnum(blob,8)}function parse_PtgStr(blob,length){blob.l++;return parse_ShortXLUnicodeString(blob)}function parse_SerAr(blob){var val=[];switch(val[0]=blob.read_shift(1)){case 4:val[1]=parsebool(blob,1)?"TRUE":"FALSE";blob.l+=7;break;case 16:val[1]=BERR[blob[blob.l]];blob.l+=8;break;case 0:blob.l+=8;break;case 1:val[1]=parse_Xnum(blob,8);break;case 2:val[1]=parse_XLUnicodeString(blob);break}return val}function parse_PtgExtraMem(blob,cce){var count=blob.read_shift(2);var out=[];for(var i=0;i!=count;++i)out.push(parse_Ref8U(blob,8));return out}function parse_PtgExtraArray(blob){var cols=1+blob.read_shift(1);var rows=1+blob.read_shift(2);for(var i=0,o=[];i!=rows&&(o[i]=[]);++i)for(var j=0;j!=cols;++j)o[i][j]=parse_SerAr(blob);return o}function parse_PtgName(blob,length){var type=blob.read_shift(1)>>>5&3;var nameindex=blob.read_shift(4);return[type,0,nameindex]}function parse_PtgNameX(blob,length){var type=blob.read_shift(1)>>>5&3;var ixti=blob.read_shift(2);var nameindex=blob.read_shift(4);return[type,ixti,nameindex]}function parse_PtgMemArea(blob,length){var type=blob.read_shift(1)>>>5&3;blob.l+=4;var cce=blob.read_shift(2);return[type,cce]}function parse_PtgMemFunc(blob,length){var type=blob.read_shift(1)>>>5&3;var cce=blob.read_shift(2);return[type,cce]}function parse_PtgRefErr(blob,length){var type=blob.read_shift(1)>>>5&3;blob.l+=4;return[type]}var parse_PtgAdd=parseread1;var parse_PtgDiv=parseread1;var parse_PtgEq=parseread1;var parse_PtgGe=parseread1;var parse_PtgGt=parseread1;var parse_PtgIsect=parseread1;var parse_PtgLe=parseread1;var parse_PtgLt=parseread1;var parse_PtgMissArg=parseread1;var parse_PtgMul=parseread1;var parse_PtgNe=parseread1;var parse_PtgParen=parseread1;var parse_PtgPercent=parseread1;var parse_PtgPower=parseread1;var parse_PtgRange=parseread1;var parse_PtgSub=parseread1;var parse_PtgUminus=parseread1;var parse_PtgUnion=parseread1;var parse_PtgUplus=parseread1;var parse_PtgMemErr=parsenoop;var parse_PtgMemNoMem=parsenoop;var parse_PtgRefErr3d=parsenoop;var parse_PtgTbl=parsenoop;var PtgTypes={1:{n:"PtgExp",f:parse_PtgExp},2:{n:"PtgTbl",f:parse_PtgTbl},3:{n:"PtgAdd",f:parse_PtgAdd},4:{n:"PtgSub",f:parse_PtgSub},5:{n:"PtgMul",f:parse_PtgMul},6:{n:"PtgDiv",f:parse_PtgDiv},7:{n:"PtgPower",f:parse_PtgPower},8:{n:"PtgConcat",f:parse_PtgConcat},9:{n:"PtgLt",f:parse_PtgLt},10:{n:"PtgLe",f:parse_PtgLe},11:{n:"PtgEq",f:parse_PtgEq},12:{n:"PtgGe",f:parse_PtgGe},13:{n:"PtgGt",f:parse_PtgGt},14:{n:"PtgNe",f:parse_PtgNe},15:{n:"PtgIsect",f:parse_PtgIsect},16:{n:"PtgUnion",f:parse_PtgUnion},17:{n:"PtgRange",f:parse_PtgRange},18:{n:"PtgUplus",f:parse_PtgUplus},19:{n:"PtgUminus",f:parse_PtgUminus},20:{n:"PtgPercent",f:parse_PtgPercent},21:{n:"PtgParen",f:parse_PtgParen},22:{n:"PtgMissArg",f:parse_PtgMissArg},23:{n:"PtgStr",f:parse_PtgStr},28:{n:"PtgErr",f:parse_PtgErr},29:{n:"PtgBool",f:parse_PtgBool},30:{n:"PtgInt",f:parse_PtgInt},31:{n:"PtgNum",f:parse_PtgNum},32:{n:"PtgArray",f:parse_PtgArray},33:{n:"PtgFunc",f:parse_PtgFunc},34:{n:"PtgFuncVar",f:parse_PtgFuncVar},35:{n:"PtgName",f:parse_PtgName},36:{n:"PtgRef",f:parse_PtgRef},37:{n:"PtgArea",f:parse_PtgArea},38:{n:"PtgMemArea",f:parse_PtgMemArea},39:{n:"PtgMemErr",f:parse_PtgMemErr},40:{n:"PtgMemNoMem",f:parse_PtgMemNoMem},41:{n:"PtgMemFunc",f:parse_PtgMemFunc},42:{n:"PtgRefErr",f:parse_PtgRefErr},43:{n:"PtgAreaErr",f:parse_PtgAreaErr},44:{n:"PtgRefN",f:parse_PtgRefN},45:{n:"PtgAreaN",f:parse_PtgAreaN},57:{n:"PtgNameX",f:parse_PtgNameX},58:{n:"PtgRef3d",f:parse_PtgRef3d},59:{n:"PtgArea3d",f:parse_PtgArea3d},60:{n:"PtgRefErr3d",f:parse_PtgRefErr3d},61:{n:"PtgAreaErr3d",f:parse_PtgAreaErr3d},255:{}};var PtgDupes={64:32,96:32,65:33,97:33,66:34,98:34,67:35,99:35,68:36,100:36,69:37,101:37,70:38,102:38,71:39,103:39,72:40,104:40,73:41,105:41,74:42,106:42,75:43,107:43,76:44,108:44,77:45,109:45,89:57,121:57,90:58,122:58,91:59,123:59,92:60,124:60,93:61,125:61};(function(){for(var y in PtgDupes)PtgTypes[y]=PtgTypes[PtgDupes[y]]})();var Ptg18={};var Ptg19={1:{n:"PtgAttrSemi",f:parse_PtgAttrSemi},2:{n:"PtgAttrIf",f:parse_PtgAttrIf},4:{n:"PtgAttrChoose",f:parse_PtgAttrChoose},8:{n:"PtgAttrGoto",f:parse_PtgAttrGoto},16:{n:"PtgAttrSum",f:parse_PtgAttrSum},32:{n:"PtgAttrBaxcel",f:parse_PtgAttrBaxcel},64:{n:"PtgAttrSpace",f:parse_PtgAttrSpace},65:{n:"PtgAttrSpaceSemi",f:parse_PtgAttrSpaceSemi},255:{}};var rcregex=/(^|[^A-Za-z])R(\[?)(-?\d+|)\]?C(\[?)(-?\d+|)\]?/g;var rcbase;function rcfunc($$,$1,$2,$3,$4,$5){var R=$3.length>0?parseInt($3,10)|0:0,C=$5.length>0?parseInt($5,10)|0:0;if(C<0&&$4.length===0)C=0;if($4.length>0)C+=rcbase.c;if($2.length>0)R+=rcbase.r;return $1+encode_col(C)+encode_row(R)}function rc_to_a1(fstr,base){rcbase=base;return fstr.replace(rcregex,rcfunc)}function parse_Formula(blob,length,opts){var cell=parse_Cell(blob,6);var val=parse_FormulaValue(blob,8);var flags=blob.read_shift(1);blob.read_shift(1);var chn=blob.read_shift(4);var cbf="";if(opts.biff===5)blob.l+=length-20;else cbf=parse_CellParsedFormula(blob,length-20,opts);return{cell:cell,val:val[0],formula:cbf,shared:flags>>3&1,tt:val[1]}}function parse_FormulaValue(blob){var b;if(__readUInt16LE(blob,blob.l+6)!==65535)return[parse_Xnum(blob),"n"];switch(blob[blob.l]){case 0:blob.l+=8;return["String","s"];case 1:b=blob[blob.l+2]===1;blob.l+=8;return[b,"b"];case 2:b=BERR[blob[blob.l+2]];blob.l+=8;return[b,"e"];case 3:blob.l+=8;return["","s"]}}function parse_RgbExtra(blob,length,rgce,opts){if(opts.biff<8)return parsenoop(blob,length);var target=blob.l+length;var o=[];for(var i=0;i!==rgce.length;++i){switch(rgce[i][0]){case"PtgArray":rgce[i][1]=parse_PtgExtraArray(blob);o.push(rgce[i][1]);break;case"PtgMemArea":rgce[i][2]=parse_PtgExtraMem(blob,rgce[i][1]);o.push(rgce[i][2]);break;default:break}}length=target-blob.l;if(length!==0)o.push(parsenoop(blob,length));return o}function parse_NameParsedFormula(blob,length,opts,cce){var target=blob.l+length;var rgce=parse_Rgce(blob,cce);var rgcb;if(target!==blob.l)rgcb=parse_RgbExtra(blob,target-blob.l,rgce,opts);return[rgce,rgcb]}function parse_CellParsedFormula(blob,length,opts){var target=blob.l+length;var rgcb,cce=blob.read_shift(2);if(cce==65535)return[[],parsenoop(blob,length-2)];var rgce=parse_Rgce(blob,cce);if(length!==cce+2)rgcb=parse_RgbExtra(blob,length-cce-2,rgce,opts);return[rgce,rgcb]}function parse_SharedParsedFormula(blob,length,opts){var target=blob.l+length;var rgcb,cce=blob.read_shift(2);var rgce=parse_Rgce(blob,cce);if(cce==65535)return[[],parsenoop(blob,length-2)];if(length!==cce+2)rgcb=parse_RgbExtra(blob,target-cce-2,rgce,opts);return[rgce,rgcb]}function parse_ArrayParsedFormula(blob,length,opts,ref){var target=blob.l+length;var rgcb,cce=blob.read_shift(2);if(cce==65535)return[[],parsenoop(blob,length-2)];var rgce=parse_Rgce(blob,cce);if(length!==cce+2)rgcb=parse_RgbExtra(blob,target-cce-2,rgce,opts);return[rgce,rgcb]}function parse_Rgce(blob,length){var target=blob.l+length;var R,id,ptgs=[];while(target!=blob.l){length=target-blob.l;id=blob[blob.l];R=PtgTypes[id];if(id===24||id===25){id=blob[blob.l+1];R=(id===24?Ptg18:Ptg19)[id]}if(!R||!R.f){ptgs.push(parsenoop(blob,length))}else{ptgs.push([R.n,R.f(blob,length)])}}return ptgs}function mapper(x){return x.map(function f2(y){return y[1]}).join(",")}function stringify_formula(formula,range,cell,supbooks,opts){if(opts!==undefined&&opts.biff===5)return"BIFF5??";var _range=range!==undefined?range:{s:{c:0,r:0}};var stack=[],e1,e2,type,c,ixti,nameidx,r;if(!formula[0]||!formula[0][0])return"";for(var ff=0,fflen=formula[0].length;ff<fflen;++ff){var f=formula[0][ff];switch(f[0]){case"PtgUminus":stack.push("-"+stack.pop());break;case"PtgUplus":stack.push("+"+stack.pop());break;case"PtgPercent":stack.push(stack.pop()+"%");break;case"PtgAdd":e1=stack.pop();e2=stack.pop();stack.push(e2+"+"+e1);break;case"PtgSub":e1=stack.pop();e2=stack.pop();stack.push(e2+"-"+e1);break;case"PtgMul":e1=stack.pop();e2=stack.pop();stack.push(e2+"*"+e1);break;case"PtgDiv":e1=stack.pop();e2=stack.pop();stack.push(e2+"/"+e1);break;case"PtgPower":e1=stack.pop();e2=stack.pop();stack.push(e2+"^"+e1);break;case"PtgConcat":e1=stack.pop();e2=stack.pop();stack.push(e2+"&"+e1);break;case"PtgLt":e1=stack.pop();e2=stack.pop();stack.push(e2+"<"+e1);break;case"PtgLe":e1=stack.pop();e2=stack.pop();stack.push(e2+"<="+e1);break;case"PtgEq":e1=stack.pop();e2=stack.pop();stack.push(e2+"="+e1);break;case"PtgGe":e1=stack.pop();e2=stack.pop();stack.push(e2+">="+e1);break;case"PtgGt":e1=stack.pop();e2=stack.pop();stack.push(e2+">"+e1);break;case"PtgNe":e1=stack.pop();e2=stack.pop();stack.push(e2+"<>"+e1);break;case"PtgIsect":e1=stack.pop();e2=stack.pop();stack.push(e2+" "+e1);break;case"PtgUnion":e1=stack.pop();e2=stack.pop();stack.push(e2+","+e1);break;case"PtgRange":break;case"PtgAttrChoose":break;case"PtgAttrGoto":break;case"PtgAttrIf":break;case"PtgRef":type=f[1][0];c=shift_cell(decode_cell(encode_cell(f[1][1])),_range);stack.push(encode_cell(c));break;case"PtgRefN":type=f[1][0];c=shift_cell(decode_cell(encode_cell(f[1][1])),cell);stack.push(encode_cell(c));break;case"PtgRef3d":type=f[1][0];ixti=f[1][1];c=shift_cell(f[1][2],_range);stack.push(supbooks[1][ixti+1]+"!"+encode_cell(c));break;case"PtgFunc":case"PtgFuncVar":var argc=f[1][0],func=f[1][1];if(!argc)argc=0;var args=stack.slice(-argc);stack.length-=argc;if(func==="User")func=args.shift();stack.push(func+"("+args.join(",")+")");break;case"PtgBool":stack.push(f[1]?"TRUE":"FALSE");break;case"PtgInt":stack.push(f[1]);break;case"PtgNum":stack.push(String(f[1]));break;case"PtgStr":stack.push('"'+f[1]+'"');break;case"PtgErr":stack.push(f[1]);break;case"PtgArea":type=f[1][0];r=shift_range(f[1][1],_range);stack.push(encode_range(r));break;case"PtgArea3d":type=f[1][0];ixti=f[1][1];r=f[1][2];stack.push(supbooks[1][ixti+1]+"!"+encode_range(r));break;case"PtgAttrSum":stack.push("SUM("+stack.pop()+")");break;case"PtgAttrSemi":break;case"PtgName":nameidx=f[1][2];var lbl=supbooks[0][nameidx];var name=lbl.Name;if(name in XLSXFutureFunctions)name=XLSXFutureFunctions[name];stack.push(name);break;case"PtgNameX":var bookidx=f[1][1];nameidx=f[1][2];var externbook;if(supbooks[bookidx+1])externbook=supbooks[bookidx+1][nameidx];else if(supbooks[bookidx-1])externbook=supbooks[bookidx-1][nameidx];if(!externbook)externbook={body:"??NAMEX??"};stack.push(externbook.body);break;case"PtgParen":stack.push("("+stack.pop()+")");break;case"PtgRefErr":stack.push("#REF!");break;case"PtgExp":c={c:f[1][1],r:f[1][0]};var q={c:cell.c,r:cell.r};if(supbooks.sharedf[encode_cell(c)]){var parsedf=supbooks.sharedf[encode_cell(c)];stack.push(stringify_formula(parsedf,_range,q,supbooks,opts))}else{var fnd=false;for(e1=0;e1!=supbooks.arrayf.length;++e1){e2=supbooks.arrayf[e1];if(c.c<e2[0].s.c||c.c>e2[0].e.c)continue;if(c.r<e2[0].s.r||c.r>e2[0].e.r)continue;stack.push(stringify_formula(e2[1],_range,q,supbooks,opts))}if(!fnd)stack.push(f[1])}break;case"PtgArray":stack.push("{"+f[1].map(mapper).join(";")+"}");break;case"PtgMemArea":break;case"PtgAttrSpace":break;case"PtgTbl":break;case"PtgMemErr":break;case"PtgMissArg":stack.push("");break;case"PtgAreaErr":break;case"PtgAreaN":stack.push("");break;case"PtgRefErr3d":break;case"PtgMemFunc":break;default:throw"Unrecognized Formula Token: "+f}}return stack[0]}var PtgDataType={1:"REFERENCE",2:"VALUE",3:"ARRAY"};var BERR={0:"#NULL!",7:"#DIV/0!",15:"#VALUE!",23:"#REF!",29:"#NAME?",36:"#NUM!",42:"#N/A",43:"#GETTING_DATA",255:"#WTF?"};var Cetab={0:"BEEP",1:"OPEN",2:"OPEN.LINKS",3:"CLOSE.ALL",4:"SAVE",5:"SAVE.AS",6:"FILE.DELETE",7:"PAGE.SETUP",8:"PRINT",9:"PRINTER.SETUP",10:"QUIT",11:"NEW.WINDOW",12:"ARRANGE.ALL",13:"WINDOW.SIZE",14:"WINDOW.MOVE",15:"FULL",16:"CLOSE",17:"RUN",22:"SET.PRINT.AREA",23:"SET.PRINT.TITLES",24:"SET.PAGE.BREAK",25:"REMOVE.PAGE.BREAK",26:"FONT",27:"DISPLAY",28:"PROTECT.DOCUMENT",29:"PRECISION",30:"A1.R1C1",31:"CALCULATE.NOW",32:"CALCULATION",34:"DATA.FIND",35:"EXTRACT",36:"DATA.DELETE",37:"SET.DATABASE",38:"SET.CRITERIA",39:"SORT",40:"DATA.SERIES",41:"TABLE",42:"FORMAT.NUMBER",43:"ALIGNMENT",44:"STYLE",45:"BORDER",46:"CELL.PROTECTION",47:"COLUMN.WIDTH",48:"UNDO",49:"CUT",50:"COPY",51:"PASTE",52:"CLEAR",53:"PASTE.SPECIAL",54:"EDIT.DELETE",55:"INSERT",56:"FILL.RIGHT",57:"FILL.DOWN",61:"DEFINE.NAME",62:"CREATE.NAMES",63:"FORMULA.GOTO",64:"FORMULA.FIND",65:"SELECT.LAST.CELL",66:"SHOW.ACTIVE.CELL",67:"GALLERY.AREA",68:"GALLERY.BAR",69:"GALLERY.COLUMN",70:"GALLERY.LINE",71:"GALLERY.PIE",72:"GALLERY.SCATTER",73:"COMBINATION",74:"PREFERRED",75:"ADD.OVERLAY",76:"GRIDLINES",77:"SET.PREFERRED",78:"AXES",79:"LEGEND",80:"ATTACH.TEXT",81:"ADD.ARROW",82:"SELECT.CHART",83:"SELECT.PLOT.AREA",84:"PATTERNS",85:"MAIN.CHART",86:"OVERLAY",87:"SCALE",88:"FORMAT.LEGEND",89:"FORMAT.TEXT",90:"EDIT.REPEAT",91:"PARSE",92:"JUSTIFY",93:"HIDE",94:"UNHIDE",95:"WORKSPACE",96:"FORMULA",97:"FORMULA.FILL",98:"FORMULA.ARRAY",99:"DATA.FIND.NEXT",100:"DATA.FIND.PREV",101:"FORMULA.FIND.NEXT",102:"FORMULA.FIND.PREV",103:"ACTIVATE",104:"ACTIVATE.NEXT",105:"ACTIVATE.PREV",106:"UNLOCKED.NEXT",107:"UNLOCKED.PREV",108:"COPY.PICTURE",109:"SELECT",110:"DELETE.NAME",111:"DELETE.FORMAT",112:"VLINE",113:"HLINE",114:"VPAGE",115:"HPAGE",116:"VSCROLL",117:"HSCROLL",118:"ALERT",119:"NEW",120:"CANCEL.COPY",121:"SHOW.CLIPBOARD",122:"MESSAGE",124:"PASTE.LINK",125:"APP.ACTIVATE",126:"DELETE.ARROW",127:"ROW.HEIGHT",128:"FORMAT.MOVE",129:"FORMAT.SIZE",130:"FORMULA.REPLACE",131:"SEND.KEYS",132:"SELECT.SPECIAL",133:"APPLY.NAMES",134:"REPLACE.FONT",135:"FREEZE.PANES",136:"SHOW.INFO",137:"SPLIT",138:"ON.WINDOW",139:"ON.DATA",140:"DISABLE.INPUT",142:"OUTLINE",143:"LIST.NAMES",144:"FILE.CLOSE",145:"SAVE.WORKBOOK",146:"DATA.FORM",147:"COPY.CHART",148:"ON.TIME",149:"WAIT",150:"FORMAT.FONT",151:"FILL.UP",152:"FILL.LEFT",153:"DELETE.OVERLAY",155:"SHORT.MENUS",159:"SET.UPDATE.STATUS",161:"COLOR.PALETTE",162:"DELETE.STYLE",163:"WINDOW.RESTORE",164:"WINDOW.MAXIMIZE",166:"CHANGE.LINK",167:"CALCULATE.DOCUMENT",168:"ON.KEY",169:"APP.RESTORE",170:"APP.MOVE",171:"APP.SIZE",172:"APP.MINIMIZE",173:"APP.MAXIMIZE",174:"BRING.TO.FRONT",175:"SEND.TO.BACK",185:"MAIN.CHART.TYPE",186:"OVERLAY.CHART.TYPE",187:"SELECT.END",188:"OPEN.MAIL",189:"SEND.MAIL",190:"STANDARD.FONT",191:"CONSOLIDATE",192:"SORT.SPECIAL",193:"GALLERY.3D.AREA",194:"GALLERY.3D.COLUMN",195:"GALLERY.3D.LINE",196:"GALLERY.3D.PIE",197:"VIEW.3D",198:"GOAL.SEEK",199:"WORKGROUP",200:"FILL.GROUP",201:"UPDATE.LINK",202:"PROMOTE",203:"DEMOTE",204:"SHOW.DETAIL",206:"UNGROUP",207:"OBJECT.PROPERTIES",208:"SAVE.NEW.OBJECT",209:"SHARE",210:"SHARE.NAME",211:"DUPLICATE",212:"APPLY.STYLE",213:"ASSIGN.TO.OBJECT",214:"OBJECT.PROTECTION",215:"HIDE.OBJECT",216:"SET.EXTRACT",217:"CREATE.PUBLISHER",218:"SUBSCRIBE.TO",219:"ATTRIBUTES",220:"SHOW.TOOLBAR",222:"PRINT.PREVIEW",223:"EDIT.COLOR",224:"SHOW.LEVELS",225:"FORMAT.MAIN",226:"FORMAT.OVERLAY",227:"ON.RECALC",228:"EDIT.SERIES",229:"DEFINE.STYLE",240:"LINE.PRINT",243:"ENTER.DATA",249:"GALLERY.RADAR",250:"MERGE.STYLES",251:"EDITION.OPTIONS",252:"PASTE.PICTURE",253:"PASTE.PICTURE.LINK",254:"SPELLING",256:"ZOOM",259:"INSERT.OBJECT",260:"WINDOW.MINIMIZE",265:"SOUND.NOTE",266:"SOUND.PLAY",267:"FORMAT.SHAPE",268:"EXTEND.POLYGON",269:"FORMAT.AUTO",272:"GALLERY.3D.BAR",273:"GALLERY.3D.SURFACE",274:"FILL.AUTO",276:"CUSTOMIZE.TOOLBAR",277:"ADD.TOOL",278:"EDIT.OBJECT",279:"ON.DOUBLECLICK",280:"ON.ENTRY",281:"WORKBOOK.ADD",282:"WORKBOOK.MOVE",283:"WORKBOOK.COPY",284:"WORKBOOK.OPTIONS",285:"SAVE.WORKSPACE",288:"CHART.WIZARD",289:"DELETE.TOOL",290:"MOVE.TOOL",291:"WORKBOOK.SELECT",292:"WORKBOOK.ACTIVATE",293:"ASSIGN.TO.TOOL",295:"COPY.TOOL",296:"RESET.TOOL",297:"CONSTRAIN.NUMERIC",298:"PASTE.TOOL",302:"WORKBOOK.NEW",305:"SCENARIO.CELLS",306:"SCENARIO.DELETE",307:"SCENARIO.ADD",308:"SCENARIO.EDIT",309:"SCENARIO.SHOW",310:"SCENARIO.SHOW.NEXT",311:"SCENARIO.SUMMARY",312:"PIVOT.TABLE.WIZARD",313:"PIVOT.FIELD.PROPERTIES",314:"PIVOT.FIELD",315:"PIVOT.ITEM",316:"PIVOT.ADD.FIELDS",318:"OPTIONS.CALCULATION",319:"OPTIONS.EDIT",320:"OPTIONS.VIEW",321:"ADDIN.MANAGER",322:"MENU.EDITOR",323:"ATTACH.TOOLBARS",324:"VBAActivate",325:"OPTIONS.CHART",328:"VBA.INSERT.FILE",330:"VBA.PROCEDURE.DEFINITION",336:"ROUTING.SLIP",338:"ROUTE.DOCUMENT",339:"MAIL.LOGON",342:"INSERT.PICTURE",343:"EDIT.TOOL",344:"GALLERY.DOUGHNUT",350:"CHART.TREND",352:"PIVOT.ITEM.PROPERTIES",354:"WORKBOOK.INSERT",355:"OPTIONS.TRANSITION",356:"OPTIONS.GENERAL",370:"FILTER.ADVANCED",373:"MAIL.ADD.MAILER",374:"MAIL.DELETE.MAILER",375:"MAIL.REPLY",376:"MAIL.REPLY.ALL",377:"MAIL.FORWARD",378:"MAIL.NEXT.LETTER",379:"DATA.LABEL",380:"INSERT.TITLE",381:"FONT.PROPERTIES",382:"MACRO.OPTIONS",383:"WORKBOOK.HIDE",384:"WORKBOOK.UNHIDE",385:"WORKBOOK.DELETE",386:"WORKBOOK.NAME",388:"GALLERY.CUSTOM",390:"ADD.CHART.AUTOFORMAT",391:"DELETE.CHART.AUTOFORMAT",392:"CHART.ADD.DATA",393:"AUTO.OUTLINE",394:"TAB.ORDER",395:"SHOW.DIALOG",396:"SELECT.ALL",397:"UNGROUP.SHEETS",398:"SUBTOTAL.CREATE",399:"SUBTOTAL.REMOVE",400:"RENAME.OBJECT",412:"WORKBOOK.SCROLL",413:"WORKBOOK.NEXT",414:"WORKBOOK.PREV",415:"WORKBOOK.TAB.SPLIT",416:"FULL.SCREEN",417:"WORKBOOK.PROTECT",420:"SCROLLBAR.PROPERTIES",421:"PIVOT.SHOW.PAGES",422:"TEXT.TO.COLUMNS",423:"FORMAT.CHARTTYPE",424:"LINK.FORMAT",425:"TRACER.DISPLAY",430:"TRACER.NAVIGATE",431:"TRACER.CLEAR",432:"TRACER.ERROR",433:"PIVOT.FIELD.GROUP",434:"PIVOT.FIELD.UNGROUP",435:"CHECKBOX.PROPERTIES",436:"LABEL.PROPERTIES",437:"LISTBOX.PROPERTIES",438:"EDITBOX.PROPERTIES",439:"PIVOT.REFRESH",440:"LINK.COMBO",441:"OPEN.TEXT",442:"HIDE.DIALOG",443:"SET.DIALOG.FOCUS",444:"ENABLE.OBJECT",445:"PUSHBUTTON.PROPERTIES",446:"SET.DIALOG.DEFAULT",447:"FILTER",448:"FILTER.SHOW.ALL",449:"CLEAR.OUTLINE",450:"FUNCTION.WIZARD",451:"ADD.LIST.ITEM",452:"SET.LIST.ITEM",453:"REMOVE.LIST.ITEM",454:"SELECT.LIST.ITEM",455:"SET.CONTROL.VALUE",456:"SAVE.COPY.AS",458:"OPTIONS.LISTS.ADD",459:"OPTIONS.LISTS.DELETE",460:"SERIES.AXES",461:"SERIES.X",462:"SERIES.Y",463:"ERRORBAR.X",464:"ERRORBAR.Y",465:"FORMAT.CHART",466:"SERIES.ORDER",467:"MAIL.LOGOFF",468:"CLEAR.ROUTING.SLIP",469:"APP.ACTIVATE.MICROSOFT",470:"MAIL.EDIT.MAILER",471:"ON.SHEET",472:"STANDARD.WIDTH",473:"SCENARIO.MERGE",474:"SUMMARY.INFO",475:"FIND.FILE",476:"ACTIVE.CELL.FONT",477:"ENABLE.TIPWIZARD",478:"VBA.MAKE.ADDIN",480:"INSERTDATATABLE",481:"WORKGROUP.OPTIONS",482:"MAIL.SEND.MAILER",485:"AUTOCORRECT",489:"POST.DOCUMENT",491:"PICKLIST",493:"VIEW.SHOW",494:"VIEW.DEFINE",495:"VIEW.DELETE",509:"SHEET.BACKGROUND",510:"INSERT.MAP.OBJECT",511:"OPTIONS.MENONO",517:"MSOCHECKS",518:"NORMAL",519:"LAYOUT",520:"RM.PRINT.AREA",521:"CLEAR.PRINT.AREA",522:"ADD.PRINT.AREA",523:"MOVE.BRK",545:"HIDECURR.NOTE",546:"HIDEALL.NOTES",547:"DELETE.NOTE",548:"TRAVERSE.NOTES",549:"ACTIVATE.NOTES",620:"PROTECT.REVISIONS",621:"UNPROTECT.REVISIONS",647:"OPTIONS.ME",653:"WEB.PUBLISH",667:"NEWWEBQUERY",673:"PIVOT.TABLE.CHART",753:"OPTIONS.SAVE",755:"OPTIONS.SPELL",808:"HIDEALL.INKANNOTS"};
+var Ftab={0:"COUNT",1:"IF",2:"ISNA",3:"ISERROR",4:"SUM",5:"AVERAGE",6:"MIN",7:"MAX",8:"ROW",9:"COLUMN",10:"NA",11:"NPV",12:"STDEV",13:"DOLLAR",14:"FIXED",15:"SIN",16:"COS",17:"TAN",18:"ATAN",19:"PI",20:"SQRT",21:"EXP",22:"LN",23:"LOG10",24:"ABS",25:"INT",26:"SIGN",27:"ROUND",28:"LOOKUP",29:"INDEX",30:"REPT",31:"MID",32:"LEN",33:"VALUE",34:"TRUE",35:"FALSE",36:"AND",37:"OR",38:"NOT",39:"MOD",40:"DCOUNT",41:"DSUM",42:"DAVERAGE",43:"DMIN",44:"DMAX",45:"DSTDEV",46:"VAR",47:"DVAR",48:"TEXT",49:"LINEST",50:"TREND",51:"LOGEST",52:"GROWTH",53:"GOTO",54:"HALT",55:"RETURN",56:"PV",57:"FV",58:"NPER",59:"PMT",60:"RATE",61:"MIRR",62:"IRR",63:"RAND",64:"MATCH",65:"DATE",66:"TIME",67:"DAY",68:"MONTH",69:"YEAR",70:"WEEKDAY",71:"HOUR",72:"MINUTE",73:"SECOND",74:"NOW",75:"AREAS",76:"ROWS",77:"COLUMNS",78:"OFFSET",79:"ABSREF",80:"RELREF",81:"ARGUMENT",82:"SEARCH",83:"TRANSPOSE",84:"ERROR",85:"STEP",86:"TYPE",87:"ECHO",88:"SET.NAME",89:"CALLER",90:"DEREF",91:"WINDOWS",92:"SERIES",93:"DOCUMENTS",94:"ACTIVE.CELL",95:"SELECTION",96:"RESULT",97:"ATAN2",98:"ASIN",99:"ACOS",100:"CHOOSE",101:"HLOOKUP",102:"VLOOKUP",103:"LINKS",104:"INPUT",105:"ISREF",106:"GET.FORMULA",107:"GET.NAME",108:"SET.VALUE",109:"LOG",110:"EXEC",111:"CHAR",112:"LOWER",113:"UPPER",114:"PROPER",115:"LEFT",116:"RIGHT",117:"EXACT",118:"TRIM",119:"REPLACE",120:"SUBSTITUTE",121:"CODE",122:"NAMES",123:"DIRECTORY",124:"FIND",125:"CELL",126:"ISERR",127:"ISTEXT",128:"ISNUMBER",129:"ISBLANK",130:"T",131:"N",132:"FOPEN",133:"FCLOSE",134:"FSIZE",135:"FREADLN",136:"FREAD",137:"FWRITELN",138:"FWRITE",139:"FPOS",140:"DATEVALUE",141:"TIMEVALUE",142:"SLN",143:"SYD",144:"DDB",145:"GET.DEF",146:"REFTEXT",147:"TEXTREF",148:"INDIRECT",149:"REGISTER",150:"CALL",151:"ADD.BAR",152:"ADD.MENU",153:"ADD.COMMAND",154:"ENABLE.COMMAND",155:"CHECK.COMMAND",156:"RENAME.COMMAND",157:"SHOW.BAR",158:"DELETE.MENU",159:"DELETE.COMMAND",160:"GET.CHART.ITEM",161:"DIALOG.BOX",162:"CLEAN",163:"MDETERM",164:"MINVERSE",165:"MMULT",166:"FILES",167:"IPMT",168:"PPMT",169:"COUNTA",170:"CANCEL.KEY",171:"FOR",172:"WHILE",173:"BREAK",174:"NEXT",175:"INITIATE",176:"REQUEST",177:"POKE",178:"EXECUTE",179:"TERMINATE",180:"RESTART",181:"HELP",182:"GET.BAR",183:"PRODUCT",184:"FACT",185:"GET.CELL",186:"GET.WORKSPACE",187:"GET.WINDOW",188:"GET.DOCUMENT",189:"DPRODUCT",190:"ISNONTEXT",191:"GET.NOTE",192:"NOTE",193:"STDEVP",194:"VARP",195:"DSTDEVP",196:"DVARP",197:"TRUNC",198:"ISLOGICAL",199:"DCOUNTA",200:"DELETE.BAR",201:"UNREGISTER",204:"USDOLLAR",205:"FINDB",206:"SEARCHB",207:"REPLACEB",208:"LEFTB",209:"RIGHTB",210:"MIDB",211:"LENB",212:"ROUNDUP",213:"ROUNDDOWN",214:"ASC",215:"DBCS",216:"RANK",219:"ADDRESS",220:"DAYS360",221:"TODAY",222:"VDB",223:"ELSE",224:"ELSE.IF",225:"END.IF",226:"FOR.CELL",227:"MEDIAN",228:"SUMPRODUCT",229:"SINH",230:"COSH",231:"TANH",232:"ASINH",233:"ACOSH",234:"ATANH",235:"DGET",236:"CREATE.OBJECT",237:"VOLATILE",238:"LAST.ERROR",239:"CUSTOM.UNDO",240:"CUSTOM.REPEAT",241:"FORMULA.CONVERT",242:"GET.LINK.INFO",243:"TEXT.BOX",244:"INFO",245:"GROUP",246:"GET.OBJECT",247:"DB",248:"PAUSE",251:"RESUME",252:"FREQUENCY",253:"ADD.TOOLBAR",254:"DELETE.TOOLBAR",255:"User",256:"RESET.TOOLBAR",257:"EVALUATE",258:"GET.TOOLBAR",259:"GET.TOOL",260:"SPELLING.CHECK",261:"ERROR.TYPE",262:"APP.TITLE",263:"WINDOW.TITLE",264:"SAVE.TOOLBAR",265:"ENABLE.TOOL",266:"PRESS.TOOL",267:"REGISTER.ID",268:"GET.WORKBOOK",269:"AVEDEV",270:"BETADIST",271:"GAMMALN",272:"BETAINV",273:"BINOMDIST",274:"CHIDIST",275:"CHIINV",276:"COMBIN",277:"CONFIDENCE",278:"CRITBINOM",279:"EVEN",280:"EXPONDIST",281:"FDIST",282:"FINV",283:"FISHER",284:"FISHERINV",285:"FLOOR",286:"GAMMADIST",287:"GAMMAINV",288:"CEILING",289:"HYPGEOMDIST",290:"LOGNORMDIST",291:"LOGINV",292:"NEGBINOMDIST",293:"NORMDIST",294:"NORMSDIST",295:"NORMINV",296:"NORMSINV",297:"STANDARDIZE",298:"ODD",299:"PERMUT",300:"POISSON",301:"TDIST",302:"WEIBULL",303:"SUMXMY2",304:"SUMX2MY2",305:"SUMX2PY2",306:"CHITEST",307:"CORREL",308:"COVAR",309:"FORECAST",310:"FTEST",311:"INTERCEPT",312:"PEARSON",313:"RSQ",314:"STEYX",315:"SLOPE",316:"TTEST",317:"PROB",318:"DEVSQ",319:"GEOMEAN",320:"HARMEAN",321:"SUMSQ",322:"KURT",323:"SKEW",324:"ZTEST",325:"LARGE",326:"SMALL",327:"QUARTILE",328:"PERCENTILE",329:"PERCENTRANK",330:"MODE",331:"TRIMMEAN",332:"TINV",334:"MOVIE.COMMAND",335:"GET.MOVIE",336:"CONCATENATE",337:"POWER",338:"PIVOT.ADD.DATA",339:"GET.PIVOT.TABLE",340:"GET.PIVOT.FIELD",341:"GET.PIVOT.ITEM",342:"RADIANS",343:"DEGREES",344:"SUBTOTAL",345:"SUMIF",346:"COUNTIF",347:"COUNTBLANK",348:"SCENARIO.GET",349:"OPTIONS.LISTS.GET",350:"ISPMT",351:"DATEDIF",352:"DATESTRING",353:"NUMBERSTRING",354:"ROMAN",355:"OPEN.DIALOG",356:"SAVE.DIALOG",357:"VIEW.GET",358:"GETPIVOTDATA",359:"HYPERLINK",360:"PHONETIC",361:"AVERAGEA",362:"MAXA",363:"MINA",364:"STDEVPA",365:"VARPA",366:"STDEVA",367:"VARA",368:"BAHTTEXT",369:"THAIDAYOFWEEK",370:"THAIDIGIT",371:"THAIMONTHOFYEAR",372:"THAINUMSOUND",373:"THAINUMSTRING",374:"THAISTRINGLENGTH",375:"ISTHAIDIGIT",376:"ROUNDBAHTDOWN",377:"ROUNDBAHTUP",378:"THAIYEAR",379:"RTD"};var FtabArgc={2:1,3:1,15:1,16:1,17:1,18:1,20:1,21:1,22:1,23:1,24:1,25:1,26:1,27:2,30:2,31:3,32:1,33:1,38:1,39:2,40:3,41:3,42:3,43:3,44:3,45:3,47:3,48:2,53:1,61:3,65:3,66:3,67:1,68:1,69:1,71:1,72:1,73:1,75:1,76:1,77:1,79:2,80:2,83:1,86:1,90:1,97:2,98:1,99:1,105:1,111:1,112:1,113:1,114:1,117:2,118:1,119:4,121:1,126:1,127:1,128:1,129:1,130:1,131:1,133:1,134:1,135:1,136:2,137:2,138:2,140:1,141:1,142:3,143:4,162:1,163:1,164:1,165:2,172:1,175:2,176:2,177:3,178:2,179:1,184:1,189:3,190:1,195:3,196:3,198:1,199:3,201:1,207:4,210:3,211:1,212:2,213:2,214:1,215:1,229:1,230:1,231:1,232:1,233:1,234:1,235:3,244:1,252:2,257:1,261:1,271:1,273:4,274:2,275:2,276:2,277:3,278:3,279:1,280:3,281:3,282:3,283:1,284:1,285:2,286:4,287:3,288:2,289:4,290:3,291:3,292:3,293:4,294:1,295:3,296:1,297:3,298:1,299:2,300:3,301:3,302:4,303:2,304:2,305:2,306:2,307:2,308:2,309:3,310:2,311:2,312:2,313:2,314:2,315:2,316:4,325:2,326:2,327:2,328:2,331:2,332:2,337:2,342:1,343:1,346:2,347:1,350:4,351:3,352:1,353:2,360:1,368:1,369:1,370:1,371:1,372:1,373:1,374:1,375:1,376:1,377:1,378:1,65535:0};var XLSXFutureFunctions={"_xlfn.ACOT":"ACOT","_xlfn.ACOTH":"ACOTH","_xlfn.AGGREGATE":"AGGREGATE","_xlfn.ARABIC":"ARABIC","_xlfn.AVERAGEIF":"AVERAGEIF","_xlfn.AVERAGEIFS":"AVERAGEIFS","_xlfn.BASE":"BASE","_xlfn.BETA.DIST":"BETA.DIST","_xlfn.BETA.INV":"BETA.INV","_xlfn.BINOM.DIST":"BINOM.DIST","_xlfn.BINOM.DIST.RANGE":"BINOM.DIST.RANGE","_xlfn.BINOM.INV":"BINOM.INV","_xlfn.BITAND":"BITAND","_xlfn.BITLSHIFT":"BITLSHIFT","_xlfn.BITOR":"BITOR","_xlfn.BITRSHIFT":"BITRSHIFT","_xlfn.BITXOR":"BITXOR","_xlfn.CEILING.MATH":"CEILING.MATH","_xlfn.CEILING.PRECISE":"CEILING.PRECISE","_xlfn.CHISQ.DIST":"CHISQ.DIST","_xlfn.CHISQ.DIST.RT":"CHISQ.DIST.RT","_xlfn.CHISQ.INV":"CHISQ.INV","_xlfn.CHISQ.INV.RT":"CHISQ.INV.RT","_xlfn.CHISQ.TEST":"CHISQ.TEST","_xlfn.COMBINA":"COMBINA","_xlfn.CONFIDENCE.NORM":"CONFIDENCE.NORM","_xlfn.CONFIDENCE.T":"CONFIDENCE.T","_xlfn.COT":"COT","_xlfn.COTH":"COTH","_xlfn.COUNTIFS":"COUNTIFS","_xlfn.COVARIANCE.P":"COVARIANCE.P","_xlfn.COVARIANCE.S":"COVARIANCE.S","_xlfn.CSC":"CSC","_xlfn.CSCH":"CSCH","_xlfn.DAYS":"DAYS","_xlfn.DECIMAL":"DECIMAL","_xlfn.ECMA.CEILING":"ECMA.CEILING","_xlfn.ERF.PRECISE":"ERF.PRECISE","_xlfn.ERFC.PRECISE":"ERFC.PRECISE","_xlfn.EXPON.DIST":"EXPON.DIST","_xlfn.F.DIST":"F.DIST","_xlfn.F.DIST.RT":"F.DIST.RT","_xlfn.F.INV":"F.INV","_xlfn.F.INV.RT":"F.INV.RT","_xlfn.F.TEST":"F.TEST","_xlfn.FILTERXML":"FILTERXML","_xlfn.FLOOR.MATH":"FLOOR.MATH","_xlfn.FLOOR.PRECISE":"FLOOR.PRECISE","_xlfn.FORMULATEXT":"FORMULATEXT","_xlfn.GAMMA":"GAMMA","_xlfn.GAMMA.DIST":"GAMMA.DIST","_xlfn.GAMMA.INV":"GAMMA.INV","_xlfn.GAMMALN.PRECISE":"GAMMALN.PRECISE","_xlfn.GAUSS":"GAUSS","_xlfn.HYPGEOM.DIST":"HYPGEOM.DIST","_xlfn.IFNA":"IFNA","_xlfn.IFERROR":"IFERROR","_xlfn.IMCOSH":"IMCOSH","_xlfn.IMCOT":"IMCOT","_xlfn.IMCSC":"IMCSC","_xlfn.IMCSCH":"IMCSCH","_xlfn.IMSEC":"IMSEC","_xlfn.IMSECH":"IMSECH","_xlfn.IMSINH":"IMSINH","_xlfn.IMTAN":"IMTAN","_xlfn.ISFORMULA":"ISFORMULA","_xlfn.ISO.CEILING":"ISO.CEILING","_xlfn.ISOWEEKNUM":"ISOWEEKNUM","_xlfn.LOGNORM.DIST":"LOGNORM.DIST","_xlfn.LOGNORM.INV":"LOGNORM.INV","_xlfn.MODE.MULT":"MODE.MULT","_xlfn.MODE.SNGL":"MODE.SNGL","_xlfn.MUNIT":"MUNIT","_xlfn.NEGBINOM.DIST":"NEGBINOM.DIST","_xlfn.NETWORKDAYS.INTL":"NETWORKDAYS.INTL","_xlfn.NIGBINOM":"NIGBINOM","_xlfn.NORM.DIST":"NORM.DIST","_xlfn.NORM.INV":"NORM.INV","_xlfn.NORM.S.DIST":"NORM.S.DIST","_xlfn.NORM.S.INV":"NORM.S.INV","_xlfn.NUMBERVALUE":"NUMBERVALUE","_xlfn.PDURATION":"PDURATION","_xlfn.PERCENTILE.EXC":"PERCENTILE.EXC","_xlfn.PERCENTILE.INC":"PERCENTILE.INC","_xlfn.PERCENTRANK.EXC":"PERCENTRANK.EXC","_xlfn.PERCENTRANK.INC":"PERCENTRANK.INC","_xlfn.PERMUTATIONA":"PERMUTATIONA","_xlfn.PHI":"PHI","_xlfn.POISSON.DIST":"POISSON.DIST","_xlfn.QUARTILE.EXC":"QUARTILE.EXC","_xlfn.QUARTILE.INC":"QUARTILE.INC","_xlfn.QUERYSTRING":"QUERYSTRING","_xlfn.RANK.AVG":"RANK.AVG","_xlfn.RANK.EQ":"RANK.EQ","_xlfn.RRI":"RRI","_xlfn.SEC":"SEC","_xlfn.SECH":"SECH","_xlfn.SHEET":"SHEET","_xlfn.SHEETS":"SHEETS","_xlfn.SKEW.P":"SKEW.P","_xlfn.STDEV.P":"STDEV.P","_xlfn.STDEV.S":"STDEV.S","_xlfn.SUMIFS":"SUMIFS","_xlfn.T.DIST":"T.DIST","_xlfn.T.DIST.2T":"T.DIST.2T","_xlfn.T.DIST.RT":"T.DIST.RT","_xlfn.T.INV":"T.INV","_xlfn.T.INV.2T":"T.INV.2T","_xlfn.T.TEST":"T.TEST","_xlfn.UNICHAR":"UNICHAR","_xlfn.UNICODE":"UNICODE","_xlfn.VAR.P":"VAR.P","_xlfn.VAR.S":"VAR.S","_xlfn.WEBSERVICE":"WEBSERVICE","_xlfn.WEIBULL.DIST":"WEIBULL.DIST","_xlfn.WORKDAY.INTL":"WORKDAY.INTL","_xlfn.XOR":"XOR","_xlfn.Z.TEST":"Z.TEST"};var RecordEnum={6:{n:"Formula",f:parse_Formula},10:{n:"EOF",f:parse_EOF},12:{n:"CalcCount",f:parse_CalcCount},13:{n:"CalcMode",f:parse_CalcMode},14:{n:"CalcPrecision",f:parse_CalcPrecision},15:{n:"CalcRefMode",f:parse_CalcRefMode},16:{n:"CalcDelta",f:parse_CalcDelta},17:{n:"CalcIter",f:parse_CalcIter},18:{n:"Protect",f:parse_Protect},19:{n:"Password",f:parse_Password},20:{n:"Header",f:parse_Header},21:{n:"Footer",f:parse_Footer},23:{n:"ExternSheet",f:parse_ExternSheet},24:{n:"Lbl",f:parse_Lbl},25:{n:"WinProtect",f:parse_WinProtect},26:{n:"VerticalPageBreaks",f:parse_VerticalPageBreaks},27:{n:"HorizontalPageBreaks",f:parse_HorizontalPageBreaks},28:{n:"Note",f:parse_Note},29:{n:"Selection",f:parse_Selection},34:{n:"Date1904",f:parse_Date1904},35:{n:"ExternName",f:parse_ExternName},38:{n:"LeftMargin",f:parse_LeftMargin},39:{n:"RightMargin",f:parse_RightMargin},40:{n:"TopMargin",f:parse_TopMargin},41:{n:"BottomMargin",f:parse_BottomMargin},42:{n:"PrintRowCol",f:parse_PrintRowCol},43:{n:"PrintGrid",f:parse_PrintGrid},47:{n:"FilePass",f:parse_FilePass},49:{n:"Font",f:parse_Font},51:{n:"PrintSize",f:parse_PrintSize},60:{n:"Continue",f:parse_Continue},61:{n:"Window1",f:parse_Window1},64:{n:"Backup",f:parse_Backup},65:{n:"Pane",f:parse_Pane},66:{n:"CodePage",f:parse_CodePage},77:{n:"Pls",f:parse_Pls},80:{n:"DCon",f:parse_DCon},81:{n:"DConRef",f:parse_DConRef},82:{n:"DConName",f:parse_DConName},85:{n:"DefColWidth",f:parse_DefColWidth},89:{n:"XCT",f:parse_XCT},90:{n:"CRN",f:parse_CRN},91:{n:"FileSharing",f:parse_FileSharing},92:{n:"WriteAccess",f:parse_WriteAccess},93:{n:"Obj",f:parse_Obj},94:{n:"Uncalced",f:parse_Uncalced},95:{n:"CalcSaveRecalc",f:parse_CalcSaveRecalc},96:{n:"Template",f:parse_Template},97:{n:"Intl",f:parse_Intl},99:{n:"ObjProtect",f:parse_ObjProtect},125:{n:"ColInfo",f:parse_ColInfo},128:{n:"Guts",f:parse_Guts},129:{n:"WsBool",f:parse_WsBool},130:{n:"GridSet",f:parse_GridSet},131:{n:"HCenter",f:parse_HCenter},132:{n:"VCenter",f:parse_VCenter},133:{n:"BoundSheet8",f:parse_BoundSheet8},134:{n:"WriteProtect",f:parse_WriteProtect},140:{n:"Country",f:parse_Country},141:{n:"HideObj",f:parse_HideObj},144:{n:"Sort",f:parse_Sort},146:{n:"Palette",f:parse_Palette},151:{n:"Sync",f:parse_Sync},152:{n:"LPr",f:parse_LPr},153:{n:"DxGCol",f:parse_DxGCol},154:{n:"FnGroupName",f:parse_FnGroupName},155:{n:"FilterMode",f:parse_FilterMode},156:{n:"BuiltInFnGroupCount",f:parse_BuiltInFnGroupCount},157:{n:"AutoFilterInfo",f:parse_AutoFilterInfo},158:{n:"AutoFilter",f:parse_AutoFilter},160:{n:"Scl",f:parse_Scl},161:{n:"Setup",f:parse_Setup},174:{n:"ScenMan",f:parse_ScenMan},175:{n:"SCENARIO",f:parse_SCENARIO},176:{n:"SxView",f:parse_SxView},177:{n:"Sxvd",f:parse_Sxvd},178:{n:"SXVI",f:parse_SXVI},180:{n:"SxIvd",f:parse_SxIvd},181:{n:"SXLI",f:parse_SXLI},182:{n:"SXPI",f:parse_SXPI},184:{n:"DocRoute",f:parse_DocRoute},185:{n:"RecipName",f:parse_RecipName},189:{n:"MulRk",f:parse_MulRk},190:{n:"MulBlank",f:parse_MulBlank},193:{n:"Mms",f:parse_Mms},197:{n:"SXDI",f:parse_SXDI},198:{n:"SXDB",f:parse_SXDB},199:{n:"SXFDB",f:parse_SXFDB},200:{n:"SXDBB",f:parse_SXDBB},201:{n:"SXNum",f:parse_SXNum},202:{n:"SxBool",f:parse_SxBool},203:{n:"SxErr",f:parse_SxErr},204:{n:"SXInt",f:parse_SXInt},205:{n:"SXString",f:parse_SXString},206:{n:"SXDtr",f:parse_SXDtr},207:{n:"SxNil",f:parse_SxNil},208:{n:"SXTbl",f:parse_SXTbl},209:{n:"SXTBRGIITM",f:parse_SXTBRGIITM},210:{n:"SxTbpg",f:parse_SxTbpg},211:{n:"ObProj",f:parse_ObProj},213:{n:"SXStreamID",f:parse_SXStreamID},215:{n:"DBCell",f:parse_DBCell},216:{n:"SXRng",f:parse_SXRng},217:{n:"SxIsxoper",f:parse_SxIsxoper},218:{n:"BookBool",f:parse_BookBool},220:{n:"DbOrParamQry",f:parse_DbOrParamQry},221:{n:"ScenarioProtect",f:parse_ScenarioProtect},222:{n:"OleObjectSize",f:parse_OleObjectSize},224:{n:"XF",f:parse_XF},225:{n:"InterfaceHdr",f:parse_InterfaceHdr},226:{n:"InterfaceEnd",f:parse_InterfaceEnd},227:{n:"SXVS",f:parse_SXVS},229:{n:"MergeCells",f:parse_MergeCells},233:{n:"BkHim",f:parse_BkHim},235:{n:"MsoDrawingGroup",f:parse_MsoDrawingGroup},236:{n:"MsoDrawing",f:parse_MsoDrawing},237:{n:"MsoDrawingSelection",f:parse_MsoDrawingSelection},239:{n:"PhoneticInfo",f:parse_PhoneticInfo},240:{n:"SxRule",f:parse_SxRule},241:{n:"SXEx",f:parse_SXEx},242:{n:"SxFilt",f:parse_SxFilt},244:{n:"SxDXF",f:parse_SxDXF},245:{n:"SxItm",f:parse_SxItm},246:{n:"SxName",f:parse_SxName},247:{n:"SxSelect",f:parse_SxSelect},248:{n:"SXPair",f:parse_SXPair},249:{n:"SxFmla",f:parse_SxFmla},251:{n:"SxFormat",f:parse_SxFormat},252:{n:"SST",f:parse_SST},253:{n:"LabelSst",f:parse_LabelSst},255:{n:"ExtSST",f:parse_ExtSST},256:{n:"SXVDEx",f:parse_SXVDEx},259:{n:"SXFormula",f:parse_SXFormula},290:{n:"SXDBEx",f:parse_SXDBEx},311:{n:"RRDInsDel",f:parse_RRDInsDel},312:{n:"RRDHead",f:parse_RRDHead},315:{n:"RRDChgCell",f:parse_RRDChgCell},317:{n:"RRTabId",f:parse_RRTabId},318:{n:"RRDRenSheet",f:parse_RRDRenSheet},319:{n:"RRSort",f:parse_RRSort},320:{n:"RRDMove",f:parse_RRDMove},330:{n:"RRFormat",f:parse_RRFormat},331:{n:"RRAutoFmt",f:parse_RRAutoFmt},333:{n:"RRInsertSh",f:parse_RRInsertSh},334:{n:"RRDMoveBegin",f:parse_RRDMoveBegin},335:{n:"RRDMoveEnd",f:parse_RRDMoveEnd},336:{n:"RRDInsDelBegin",f:parse_RRDInsDelBegin},337:{n:"RRDInsDelEnd",f:parse_RRDInsDelEnd},338:{n:"RRDConflict",f:parse_RRDConflict},339:{n:"RRDDefName",f:parse_RRDDefName},340:{n:"RRDRstEtxp",f:parse_RRDRstEtxp},351:{n:"LRng",f:parse_LRng},352:{n:"UsesELFs",f:parse_UsesELFs},353:{n:"DSF",f:parse_DSF},401:{n:"CUsr",f:parse_CUsr},402:{n:"CbUsr",f:parse_CbUsr},403:{n:"UsrInfo",f:parse_UsrInfo},404:{n:"UsrExcl",f:parse_UsrExcl},405:{n:"FileLock",f:parse_FileLock},406:{n:"RRDInfo",f:parse_RRDInfo},407:{n:"BCUsrs",f:parse_BCUsrs},408:{n:"UsrChk",f:parse_UsrChk},425:{n:"UserBView",f:parse_UserBView},426:{n:"UserSViewBegin",f:parse_UserSViewBegin},427:{n:"UserSViewEnd",f:parse_UserSViewEnd},428:{n:"RRDUserView",f:parse_RRDUserView},429:{n:"Qsi",f:parse_Qsi},430:{n:"SupBook",f:parse_SupBook},431:{n:"Prot4Rev",f:parse_Prot4Rev},432:{n:"CondFmt",f:parse_CondFmt},433:{n:"CF",f:parse_CF},434:{n:"DVal",f:parse_DVal},437:{n:"DConBin",f:parse_DConBin},438:{n:"TxO",f:parse_TxO},439:{n:"RefreshAll",f:parse_RefreshAll},440:{n:"HLink",f:parse_HLink},441:{n:"Lel",f:parse_Lel},442:{n:"CodeName",f:parse_CodeName},443:{n:"SXFDBType",f:parse_SXFDBType},444:{n:"Prot4RevPass",f:parse_Prot4RevPass},445:{n:"ObNoMacros",f:parse_ObNoMacros},446:{n:"Dv",f:parse_Dv},448:{n:"Excel9File",f:parse_Excel9File},449:{n:"RecalcId",f:parse_RecalcId,r:2},450:{n:"EntExU2",f:parse_EntExU2},512:{n:"Dimensions",f:parse_Dimensions},513:{n:"Blank",f:parse_Blank},515:{n:"Number",f:parse_Number},516:{n:"Label",f:parse_Label},517:{n:"BoolErr",f:parse_BoolErr},519:{n:"String",f:parse_String},520:{n:"Row",f:parse_Row},523:{n:"Index",f:parse_Index},545:{n:"Array",f:parse_Array},549:{n:"DefaultRowHeight",f:parse_DefaultRowHeight},566:{n:"Table",f:parse_Table},574:{n:"Window2",f:parse_Window2},638:{n:"RK",f:parse_RK},659:{n:"Style",f:parse_Style},1048:{n:"BigName",f:parse_BigName},1054:{n:"Format",f:parse_Format},1084:{n:"ContinueBigName",f:parse_ContinueBigName},1212:{n:"ShrFmla",f:parse_ShrFmla},2048:{n:"HLinkTooltip",f:parse_HLinkTooltip},2049:{n:"WebPub",f:parse_WebPub},2050:{n:"QsiSXTag",f:parse_QsiSXTag},2051:{n:"DBQueryExt",f:parse_DBQueryExt},2052:{n:"ExtString",f:parse_ExtString},2053:{n:"TxtQry",f:parse_TxtQry},2054:{n:"Qsir",f:parse_Qsir},2055:{n:"Qsif",f:parse_Qsif},2056:{n:"RRDTQSIF",f:parse_RRDTQSIF},2057:{n:"BOF",f:parse_BOF},2058:{n:"OleDbConn",f:parse_OleDbConn},2059:{n:"WOpt",f:parse_WOpt},2060:{n:"SXViewEx",f:parse_SXViewEx},2061:{n:"SXTH",f:parse_SXTH},2062:{n:"SXPIEx",f:parse_SXPIEx},2063:{n:"SXVDTEx",f:parse_SXVDTEx},2064:{n:"SXViewEx9",f:parse_SXViewEx9},2066:{n:"ContinueFrt",f:parse_ContinueFrt},2067:{n:"RealTimeData",f:parse_RealTimeData},2128:{n:"ChartFrtInfo",f:parse_ChartFrtInfo},2129:{n:"FrtWrapper",f:parse_FrtWrapper},2130:{n:"StartBlock",f:parse_StartBlock},2131:{n:"EndBlock",f:parse_EndBlock},2132:{n:"StartObject",f:parse_StartObject},2133:{n:"EndObject",f:parse_EndObject},2134:{n:"CatLab",f:parse_CatLab},2135:{n:"YMult",f:parse_YMult},2136:{n:"SXViewLink",f:parse_SXViewLink},2137:{n:"PivotChartBits",f:parse_PivotChartBits},2138:{n:"FrtFontList",f:parse_FrtFontList},2146:{n:"SheetExt",f:parse_SheetExt},2147:{n:"BookExt",f:parse_BookExt,r:12},2148:{n:"SXAddl",f:parse_SXAddl},2149:{n:"CrErr",f:parse_CrErr},2150:{n:"HFPicture",f:parse_HFPicture},2151:{n:"FeatHdr",f:parse_FeatHdr},2152:{n:"Feat",f:parse_Feat},2154:{n:"DataLabExt",f:parse_DataLabExt},2155:{n:"DataLabExtContents",f:parse_DataLabExtContents},2156:{n:"CellWatch",f:parse_CellWatch},2161:{n:"FeatHdr11",f:parse_FeatHdr11},2162:{n:"Feature11",f:parse_Feature11},2164:{n:"DropDownObjIds",f:parse_DropDownObjIds},2165:{n:"ContinueFrt11",f:parse_ContinueFrt11},2166:{n:"DConn",f:parse_DConn},2167:{n:"List12",f:parse_List12},2168:{n:"Feature12",f:parse_Feature12},2169:{n:"CondFmt12",f:parse_CondFmt12},2170:{n:"CF12",f:parse_CF12},2171:{n:"CFEx",f:parse_CFEx},2172:{n:"XFCRC",f:parse_XFCRC},2173:{n:"XFExt",f:parse_XFExt},2174:{n:"AutoFilter12",f:parse_AutoFilter12},2175:{n:"ContinueFrt12",f:parse_ContinueFrt12},2180:{n:"MDTInfo",f:parse_MDTInfo},2181:{n:"MDXStr",f:parse_MDXStr},2182:{n:"MDXTuple",f:parse_MDXTuple},2183:{n:"MDXSet",f:parse_MDXSet},2184:{n:"MDXProp",f:parse_MDXProp},2185:{n:"MDXKPI",f:parse_MDXKPI},2186:{n:"MDB",f:parse_MDB},2187:{n:"PLV",f:parse_PLV},2188:{n:"Compat12",f:parse_Compat12,r:12},2189:{n:"DXF",f:parse_DXF},2190:{n:"TableStyles",f:parse_TableStyles,r:12},2191:{n:"TableStyle",f:parse_TableStyle},2192:{n:"TableStyleElement",f:parse_TableStyleElement},2194:{n:"StyleExt",f:parse_StyleExt},2195:{n:"NamePublish",f:parse_NamePublish},2196:{n:"NameCmt",f:parse_NameCmt},2197:{n:"SortData",f:parse_SortData},2198:{n:"Theme",f:parse_Theme},2199:{n:"GUIDTypeLib",f:parse_GUIDTypeLib},2200:{n:"FnGrp12",f:parse_FnGrp12},2201:{n:"NameFnGrp12",f:parse_NameFnGrp12},2202:{n:"MTRSettings",f:parse_MTRSettings,r:12},2203:{n:"CompressPictures",f:parse_CompressPictures},2204:{n:"HeaderFooter",f:parse_HeaderFooter},2205:{n:"CrtLayout12",f:parse_CrtLayout12},2206:{n:"CrtMlFrt",f:parse_CrtMlFrt},2207:{n:"CrtMlFrtContinue",f:parse_CrtMlFrtContinue},2211:{n:"ForceFullCalculation",f:parse_ForceFullCalculation},2212:{n:"ShapePropsStream",f:parse_ShapePropsStream},2213:{n:"TextPropsStream",f:parse_TextPropsStream},2214:{n:"RichTextStream",f:parse_RichTextStream},2215:{n:"CrtLayout12A",f:parse_CrtLayout12A},4097:{n:"Units",f:parse_Units},4098:{n:"Chart",f:parse_Chart},4099:{n:"Series",f:parse_Series},4102:{n:"DataFormat",f:parse_DataFormat},4103:{n:"LineFormat",f:parse_LineFormat},4105:{n:"MarkerFormat",f:parse_MarkerFormat},4106:{n:"AreaFormat",f:parse_AreaFormat},4107:{n:"PieFormat",f:parse_PieFormat},4108:{n:"AttachedLabel",f:parse_AttachedLabel},4109:{n:"SeriesText",f:parse_SeriesText},4116:{n:"ChartFormat",f:parse_ChartFormat},4117:{n:"Legend",f:parse_Legend},4118:{n:"SeriesList",f:parse_SeriesList},4119:{n:"Bar",f:parse_Bar},4120:{n:"Line",f:parse_Line},4121:{n:"Pie",f:parse_Pie},4122:{n:"Area",f:parse_Area},4123:{n:"Scatter",f:parse_Scatter},4124:{n:"CrtLine",f:parse_CrtLine},4125:{n:"Axis",f:parse_Axis},4126:{n:"Tick",f:parse_Tick},4127:{n:"ValueRange",f:parse_ValueRange},4128:{n:"CatSerRange",f:parse_CatSerRange},4129:{n:"AxisLine",f:parse_AxisLine},4130:{n:"CrtLink",f:parse_CrtLink},4132:{n:"DefaultText",f:parse_DefaultText},4133:{n:"Text",f:parse_Text},4134:{n:"FontX",f:parse_FontX},4135:{n:"ObjectLink",f:parse_ObjectLink},4146:{n:"Frame",f:parse_Frame},4147:{n:"Begin",f:parse_Begin},4148:{n:"End",f:parse_End},4149:{n:"PlotArea",f:parse_PlotArea},4154:{n:"Chart3d",f:parse_Chart3d},4156:{n:"PicF",f:parse_PicF},4157:{n:"DropBar",f:parse_DropBar},4158:{n:"Radar",f:parse_Radar},4159:{n:"Surf",f:parse_Surf},4160:{n:"RadarArea",f:parse_RadarArea},4161:{n:"AxisParent",f:parse_AxisParent},4163:{n:"LegendException",f:parse_LegendException},4164:{n:"ShtProps",f:parse_ShtProps},4165:{n:"SerToCrt",f:parse_SerToCrt},4166:{n:"AxesUsed",f:parse_AxesUsed},4168:{n:"SBaseRef",f:parse_SBaseRef},4170:{n:"SerParent",f:parse_SerParent},4171:{n:"SerAuxTrend",f:parse_SerAuxTrend},4174:{n:"IFmtRecord",f:parse_IFmtRecord},4175:{n:"Pos",f:parse_Pos},4176:{n:"AlRuns",f:parse_AlRuns},4177:{n:"BRAI",f:parse_BRAI},4187:{n:"SerAuxErrBar",f:parse_SerAuxErrBar},4188:{n:"ClrtClient",f:parse_ClrtClient},4189:{n:"SerFmt",f:parse_SerFmt},4191:{n:"Chart3DBarShape",f:parse_Chart3DBarShape},4192:{n:"Fbi",f:parse_Fbi},4193:{n:"BopPop",f:parse_BopPop},4194:{n:"AxcExt",f:parse_AxcExt},4195:{n:"Dat",f:parse_Dat},4196:{n:"PlotGrowth",f:parse_PlotGrowth},4197:{n:"SIIndex",f:parse_SIIndex},4198:{n:"GelFrame",f:parse_GelFrame},4199:{n:"BopPopCustom",f:parse_BopPopCustom},4200:{n:"Fbi2",f:parse_Fbi2},22:{n:"ExternCount",f:parsenoop},126:{n:"RK",f:parsenoop},127:{n:"ImData",f:parsenoop},135:{n:"Addin",f:parsenoop},136:{n:"Edg",f:parsenoop},137:{n:"Pub",f:parsenoop},145:{n:"Sub",f:parsenoop},148:{n:"LHRecord",f:parsenoop},149:{n:"LHNGraph",f:parsenoop},150:{n:"Sound",f:parsenoop},169:{n:"CoordList",f:parsenoop},171:{n:"GCW",f:parsenoop},188:{n:"ShrFmla",f:parsenoop},194:{n:"AddMenu",f:parsenoop},195:{n:"DelMenu",f:parsenoop},214:{n:"RString",f:parsenoop},223:{n:"UDDesc",f:parsenoop},234:{n:"TabIdConf",f:parsenoop},354:{n:"XL5Modify",f:parsenoop},421:{n:"FileSharing2",f:parsenoop},536:{n:"Name",f:parsenoop},547:{n:"ExternName",f:parse_ExternName},561:{n:"Font",f:parsenoop},1030:{n:"Formula",f:parse_Formula},2157:{n:"FeatInfo",f:parsenoop},2163:{n:"FeatInfo11",f:parsenoop},2177:{n:"SXAddl12",f:parsenoop},2240:{n:"AutoWebPub",f:parsenoop},2241:{n:"ListObj",f:parsenoop},2242:{n:"ListField",f:parsenoop},2243:{n:"ListDV",f:parsenoop},2244:{n:"ListCondFmt",f:parsenoop},2245:{n:"ListCF",f:parsenoop},2246:{n:"FMQry",f:parsenoop},2247:{n:"FMSQry",f:parsenoop},2248:{n:"PLV",f:parsenoop},2249:{n:"LnExt",f:parsenoop},2250:{n:"MkrExt",f:parsenoop},2251:{n:"CrtCoopt",f:parsenoop},0:{}};var CountryEnum={1:"US",2:"CA",3:"",7:"RU",20:"EG",30:"GR",31:"NL",32:"BE",33:"FR",34:"ES",36:"HU",39:"IT",41:"CH",43:"AT",44:"GB",45:"DK",46:"SE",47:"NO",48:"PL",49:"DE",52:"MX",55:"BR",61:"AU",64:"NZ",66:"TH",81:"JP",82:"KR",84:"VN",86:"CN",90:"TR",105:"JS",213:"DZ",216:"MA",218:"LY",351:"PT",354:"IS",358:"FI",420:"CZ",886:"TW",961:"LB",962:"JO",963:"SY",964:"IQ",965:"KW",966:"SA",971:"AE",972:"IL",974:"QA",981:"IR",65535:"US"};function fix_opts_func(defaults){return function fix_opts(opts){for(var i=0;i!=defaults.length;++i){var d=defaults[i];if(typeof opts[d[0]]==="undefined")opts[d[0]]=d[1];if(d[2]==="n")opts[d[0]]=Number(opts[d[0]])}}}var fixopts=fix_opts_func([["cellNF",false],["cellFormula",true],["sheetRows",0,"n"],["bookSheets",false],["bookProps",false],["bookFiles",false],["password",""],["WTF",false]]);function parse_compobj(obj){var v={};var o=obj.content;var l=28,m;m=__lpstr(o,l);l+=4+__readUInt32LE(o,l);v.UserType=m;m=__readUInt32LE(o,l);l+=4;switch(m){case 0:break;case 4294967295:case 4294967294:l+=4;break;default:if(m>400)throw new Error("Unsupported Clipboard: "+m.toString(16));l+=m}m=__lpstr(o,l);l+=m.length===0?0:5+m.length;v.Reserved1=m;if((m=__readUInt32LE(o,l))!==1907550708)return v;throw"Unsupported Unicode Extension"}function parse_xlscfb(cfb,options){if(!options)options={};fixopts(options);reset_cp();var CompObj=cfb.find("!CompObj");var Summary=cfb.find("!SummaryInformation");var Workbook=cfb.find("/Workbook");if(!Workbook)Workbook=cfb.find("/Book");var CompObjP,SummaryP,WorkbookP;function slurp(R,blob,length,opts){var l=length;var bufs=[];var d=blob.slice(blob.l,blob.l+l);if(opts.enc&&opts.enc.insitu_decrypt)switch(R.n){case"BOF":case"FilePass":case"FileLock":case"InterfaceHdr":case"RRDInfo":case"RRDHead":case"UsrExcl":break;default:if(d.length===0)break;opts.enc.insitu_decrypt(d)}bufs.push(d);blob.l+=l;var next=RecordEnum[__readUInt16LE(blob,blob.l)];while(next!=null&&next.n==="Continue"){l=__readUInt16LE(blob,blob.l+2);bufs.push(blob.slice(blob.l+4,blob.l+4+l));blob.l+=4+l;next=RecordEnum[__readUInt16LE(blob,blob.l)]}var b=bconcat(bufs);prep_blob(b,0);var ll=0;b.lens=[];for(var j=0;j<bufs.length;++j){b.lens.push(ll);ll+=bufs[j].length}return R.f(b,b.length,opts)}function safe_format_xf(p,opts){if(!p.XF)return;try{var fmtid=p.XF.ifmt||0;if(fmtid===0){if(p.t==="n"){if((p.v|0)===p.v)p.w=SSF._general_int(p.v);else p.w=SSF._general_num(p.v)}else p.w=SSF._general(p.v)}else p.w=SSF.format(fmtid,p.v);if(opts.cellNF)p.z=SSF._table[fmtid]}catch(e){if(opts.WTF)throw e}}function make_cell(val,ixfe,t){return{v:val,ixfe:ixfe,t:t}}function parse_workbook(blob,options){var wb={opts:{}};var Sheets={};var out={};var Directory={};var found_sheet=false;var range={};var last_formula=null;var sst=[];var cur_sheet="";var Preamble={};var lastcell,last_cell,cc,cmnt,rng,rngC,rngR;var shared_formulae={};var array_formulae=[];var temp_val;var country;var cell_valid=true;var XFs=[];var addline=function addline(cell,line,options){if(!cell_valid)return;lastcell=cell;last_cell=encode_cell(cell);if(range.s){if(cell.r<range.s.r)range.s.r=cell.r;if(cell.c<range.s.c)range.s.c=cell.c}if(range.e){if(cell.r+1>range.e.r)range.e.r=cell.r+1;if(cell.c+1>range.e.c)range.e.c=cell.c+1}if(options.sheetRows&&lastcell.r>=options.sheetRows)cell_valid=false;else out[last_cell]=line};var opts={enc:false,sbcch:0,snames:[],sharedf:shared_formulae,arrayf:array_formulae,rrtabid:[],lastuser:"",biff:8,codepage:0,winlocked:0,wtf:false};if(options.password)opts.password=options.password;var mergecells=[];var objects=[];var supbooks=[[]];var sbc=0,sbci=0,sbcli=0;supbooks.SheetNames=opts.snames;supbooks.sharedf=opts.sharedf;supbooks.arrayf=opts.arrayf;var last_Rn="";var file_depth=0;while(blob.l<blob.length-1){var s=blob.l;var RecordType=blob.read_shift(2);if(RecordType===0&&last_Rn==="EOF")break;var length=blob.l===blob.length?0:blob.read_shift(2),y;var R=RecordEnum[RecordType];if(R&&R.f){if(options.bookSheets){if(last_Rn==="BoundSheet8"&&R.n!=="BoundSheet8")break}last_Rn=R.n;if(R.r===2||R.r==12){var rt=blob.read_shift(2);length-=2;if(!opts.enc&&rt!==RecordType)throw"rt mismatch";if(R.r==12){blob.l+=10;length-=10}}var val;if(R.n==="EOF")val=R.f(blob,length,opts);else val=slurp(R,blob,length,opts);var Rn=R.n;if(opts.biff===5)switch(Rn){case"Lbl":Rn="Label";break}switch(Rn){case"Date1904":wb.opts.Date1904=val;break;case"WriteProtect":wb.opts.WriteProtect=true;break;case"FilePass":if(!opts.enc)blob.l=0;opts.enc=val;if(opts.WTF)console.error(val);if(!options.password)throw new Error("File is password-protected");if(val.Type!==0)throw new Error("Encryption scheme unsupported");if(!val.valid)throw new Error("Password is incorrect");break;case"WriteAccess":opts.lastuser=val;break;case"FileSharing":break;case"CodePage":if(val===21010)val=1200;opts.codepage=val;set_cp(val);break;case"RRTabId":opts.rrtabid=val;break;case"WinProtect":opts.winlocked=val;break;case"Template":break;case"RefreshAll":wb.opts.RefreshAll=val;break;case"BookBool":break;case"UsesELFs":break;case"MTRSettings":{if(val[0]&&val[1])throw"Unsupported threads: "+val}break;case"CalcCount":wb.opts.CalcCount=val;break;case"CalcDelta":wb.opts.CalcDelta=val;break;case"CalcIter":wb.opts.CalcIter=val;break;case"CalcMode":wb.opts.CalcMode=val;break;case"CalcPrecision":wb.opts.CalcPrecision=val;break;case"CalcSaveRecalc":wb.opts.CalcSaveRecalc=val;break;case"CalcRefMode":opts.CalcRefMode=val;break;case"Uncalced":break;case"ForceFullCalculation":wb.opts.FullCalc=val;break;case"WsBool":break;case"XF":XFs.push(val);break;case"ExtSST":break;case"BookExt":break;case"RichTextStream":break;case"BkHim":break;case"SupBook":supbooks[++sbc]=[val];sbci=0;break;case"ExternName":supbooks[sbc][++sbci]=val;break;case"Index":break;case"Lbl":supbooks[0][++sbcli]=val;break;case"ExternSheet":supbooks[sbc]=supbooks[sbc].concat(val);sbci+=val.length;break;case"Protect":out["!protect"]=val;break;case"Password":if(val!==0&&opts.WTF)console.error("Password verifier: "+val);break;case"Prot4Rev":case"Prot4RevPass":break;case"BoundSheet8":{Directory[val.pos]=val;opts.snames.push(val.name)}break;case"EOF":{if(--file_depth)break;if(range.e){out["!range"]=range;if(range.e.r>0&&range.e.c>0){range.e.r--;range.e.c--;out["!ref"]=encode_range(range);range.e.r++;range.e.c++}if(mergecells.length>0)out["!merges"]=mergecells;if(objects.length>0)out["!objects"]=objects}if(cur_sheet==="")Preamble=out;else Sheets[cur_sheet]=out;out={}}break;case"BOF":{if(val.BIFFVer===1280)opts.biff=5;if(file_depth++)break;cell_valid=true;out={};cur_sheet=(Directory[s]||{name:""}).name;mergecells=[];objects=[]}break;case"Number":{temp_val={ixfe:val.ixfe,XF:XFs[val.ixfe],v:val.val,t:"n"};if(temp_val.XF)safe_format_xf(temp_val,options);addline({c:val.c,r:val.r},temp_val,options)}break;case"BoolErr":{temp_val={ixfe:val.ixfe,XF:XFs[val.ixfe],v:val.val,t:val.t};if(temp_val.XF)safe_format_xf(temp_val,options);addline({c:val.c,r:val.r},temp_val,options)}break;case"RK":{temp_val={ixfe:val.ixfe,XF:XFs[val.ixfe],v:val.rknum,t:"n"};if(temp_val.XF)safe_format_xf(temp_val,options);addline({c:val.c,r:val.r},temp_val,options)}break;case"MulRk":{for(var j=val.c;j<=val.C;++j){var ixfe=val.rkrec[j-val.c][0];temp_val={ixfe:ixfe,XF:XFs[ixfe],v:val.rkrec[j-val.c][1],t:"n"};if(temp_val.XF)safe_format_xf(temp_val,options);addline({c:j,r:val.r},temp_val,options)}}break;case"Formula":{switch(val.val){case"String":last_formula=val;break;case"Array Formula":throw"Array Formula unsupported";default:temp_val={v:val.val,ixfe:val.cell.ixfe,t:val.tt};temp_val.XF=XFs[temp_val.ixfe];if(options.cellFormula)temp_val.f="="+stringify_formula(val.formula,range,val.cell,supbooks,opts);if(temp_val.XF)safe_format_xf(temp_val,options);addline(val.cell,temp_val,options);last_formula=val}}break;case"String":{if(last_formula){last_formula.val=val;temp_val={v:last_formula.val,ixfe:last_formula.cell.ixfe,t:"s"};temp_val.XF=XFs[temp_val.ixfe];if(options.cellFormula)temp_val.f="="+stringify_formula(last_formula.formula,range,last_formula.cell,supbooks,opts);if(temp_val.XF)safe_format_xf(temp_val,options);addline(last_formula.cell,temp_val,options);last_formula=null}}break;case"Array":{array_formulae.push(val)}break;case"ShrFmla":{if(!cell_valid)break;shared_formulae[encode_cell(last_formula.cell)]=val[0]}break;case"LabelSst":temp_val=make_cell(sst[val.isst].t,val.ixfe,"s");temp_val.XF=XFs[temp_val.ixfe];if(temp_val.XF)safe_format_xf(temp_val,options);addline({c:val.c,r:val.r},temp_val,options);break;case"Label":temp_val=make_cell(val.val,val.ixfe,"s");temp_val.XF=XFs[temp_val.ixfe];
+if(temp_val.XF)safe_format_xf(temp_val,options);addline({c:val.c,r:val.r},temp_val,options);break;case"Dimensions":{if(file_depth===1)range=val}break;case"SST":{sst=val}break;case"Format":{SSF.load(val[1],val[0])}break;case"MergeCells":mergecells=mergecells.concat(val);break;case"Obj":objects[val.cmo[0]]=opts.lastobj=val;break;case"TxO":opts.lastobj.TxO=val;break;case"HLink":{for(rngR=val[0].s.r;rngR<=val[0].e.r;++rngR)for(rngC=val[0].s.c;rngC<=val[0].e.c;++rngC)if(out[encode_cell({c:rngC,r:rngR})])out[encode_cell({c:rngC,r:rngR})].l=val[1]}break;case"HLinkTooltip":{for(rngR=val[0].s.r;rngR<=val[0].e.r;++rngR)for(rngC=val[0].s.c;rngC<=val[0].e.c;++rngC)if(out[encode_cell({c:rngC,r:rngR})])out[encode_cell({c:rngC,r:rngR})].l.tooltip=val[1]}break;case"Note":{if(opts.biff===5)break;cc=out[encode_cell(val[0])];var noteobj=objects[val[2]];if(!cc)break;if(!cc.c)cc.c=[];cmnt={a:val[1],t:noteobj.TxO.t};cc.c.push(cmnt)}break;case"NameCmt":break;default:switch(R.n){case"Header":break;case"Footer":break;case"HCenter":break;case"VCenter":break;case"Pls":break;case"Setup":break;case"DefColWidth":break;case"GCW":break;case"LHRecord":break;case"ColInfo":break;case"Row":break;case"DBCell":break;case"MulBlank":break;case"EntExU2":break;case"SxView":break;case"Sxvd":break;case"SXVI":break;case"SXVDEx":break;case"SxIvd":break;case"SXDI":break;case"SXLI":break;case"SXEx":break;case"QsiSXTag":break;case"Selection":break;case"Feat":break;case"FeatHdr":case"FeatHdr11":break;case"Feature11":case"Feature12":case"List12":break;case"Blank":break;case"Country":country=val;break;case"RecalcId":break;case"DefaultRowHeight":case"DxGCol":break;case"Fbi":case"Fbi2":case"GelFrame":break;case"Font":break;case"XFCRC":break;case"XFExt":break;case"Style":break;case"StyleExt":break;case"Palette":break;case"ClrtClient":break;case"Theme":break;case"ScenarioProtect":break;case"ObjProtect":break;case"CondFmt12":break;case"Table":break;case"TableStyles":break;case"TableStyle":break;case"TableStyleElement":break;case"SXStreamID":break;case"SXVS":break;case"DConRef":break;case"SXAddl":break;case"DConName":break;case"SXPI":break;case"SxFormat":break;case"SxSelect":break;case"SxRule":break;case"SxFilt":break;case"SxItm":break;case"SxDXF":break;case"ScenMan":break;case"DCon":break;case"CellWatch":break;case"PrintRowCol":break;case"PrintGrid":break;case"PrintSize":break;case"XCT":break;case"CRN":break;case"Scl":{}break;case"SheetExt":{}break;case"SheetExtOptional":{}break;case"ObNoMacros":{}break;case"ObProj":{}break;case"CodeName":{}break;case"GUIDTypeLib":{}break;case"WOpt":break;case"PhoneticInfo":break;case"OleObjectSize":break;case"DXF":case"DXFN":case"DXFN12":case"DXFN12List":case"DXFN12NoCB":break;case"Dv":case"DVal":break;case"BRAI":case"Series":case"SeriesText":break;case"DConn":break;case"DbOrParamQry":break;case"DBQueryExt":break;case"IFmtRecord":break;case"CondFmt":case"CF":case"CF12":case"CFEx":break;case"Excel9File":break;case"Units":break;case"InterfaceHdr":case"Mms":case"InterfaceEnd":case"DSF":case"BuiltInFnGroupCount":case"Window1":case"Window2":case"HideObj":case"GridSet":case"Guts":case"UserBView":case"UserSViewBegin":case"UserSViewEnd":case"Pane":break;default:switch(R.n){case"Dat":case"Begin":case"End":case"StartBlock":case"EndBlock":case"Frame":case"Area":case"Axis":case"AxisLine":case"Tick":break;case"AxesUsed":case"CrtLayout12":case"CrtLayout12A":case"CrtLink":case"CrtLine":case"CrtMlFrt":break;case"LineFormat":case"AreaFormat":case"Chart":case"Chart3d":case"Chart3DBarShape":case"ChartFormat":case"ChartFrtInfo":break;case"PlotArea":case"PlotGrowth":break;case"SeriesList":case"SerParent":case"SerAuxTrend":break;case"DataFormat":case"SerToCrt":case"FontX":break;case"CatSerRange":case"AxcExt":case"SerFmt":break;case"ShtProps":break;case"DefaultText":case"Text":case"CatLab":break;case"DataLabExtContents":break;case"Legend":case"LegendException":break;case"Pie":case"Scatter":break;case"PieFormat":case"MarkerFormat":break;case"StartObject":case"EndObject":break;case"AlRuns":case"ObjectLink":break;case"SIIndex":break;case"AttachedLabel":break;case"Line":case"Bar":break;case"Surf":break;case"AxisParent":break;case"Pos":break;case"ValueRange":break;case"SXViewEx9":break;case"SXViewLink":break;case"PivotChartBits":break;case"SBaseRef":break;case"TextPropsStream":break;case"LnExt":break;case"MkrExt":break;case"CrtCoopt":break;case"Qsi":case"Qsif":case"Qsir":case"QsiSXTag":break;case"TxtQry":break;case"FilterMode":break;case"AutoFilter":case"AutoFilterInfo":break;case"AutoFilter12":break;case"DropDownObjIds":break;case"Sort":break;case"SortData":break;case"ShapePropsStream":break;case"MsoDrawing":case"MsoDrawingGroup":case"MsoDrawingSelection":break;case"ImData":break;case"WebPub":case"AutoWebPub":case"RightMargin":case"LeftMargin":case"TopMargin":case"BottomMargin":case"HeaderFooter":case"HFPicture":case"PLV":case"HorizontalPageBreaks":case"VerticalPageBreaks":case"Backup":case"CompressPictures":case"Compat12":break;case"Continue":case"ContinueFrt12":break;case"ExternCount":break;case"RString":break;case"TabIdConf":case"Radar":case"RadarArea":case"DropBar":case"Intl":case"CoordList":case"SerAuxErrBar":break;default:if(options.WTF)throw"Unrecognized Record "+R.n}}}}else blob.l+=length}var sheetnamesraw=Object.keys(Directory).sort(function(a,b){return Number(a)-Number(b)}).map(function(x){return Directory[x].name});var sheetnames=sheetnamesraw.slice();wb.Directory=sheetnamesraw;wb.SheetNames=sheetnamesraw;if(!options.bookSheets)wb.Sheets=Sheets;wb.Preamble=Preamble;wb.Strings=sst;wb.SSF=SSF.get_table();if(opts.enc)wb.Encryption=opts.enc;wb.Metadata={};if(country!==undefined)wb.Metadata.Country=country;return wb}if(CompObj)CompObjP=parse_compobj(CompObj);if(options.bookProps&&!options.bookSheets)WorkbookP={};else{if(Workbook)WorkbookP=parse_workbook(Workbook.content,options);else throw new Error("Cannot find Workbook stream")}parse_props(cfb);var props={};for(var y in cfb.Summary)props[y]=cfb.Summary[y];for(y in cfb.DocSummary)props[y]=cfb.DocSummary[y];WorkbookP.Props=WorkbookP.Custprops=props;if(options.bookFiles)WorkbookP.cfb=cfb;WorkbookP.CompObjP=CompObjP;return WorkbookP}function parse_props(cfb){var DSI=cfb.find("!DocumentSummaryInformation");if(DSI)try{cfb.DocSummary=parse_PropertySetStream(DSI,DocSummaryPIDDSI)}catch(e){}var SI=cfb.find("!SummaryInformation");if(SI)try{cfb.Summary=parse_PropertySetStream(SI,SummaryPIDSI)}catch(e){}}var encregex=/&[a-z]*;/g,coderegex=/_x([0-9a-fA-F]+)_/g;function coderepl(m,c){return _chr(parseInt(c,16))}function encrepl($$){return encodings[$$]}function unescapexml(s){if(s.indexOf("&")>-1)s=s.replace(encregex,encrepl);return s.indexOf("_")===-1?s:s.replace(coderegex,coderepl)}function parsexmlbool(value,tag){switch(value){case"1":case"true":case"TRUE":return true;default:return false}}function matchtag(f,g){return new RegExp("<"+f+'(?: xml:space="preserve")?>([^☃]*)</'+f+">",(g||"")+"m")}var entregex=/&#(\d+);/g;function entrepl($$,$1){return String.fromCharCode(parseInt($1,10))}function fixstr(str){return str.replace(entregex,entrepl)}var everted_BERR=evert(BERR);var magic_formats={"General Number":"General","General Date":SSF._table[22],"Long Date":"dddd, mmmm dd, yyyy","Medium Date":SSF._table[15],"Short Date":SSF._table[14],"Long Time":SSF._table[19],"Medium Time":SSF._table[18],"Short Time":SSF._table[20],Currency:'"$"#,##0.00_);[Red]\\("$"#,##0.00\\)',Fixed:SSF._table[2],Standard:SSF._table[4],Percent:SSF._table[10],Scientific:SSF._table[11],"Yes/No":'"Yes";"Yes";"No";@',"True/False":'"True";"True";"False";@',"On/Off":'"Yes";"Yes";"No";@'};function xlml_format(format,value){var fmt=magic_formats[format]||unescapexml(format);if(fmt==="General")return SSF._general(value);return SSF.format(fmt,value)}function xlml_set_prop(Props,tag,val){switch(tag){case"Description":tag="Comments";break}Props[tag]=val}function xlml_set_custprop(Custprops,Rn,cp,val){switch((cp[0].match(/dt:dt="([\w.]+)"/)||["",""])[1]){case"boolean":val=parsexmlbool(val);break;case"i2":case"int":val=parseInt(val,10);break;case"r4":case"float":val=parseFloat(val);break;case"date":case"dateTime.tz":val=new Date(val);break;case"i8":case"string":case"fixed":case"uuid":case"bin.base64":break;default:throw"bad custprop:"+cp[0]}Custprops[unescapexml(Rn[3])]=val}function safe_format_xlml(cell,nf,o){try{if(nf==="General"){if(cell.t==="n"){if((cell.v|0)===cell.v)cell.w=SSF._general_int(cell.v);else cell.w=SSF._general_num(cell.v)}else cell.w=SSF._general(cell.v)}else cell.w=xlml_format(nf||"General",cell.v);if(o.cellNF)cell.z=magic_formats[nf]||nf||"General"}catch(e){if(o.WTF)throw e}}function parse_xlml_data(xml,ss,data,cell,base,styles,csty,o){var nf="General",sid=cell.StyleID;o=o||{};if(sid===undefined&&csty)sid=csty.StyleID;while(styles[sid]!==undefined){if(styles[sid].nf)nf=styles[sid].nf;if(!styles[sid].Parent)break;sid=styles[sid].Parent}switch(data.Type){case"Boolean":cell.t="b";cell.v=parsexmlbool(xml);break;case"String":cell.t="str";cell.r=fixstr(unescapexml(xml));cell.v=xml.indexOf("<")>-1?ss:cell.r;break;case"DateTime":cell.v=(Date.parse(xml)-new Date(Date.UTC(1899,11,30)))/(24*60*60*1e3);if(cell.v!==cell.v)cell.v=unescapexml(xml);else if(cell.v>=1&&cell.v<60)cell.v=cell.v-1;if(!nf||nf=="General")nf="yyyy-mm-dd";case"Number":if(cell.v===undefined)cell.v=+xml;if(!cell.t)cell.t="n";break;case"Error":cell.t="e";cell.v=xml;cell.w=xml;break;default:cell.t="s";cell.v=fixstr(ss);break}if(cell.t!=="e")safe_format_xlml(cell,nf,o);if(o.cellFormula!=null&&cell.Formula){cell.f=rc_to_a1(unescapexml(cell.Formula),base);cell.Formula=undefined}cell.ixfe=cell.StyleID!==undefined?cell.StyleID:"Default"}function xlml_clean_comment(comment){comment.t=comment.v;comment.v=comment.w=comment.ixfe=undefined}function xlml_normalize(d){if(has_buf&&Buffer.isBuffer(d))return d.toString("utf8");if(typeof d==="string")return d;throw"badf"}var xlmlregex=/<(\/?)([a-z0-9]*:|)(\w+)[^>]*>/gm;function parse_xlml_xml(d,opts){var str=xlml_normalize(d);var Rn;var state=[],tmp;var sheets={},sheetnames=[],cursheet={},sheetname="";var table={},cell={},row={},dtag,didx;var c=0,r=0;var refguess={s:{r:1e6,c:1e6},e:{r:0,c:0}};var styles={},stag={};var ss="",fidx=0;var mergecells=[];var Props={},Custprops={},pidx=0,cp={};var comments=[],comment={};var cstys=[],csty;while(Rn=xlmlregex.exec(str))switch(Rn[3]){case"Data":if(state[state.length-1][1])break;if(Rn[1]==="/")parse_xlml_data(str.slice(didx,Rn.index),ss,dtag,state[state.length-1][0]=="Comment"?comment:cell,{c:c,r:r},styles,cstys[c],opts);else{ss="";dtag=parsexmltag(Rn[0]);didx=Rn.index+Rn[0].length}break;case"Cell":if(Rn[1]==="/"){if(comments.length>0)cell.c=comments;if((!opts.sheetRows||opts.sheetRows>r)&&cell.v!==undefined)cursheet[encode_col(c)+encode_row(r)]=cell;if(cell.HRef){cell.l={Target:cell.HRef,tooltip:cell.HRefScreenTip};cell.HRef=cell.HRefScreenTip=undefined}if(cell.MergeAcross||cell.MergeDown){var cc=c+(parseInt(cell.MergeAcross,10)|0);var rr=r+(parseInt(cell.MergeDown,10)|0);mergecells.push({s:{c:c,r:r},e:{c:cc,r:rr}})}++c;if(cell.MergeAcross)c+=+cell.MergeAcross}else{cell=parsexmltagobj(Rn[0]);if(cell.Index)c=+cell.Index-1;if(c<refguess.s.c)refguess.s.c=c;if(c>refguess.e.c)refguess.e.c=c;if(Rn[0].substr(-2)==="/>")++c;comments=[]}break;case"Row":if(Rn[1]==="/"||Rn[0].substr(-2)==="/>"){if(r<refguess.s.r)refguess.s.r=r;if(r>refguess.e.r)refguess.e.r=r;if(Rn[0].substr(-2)==="/>"){row=parsexmltag(Rn[0]);if(row.Index)r=+row.Index-1}c=0;++r}else{row=parsexmltag(Rn[0]);if(row.Index)r=+row.Index-1}break;case"Worksheet":if(Rn[1]==="/"){if((tmp=state.pop())[0]!==Rn[3])throw"Bad state: "+tmp;sheetnames.push(sheetname);if(refguess.s.r<=refguess.e.r&&refguess.s.c<=refguess.e.c)cursheet["!ref"]=encode_range(refguess);if(mergecells.length)cursheet["!merges"]=mergecells;sheets[sheetname]=cursheet}else{refguess={s:{r:1e6,c:1e6},e:{r:0,c:0}};r=c=0;state.push([Rn[3],false]);tmp=parsexmltag(Rn[0]);sheetname=tmp.Name;cursheet={};mergecells=[]}break;case"Table":if(Rn[1]==="/"){if((tmp=state.pop())[0]!==Rn[3])throw"Bad state: "+tmp}else if(Rn[0].slice(-2)=="/>")break;else{table=parsexmltag(Rn[0]);state.push([Rn[3],false]);cstys=[]}break;case"Style":if(Rn[1]==="/")styles[stag.ID]=stag;else stag=parsexmltag(Rn[0]);break;case"NumberFormat":stag.nf=parsexmltag(Rn[0]).Format||"General";break;case"Column":if(state[state.length-1][0]!=="Table")break;csty=parsexmltag(Rn[0]);cstys[csty.Index-1||cstys.length]=csty;for(var i=0;i<+csty.Span;++i)cstys[cstys.length]=csty;break;case"NamedRange":break;case"NamedCell":break;case"B":break;case"I":break;case"U":break;case"S":break;case"Sub":break;case"Sup":break;case"Span":break;case"Border":break;case"Alignment":break;case"Borders":break;case"Font":if(Rn[0].substr(-2)==="/>")break;else if(Rn[1]==="/")ss+=str.slice(fidx,Rn.index);else fidx=Rn.index+Rn[0].length;break;case"Interior":break;case"Protection":break;case"Author":case"Title":case"Description":case"Created":case"Keywords":case"Subject":case"Category":case"Company":case"LastAuthor":case"LastSaved":case"LastPrinted":case"Version":case"Revision":case"TotalTime":case"HyperlinkBase":case"Manager":if(Rn[0].substr(-2)==="/>")break;else if(Rn[1]==="/")xlml_set_prop(Props,Rn[3],str.slice(pidx,Rn.index));else pidx=Rn.index+Rn[0].length;break;case"Paragraphs":break;case"Styles":case"Workbook":if(Rn[1]==="/"){if((tmp=state.pop())[0]!==Rn[3])throw"Bad state: "+tmp}else state.push([Rn[3],false]);break;case"Comment":if(Rn[1]==="/"){if((tmp=state.pop())[0]!==Rn[3])throw"Bad state: "+tmp;xlml_clean_comment(comment);comments.push(comment)}else{state.push([Rn[3],false]);tmp=parsexmltag(Rn[0]);comment={a:tmp.Author}}break;case"Name":break;case"ComponentOptions":case"DocumentProperties":case"CustomDocumentProperties":case"OfficeDocumentSettings":case"PivotTable":case"PivotCache":case"Names":case"MapInfo":case"PageBreaks":case"QueryTable":case"DataValidation":case"AutoFilter":case"Sorting":case"Schema":case"data":case"ConditionalFormatting":case"SmartTagType":case"SmartTags":case"ExcelWorkbook":case"WorkbookOptions":case"WorksheetOptions":if(Rn[1]==="/"){if((tmp=state.pop())[0]!==Rn[3])throw"Bad state: "+tmp}else if(Rn[0].charAt(Rn[0].length-2)!=="/")state.push([Rn[3],true]);break;default:var seen=true;switch(state[state.length-1][0]){case"OfficeDocumentSettings":switch(Rn[3]){case"AllowPNG":break;case"RemovePersonalInformation":break;case"DownloadComponents":break;case"LocationOfComponents":break;case"Colors":break;case"Color":break;case"Index":break;case"RGB":break;case"PixelsPerInch":break;case"TargetScreenSize":break;case"ReadOnlyRecommended":break;default:seen=false}break;case"ComponentOptions":switch(Rn[3]){case"Toolbar":break;case"HideOfficeLogo":break;case"SpreadsheetAutoFit":break;case"Label":break;case"Caption":break;case"MaxHeight":break;case"MaxWidth":break;case"NextSheetNumber":break;default:seen=false}break;case"ExcelWorkbook":switch(Rn[3]){case"WindowHeight":break;case"WindowWidth":break;case"WindowTopX":break;case"WindowTopY":break;case"TabRatio":break;case"ProtectStructure":break;case"ProtectWindows":break;case"ActiveSheet":break;case"DisplayInkNotes":break;case"FirstVisibleSheet":break;case"SupBook":break;case"SheetName":break;case"SheetIndex":break;case"SheetIndexFirst":break;case"SheetIndexLast":break;case"Dll":break;case"AcceptLabelsInFormulas":break;case"DoNotSaveLinkValues":break;case"Date1904":break;case"Iteration":break;case"MaxIterations":break;case"MaxChange":break;case"Path":break;case"Xct":break;case"Count":break;case"SelectedSheets":break;case"Calculation":break;case"Uncalced":break;case"StartupPrompt":break;case"Crn":break;case"ExternName":break;case"Formula":break;case"ColFirst":break;case"ColLast":break;case"WantAdvise":break;case"Boolean":break;case"Error":break;case"Text":break;case"OLE":break;case"NoAutoRecover":break;case"PublishObjects":break;case"DoNotCalculateBeforeSave":break;case"Number":break;case"RefModeR1C1":break;case"EmbedSaveSmartTags":break;default:seen=false}break;case"WorkbookOptions":switch(Rn[3]){case"OWCVersion":break;case"Height":break;case"Width":break;default:seen=false}break;case"WorksheetOptions":switch(Rn[3]){case"Unsynced":break;case"Visible":break;case"Print":break;case"Panes":break;case"Scale":break;case"Pane":break;case"Number":break;case"Layout":break;case"Header":break;case"Footer":break;case"PageSetup":break;case"PageMargins":break;case"Selected":break;case"ProtectObjects":break;case"EnableSelection":break;case"ProtectScenarios":break;case"ValidPrinterInfo":break;case"HorizontalResolution":break;case"VerticalResolution":break;case"NumberofCopies":break;case"ActiveRow":break;case"ActiveCol":break;case"ActivePane":break;case"TopRowVisible":break;case"TopRowBottomPane":break;case"LeftColumnVisible":break;case"LeftColumnRightPane":break;case"FitToPage":break;case"RangeSelection":break;case"PaperSizeIndex":break;case"PageLayoutZoom":break;case"PageBreakZoom":break;case"FilterOn":break;case"DoNotDisplayGridlines":break;case"SplitHorizontal":break;case"SplitVertical":break;case"FreezePanes":break;case"FrozenNoSplit":break;case"FitWidth":break;case"FitHeight":break;case"CommentsLayout":break;case"Zoom":break;case"LeftToRight":break;case"Gridlines":break;case"AllowSort":break;case"AllowFilter":break;case"AllowInsertRows":break;case"AllowDeleteRows":break;case"AllowInsertCols":break;case"AllowDeleteCols":break;case"AllowInsertHyperlinks":break;case"AllowFormatCells":break;case"AllowSizeCols":break;case"AllowSizeRows":break;case"NoSummaryRowsBelowDetail":break;case"TabColorIndex":break;case"DoNotDisplayHeadings":break;case"ShowPageLayoutZoom":break;case"NoSummaryColumnsRightDetail":break;case"BlackAndWhite":break;case"DoNotDisplayZeros":break;case"DisplayPageBreak":break;case"RowColHeadings":break;case"DoNotDisplayOutline":break;case"NoOrientation":break;case"AllowUsePivotTables":break;case"ZeroHeight":break;case"ViewableRange":break;case"Selection":break;case"ProtectContents":break;default:seen=false}break;case"PivotTable":case"PivotCache":switch(Rn[3]){case"ImmediateItemsOnDrop":break;case"ShowPageMultipleItemLabel":break;case"CompactRowIndent":break;case"Location":break;case"PivotField":break;case"Orientation":break;case"LayoutForm":break;case"LayoutSubtotalLocation":break;case"LayoutCompactRow":break;case"Position":break;case"PivotItem":break;case"DataType":break;case"DataField":break;case"SourceName":break;case"ParentField":break;case"PTLineItems":break;case"PTLineItem":break;case"CountOfSameItems":break;case"Item":break;case"ItemType":break;case"PTSource":break;case"CacheIndex":break;case"ConsolidationReference":break;case"FileName":break;case"Reference":break;case"NoColumnGrand":break;case"NoRowGrand":break;case"BlankLineAfterItems":break;case"Hidden":break;case"Subtotal":break;case"BaseField":break;case"MapChildItems":break;case"Function":break;case"RefreshOnFileOpen":break;case"PrintSetTitles":break;case"MergeLabels":break;case"DefaultVersion":break;case"RefreshName":break;case"RefreshDate":break;case"RefreshDateCopy":break;case"VersionLastRefresh":break;case"VersionLastUpdate":break;case"VersionUpdateableMin":break;case"VersionRefreshableMin":break;case"Calculation":break;default:seen=false}break;case"PageBreaks":switch(Rn[3]){case"ColBreaks":break;case"ColBreak":break;case"RowBreaks":break;case"RowBreak":break;case"ColStart":break;case"ColEnd":break;case"RowEnd":break;default:seen=false}break;case"AutoFilter":switch(Rn[3]){case"AutoFilterColumn":break;case"AutoFilterCondition":break;case"AutoFilterAnd":break;case"AutoFilterOr":break;default:seen=false}break;case"QueryTable":switch(Rn[3]){case"Id":break;case"AutoFormatFont":break;case"AutoFormatPattern":break;case"QuerySource":break;case"QueryType":break;case"EnableRedirections":break;case"RefreshedInXl9":break;case"URLString":break;case"HTMLTables":break;case"Connection":break;case"CommandText":break;case"RefreshInfo":break;case"NoTitles":break;case"NextId":break;case"ColumnInfo":break;case"OverwriteCells":break;case"DoNotPromptForFile":break;case"TextWizardSettings":break;case"Source":break;case"Number":break;case"Decimal":break;case"ThousandSeparator":break;case"TrailingMinusNumbers":break;case"FormatSettings":break;case"FieldType":break;case"Delimiters":break;case"Tab":break;case"Comma":break;case"AutoFormatName":break;case"VersionLastEdit":break;case"VersionLastRefresh":break;default:seen=false}break;case"Sorting":case"ConditionalFormatting":case"DataValidation":switch(Rn[3]){case"Range":break;case"Type":break;case"Min":break;case"Max":break;case"Sort":break;case"Descending":break;case"Order":break;case"CaseSensitive":break;case"Value":break;case"ErrorStyle":break;case"ErrorMessage":break;case"ErrorTitle":break;case"CellRangeList":break;case"InputMessage":break;case"InputTitle":break;case"ComboHide":break;case"InputHide":break;case"Condition":break;case"Qualifier":break;case"UseBlank":break;case"Value1":break;case"Value2":break;case"Format":break;default:seen=false}break;case"MapInfo":case"Schema":case"data":switch(Rn[3]){case"Map":break;case"Entry":break;case"Range":break;case"XPath":break;case"Field":break;case"XSDType":break;case"FilterOn":break;case"Aggregate":break;case"ElementType":break;case"AttributeType":break;case"schema":case"element":case"complexType":case"datatype":case"all":case"attribute":case"extends":break;case"row":break;default:seen=false}break;case"SmartTags":break;default:seen=false;break}if(seen)break;if(!state[state.length-1][1])throw"Unrecognized tag: "+Rn[3]+"|"+state.join("|");if(state[state.length-1][0]==="CustomDocumentProperties"){if(Rn[0].substr(-2)==="/>")break;else if(Rn[1]==="/")xlml_set_custprop(Custprops,Rn,cp,str.slice(pidx,Rn.index));else{cp=Rn;pidx=Rn.index+Rn[0].length}break}if(opts.WTF)throw"Unrecognized tag: "+Rn[3]+"|"+state.join("|")}var out={};if(!opts.bookSheets&&!opts.bookProps)out.Sheets=sheets;out.SheetNames=sheetnames;out.SSF=SSF.get_table();out.Props=Props;out.Custprops=Custprops;return out}function parse_xlml(data,opts){fixopts(opts=opts||{});switch(opts.type||"base64"){case"base64":return parse_xlml_xml(Base64.decode(data),opts);case"binary":case"buffer":case"file":return parse_xlml_xml(data,opts);case"array":return parse_xlml_xml(data.map(_chr).join(""),opts)}}function write_xlml(wb,opts){}var fs;if(typeof exports!=="undefined"){if(typeof module!=="undefined"&&module.exports){fs=require("fs")}}function firstbyte(f,o){switch((o||{}).type||"base64"){case"buffer":return f[0];case"base64":return Base64.decode(f.substr(0,12)).charCodeAt(0);case"binary":return f.charCodeAt(0);case"array":return f[0];default:throw new Error("Unrecognized type "+o.type)}}function xlsread(f,o){if(!o)o={};if(!o.type)o.type=has_buf&&Buffer.isBuffer(f)?"buffer":"base64";switch(firstbyte(f,o)){case 208:return parse_xlscfb(CFB.read(f,o),o);case 60:return parse_xlml(f,o);default:throw"Unsupported file"}}var readFile=function(f,o){var d=fs.readFileSync(f);if(!o)o={};switch(firstbyte(d,{type:"buffer"})){case 208:return parse_xlscfb(CFB.read(d,{type:"buffer"}),o);case 60:return parse_xlml(d,(o.type="buffer",o));default:throw"Unsupported file"}};function writeSync(wb,opts){var o=opts||{};switch(o.bookType){case"xml":return write_xlml(wb,o);default:throw"unsupported output format "+o.bookType}}function writeFileSync(wb,filename,opts){var o=opts|{};o.type="file";o.file=filename;switch(o.file.substr(-4).toLowerCase()){case".xls":o.bookType="xls";break;case".xml":o.bookType="xml";break}return writeSync(wb,o)}function shift_cell(cell,tgt){if(tgt.s){if(cell.cRel)cell.c+=tgt.s.c;if(cell.rRel)cell.r+=tgt.s.r}else{cell.c+=tgt.c;cell.r+=tgt.r}cell.cRel=cell.rRel=0;while(cell.c>=256)cell.c-=256;while(cell.r>=65536)cell.r-=65536;return cell}function shift_range(cell,range){cell.s=shift_cell(cell.s,range.s);cell.e=shift_cell(cell.e,range.s);return cell}function decode_row(rowstr){return parseInt(unfix_row(rowstr),10)-1}function encode_row(row){return""+(row+1)}function fix_row(cstr){return cstr.replace(/([A-Z]|^)(\d+)$/,"$1$$$2")}function unfix_row(cstr){return cstr.replace(/\$(\d+)$/,"$1")}function decode_col(colstr){var c=unfix_col(colstr),d=0,i=0;for(;i!==c.length;++i)d=26*d+c.charCodeAt(i)-64;return d-1}function encode_col(col){var s="";for(++col;col;col=Math.floor((col-1)/26))s=String.fromCharCode((col-1)%26+65)+s;return s}function fix_col(cstr){return cstr.replace(/^([A-Z])/,"$$$1")}function unfix_col(cstr){return cstr.replace(/^\$([A-Z])/,"$1")}function split_cell(cstr){return cstr.replace(/(\$?[A-Z]*)(\$?\d*)/,"$1,$2").split(",")}function decode_cell(cstr){var splt=split_cell(cstr);return{c:decode_col(splt[0]),r:decode_row(splt[1])}}function encode_cell(cell){return encode_col(cell.c)+encode_row(cell.r)}function fix_cell(cstr){return fix_col(fix_row(cstr))}function unfix_cell(cstr){return unfix_col(unfix_row(cstr))}function decode_range(range){var x=range.split(":").map(decode_cell);return{s:x[0],e:x[x.length-1]}}function encode_range(cs,ce){if(ce===undefined||typeof ce==="number")return encode_range(cs.s,cs.e);if(typeof cs!=="string")cs=encode_cell(cs);if(typeof ce!=="string")ce=encode_cell(ce);return cs==ce?cs:cs+":"+ce}function safe_decode_range(range){var o={s:{c:0,r:0},e:{c:0,r:0}};var idx=0,i=0,cc=0;var len=range.length;for(idx=0;i<len;++i){if((cc=range.charCodeAt(i)-64)<1||cc>26)break;idx=26*idx+cc}o.s.c=--idx;for(idx=0;i<len;++i){if((cc=range.charCodeAt(i)-48)<0||cc>9)break;idx=10*idx+cc}o.s.r=--idx;if(i===len||range.charCodeAt(++i)===58){o.e.c=o.s.c;o.e.r=o.s.r;return o}for(idx=0;i!=len;++i){if((cc=range.charCodeAt(i)-64)<1||cc>26)break;idx=26*idx+cc}o.e.c=--idx;for(idx=0;i!=len;++i){if((cc=range.charCodeAt(i)-48)<0||cc>9)break;idx=10*idx+cc}o.e.r=--idx;return o}function safe_format_cell(cell,v){if(cell.z!==undefined)try{return cell.w=SSF.format(cell.z,v)}catch(e){}if(!cell.XF)return v;try{return cell.w=SSF.format(cell.XF.ifmt||0,v)}catch(e){return""+v}}function format_cell(cell,v){if(cell==null||cell.t==null)return"";if(cell.w!==undefined)return cell.w;if(v===undefined)return safe_format_cell(cell,cell.v);return safe_format_cell(cell,v)}function sheet_to_json(sheet,opts){var val,row,range,header=0,offset=1,r,hdr=[],isempty,R,C,v;var o=opts!=null?opts:{};var raw=o.raw;if(sheet==null||sheet["!ref"]==null)return[];range=o.range!==undefined?o.range:sheet["!ref"];if(o.header===1)header=1;else if(o.header==="A")header=2;else if(Array.isArray(o.header))header=3;switch(typeof range){case"string":r=safe_decode_range(range);break;case"number":r=safe_decode_range(sheet["!ref"]);r.s.r=range;break;default:r=range}if(header>0)offset=0;var rr=encode_row(r.s.r);var cols=new Array(r.e.c-r.s.c+1);var out=new Array(r.e.r-r.s.r-offset+1);var outi=0;for(C=r.s.c;C<=r.e.c;++C){cols[C]=encode_col(C);val=sheet[cols[C]+rr];switch(header){case 1:hdr[C]=C;break;case 2:hdr[C]=cols[C];break;case 3:hdr[C]=o.header[C-r.s.c];break;default:if(val===undefined)continue;hdr[C]=format_cell(val)}}for(R=r.s.r+offset;R<=r.e.r;++R){rr=encode_row(R);isempty=true;row=header===1?[]:Object.create({__rowNum__:R});for(C=r.s.c;C<=r.e.c;++C){val=sheet[cols[C]+rr];if(val===undefined||val.t===undefined)continue;v=val.v;switch(val.t){case"e":continue;case"s":case"str":break;case"b":case"n":break;default:throw"unrecognized type "+val.t}if(v!==undefined){row[hdr[C]]=raw?v:format_cell(val,v);isempty=false}}if(isempty===false)out[outi++]=row}out.length=outi;return out}function sheet_to_row_object_array(sheet,opts){return sheet_to_json(sheet,opts!=null?opts:{})}function sheet_to_csv(sheet,opts){var out="",txt="",qreg=/"/g;var o=opts==null?{}:opts;if(sheet==null||sheet["!ref"]==null)return"";var r=safe_decode_range(sheet["!ref"]);var FS=o.FS!==undefined?o.FS:",",fs=FS.charCodeAt(0);var RS=o.RS!==undefined?o.RS:"\n",rs=RS.charCodeAt(0);var row="",rr="",cols=[];var i=0,cc=0,val;var R=0,C=0;for(C=r.s.c;C<=r.e.c;++C)cols[C]=encode_col(C);for(R=r.s.r;R<=r.e.r;++R){row="";rr=encode_row(R);for(C=r.s.c;C<=r.e.c;++C){val=sheet[cols[C]+rr];txt=val!==undefined?""+format_cell(val):"";for(i=0,cc=0;i!==txt.length;++i)if((cc=txt.charCodeAt(i))===fs||cc===rs||cc===34){txt='"'+txt.replace(qreg,'""')+'"';break}row+=(C===r.s.c?"":FS)+txt}out+=row+RS}return out}var make_csv=sheet_to_csv;function sheet_to_formulae(sheet){var cmds,y="",x,val="";if(sheet==null||sheet["!ref"]==null)return"";var r=safe_decode_range(sheet["!ref"]),rr="",cols=[],C;cmds=new Array((r.e.r-r.s.r+1)*(r.e.c-r.s.c+1));var i=0;for(C=r.s.c;C<=r.e.c;++C)cols[C]=encode_col(C);for(var R=r.s.r;R<=r.e.r;++R){rr=encode_row(R);for(C=r.s.c;C<=r.e.c;++C){y=cols[C]+rr;x=sheet[y];val="";if(x===undefined)continue;if(x.f!=null)val=x.f;else if(x.w!==undefined)val="'"+x.w;else if(x.v===undefined)continue;else val=""+x.v;cmds[i++]=y+"="+val}}cmds.length=i;return cmds}var utils={encode_col:encode_col,encode_row:encode_row,encode_cell:encode_cell,encode_range:encode_range,decode_col:decode_col,decode_row:decode_row,split_cell:split_cell,decode_cell:decode_cell,decode_range:decode_range,format_cell:format_cell,get_formulae:sheet_to_formulae,make_csv:sheet_to_csv,make_json:sheet_to_json,make_formulae:sheet_to_formulae,sheet_to_csv:sheet_to_csv,sheet_to_json:sheet_to_json,sheet_to_formulae:sheet_to_formulae,sheet_to_row_object_array:sheet_to_row_object_array};XLS.parse_xlscfb=parse_xlscfb;XLS.read=xlsread;XLS.readFile=readFile;XLS.utils=utils;XLS.CFB=CFB;XLS.SSF=SSF})(typeof exports!=="undefined"?exports:XLS);
+//# sourceMappingURL=dist/xls.min.map
+/* xlsx.js (C) 2013-2014 SheetJS -- http://sheetjs.com */
+/* vim: set ts=2: */
+/*jshint -W041 */
+var XLSX = {};
+(function(XLSX){
+XLSX.version = '0.7.8';
+var current_codepage = 1252, current_cptable;
+if(typeof module !== "undefined" && typeof require !== 'undefined') {
+	if(typeof cptable === 'undefined') cptable = require('./dist/cpexcel');
+	current_cptable = cptable[current_codepage];
+}
+function reset_cp() { set_cp(1252); }
+var set_cp = function(cp) { current_codepage = cp; };
+
+function char_codes(data) { var o = []; for(var i = 0, len = data.length; i < len; ++i) o[i] = data.charCodeAt(i); return o; }
+var debom_xml = function(data) { return data; };
+
+if(typeof cptable !== 'undefined') {
+	set_cp = function(cp) { current_codepage = cp; current_cptable = cptable[cp]; };
+	debom_xml = function(data) {
+		if(data.charCodeAt(0) === 0xFF && data.charCodeAt(1) === 0xFE) { return cptable.utils.decode(1200, char_codes(data.substr(2))); }
+		return data;
+	};
+}
+/* ssf.js (C) 2013-2014 SheetJS -- http://sheetjs.com */
+/*jshint -W041 */
+var SSF = {};
+var make_ssf = function make_ssf(SSF){
+SSF.version = '0.8.1';
+function _strrev(x) { var o = "", i = x.length-1; while(i>=0) o += x.charAt(i--); return o; }
+function fill(c,l) { var o = ""; while(o.length < l) o+=c; return o; }
+function pad0(v,d){var t=""+v; return t.length>=d?t:fill('0',d-t.length)+t;}
+function pad_(v,d){var t=""+v;return t.length>=d?t:fill(' ',d-t.length)+t;}
+function rpad_(v,d){var t=""+v; return t.length>=d?t:t+fill(' ',d-t.length);}
+function pad0r1(v,d){var t=""+Math.round(v); return t.length>=d?t:fill('0',d-t.length)+t;}
+function pad0r2(v,d){var t=""+v; return t.length>=d?t:fill('0',d-t.length)+t;}
+var p2_32 = Math.pow(2,32);
+function pad0r(v,d){if(v>p2_32||v<-p2_32) return pad0r1(v,d); var i = Math.round(v); return pad0r2(i,d); }
+function isgeneral(s, i) { return s.length >= 7 + i && (s.charCodeAt(i)|32) === 103 && (s.charCodeAt(i+1)|32) === 101 && (s.charCodeAt(i+2)|32) === 110 && (s.charCodeAt(i+3)|32) === 101 && (s.charCodeAt(i+4)|32) === 114 && (s.charCodeAt(i+5)|32) === 97 && (s.charCodeAt(i+6)|32) === 108; }
+/* Options */
+var opts_fmt = [
+	["date1904", 0],
+	["output", ""],
+	["WTF", false]
+];
+function fixopts(o){
+	for(var y = 0; y != opts_fmt.length; ++y) if(o[opts_fmt[y][0]]===undefined) o[opts_fmt[y][0]]=opts_fmt[y][1];
+}
+SSF.opts = opts_fmt;
+var table_fmt = {
+	0:  'General',
+	1:  '0',
+	2:  '0.00',
+	3:  '#,##0',
+	4:  '#,##0.00',
+	9:  '0%',
+	10: '0.00%',
+	11: '0.00E+00',
+	12: '# ?/?',
+	13: '# ??/??',
+	14: 'm/d/yy',
+	15: 'd-mmm-yy',
+	16: 'd-mmm',
+	17: 'mmm-yy',
+	18: 'h:mm AM/PM',
+	19: 'h:mm:ss AM/PM',
+	20: 'h:mm',
+	21: 'h:mm:ss',
+	22: 'm/d/yy h:mm',
+	37: '#,##0 ;(#,##0)',
+	38: '#,##0 ;[Red](#,##0)',
+	39: '#,##0.00;(#,##0.00)',
+	40: '#,##0.00;[Red](#,##0.00)',
+	45: 'mm:ss',
+	46: '[h]:mm:ss',
+	47: 'mmss.0',
+	48: '##0.0E+0',
+	49: '@',
+	56: '"上午/下午 "hh"時"mm"分"ss"秒 "',
+	65535: 'General'
+};
+var days = [
+	['Sun', 'Sunday'],
+	['Mon', 'Monday'],
+	['Tue', 'Tuesday'],
+	['Wed', 'Wednesday'],
+	['Thu', 'Thursday'],
+	['Fri', 'Friday'],
+	['Sat', 'Saturday']
+];
+var months = [
+	['J', 'Jan', 'January'],
+	['F', 'Feb', 'February'],
+	['M', 'Mar', 'March'],
+	['A', 'Apr', 'April'],
+	['M', 'May', 'May'],
+	['J', 'Jun', 'June'],
+	['J', 'Jul', 'July'],
+	['A', 'Aug', 'August'],
+	['S', 'Sep', 'September'],
+	['O', 'Oct', 'October'],
+	['N', 'Nov', 'November'],
+	['D', 'Dec', 'December']
+];
+function frac(x, D, mixed) {
+	var sgn = x < 0 ? -1 : 1;
+	var B = x * sgn;
+	var P_2 = 0, P_1 = 1, P = 0;
+	var Q_2 = 1, Q_1 = 0, Q = 0;
+	var A = Math.floor(B);
+	while(Q_1 < D) {
+		A = Math.floor(B);
+		P = A * P_1 + P_2;
+		Q = A * Q_1 + Q_2;
+		if((B - A) < 0.0000000005) break;
+		B = 1 / (B - A);
+		P_2 = P_1; P_1 = P;
+		Q_2 = Q_1; Q_1 = Q;
+	}
+	if(Q > D) { Q = Q_1; P = P_1; }
+	if(Q > D) { Q = Q_2; P = P_2; }
+	if(!mixed) return [0, sgn * P, Q];
+	if(Q===0) throw "Unexpected state: "+P+" "+P_1+" "+P_2+" "+Q+" "+Q_1+" "+Q_2;
+	var q = Math.floor(sgn * P/Q);
+	return [q, sgn*P - q*Q, Q];
+}
+function general_fmt_int(v, opts) { return ""+v; }
+SSF._general_int = general_fmt_int;
+var general_fmt_num = (function make_general_fmt_num() {
+var gnr1 = /\.(\d*[1-9])0+$/, gnr2 = /\.0*$/, gnr4 = /\.(\d*[1-9])0+/, gnr5 = /\.0*[Ee]/, gnr6 = /(E[+-])(\d)$/;
+function gfn2(v) {
+	var w = (v<0?12:11);
+	var o = gfn5(v.toFixed(12)); if(o.length <= w) return o;
+	o = v.toPrecision(10); if(o.length <= w) return o;
+	return v.toExponential(5);
+}
+function gfn3(v) {
+	var o = v.toFixed(11).replace(gnr1,".$1");
+	if(o.length > (v<0?12:11)) o = v.toPrecision(6);
+	return o;
+}
+function gfn4(o) {
+	for(var i = 0; i != o.length; ++i) if((o.charCodeAt(i) | 0x20) === 101) return o.replace(gnr4,".$1").replace(gnr5,"E").replace("e","E").replace(gnr6,"$10$2");
+	return o;
+}
+function gfn5(o) {
+	//for(var i = 0; i != o.length; ++i) if(o.charCodeAt(i) === 46) return o.replace(gnr2,"").replace(gnr1,".$1");
+	//return o;
+	return o.indexOf(".") > -1 ? o.replace(gnr2,"").replace(gnr1,".$1") : o;
+}
+return function general_fmt_num(v, opts) {
+	var V = Math.floor(Math.log(Math.abs(v))*Math.LOG10E), o;
+	if(V >= -4 && V <= -1) o = v.toPrecision(10+V);
+	else if(Math.abs(V) <= 9) o = gfn2(v);
+	else if(V === 10) o = v.toFixed(10).substr(0,12);
+	else o = gfn3(v);
+	return gfn5(gfn4(o));
+};})();
+SSF._general_num = general_fmt_num;
+function general_fmt(v, opts) {
+	switch(typeof v) {
+		case 'string': return v;
+		case 'boolean': return v ? "TRUE" : "FALSE";
+		case 'number': return (v|0) === v ? general_fmt_int(v, opts) : general_fmt_num(v, opts);
+	}
+	throw new Error("unsupported value in General format: " + v);
+}
+SSF._general = general_fmt;
+function fix_hijri(date, o) { return 0; }
+function parse_date_code(v,opts,b2) {
+	if(v > 2958465 || v < 0) return null;
+	var date = (v|0), time = Math.floor(86400 * (v - date)), dow=0;
+	var dout=[];
+	var out={D:date, T:time, u:86400*(v-date)-time,y:0,m:0,d:0,H:0,M:0,S:0,q:0};
+	if(Math.abs(out.u) < 1e-6) out.u = 0;
+	fixopts(opts != null ? opts : (opts=[]));
+	if(opts.date1904) date += 1462;
+	if(out.u > 0.999) {
+		out.u = 0;
+		if(++time == 86400) { time = 0; ++date; }
+	}
+	if(date === 60) {dout = b2 ? [1317,10,29] : [1900,2,29]; dow=3;}
+	else if(date === 0) {dout = b2 ? [1317,8,29] : [1900,1,0]; dow=6;}
+	else {
+		if(date > 60) --date;
+		/* 1 = Jan 1 1900 */
+		var d = new Date(1900,0,1);
+		d.setDate(d.getDate() + date - 1);
+		dout = [d.getFullYear(), d.getMonth()+1,d.getDate()];
+		dow = d.getDay();
+		if(date < 60) dow = (dow + 6) % 7;
+		if(b2) dow = fix_hijri(d, dout);
+	}
+	out.y = dout[0]; out.m = dout[1]; out.d = dout[2];
+	out.S = time % 60; time = Math.floor(time / 60);
+	out.M = time % 60; time = Math.floor(time / 60);
+	out.H = time;
+	out.q = dow;
+	return out;
+}
+SSF.parse_date_code = parse_date_code;
+/*jshint -W086 */
+function write_date(type, fmt, val, ss0) {
+	var o="", ss=0, tt=0, y = val.y, out, outl = 0;
+	switch(type) {
+		case 98: /* 'b' buddhist year */
+			y = val.y + 543;
+			/* falls through */
+		case 121: /* 'y' year */
+		switch(fmt.length) {
+			case 1: case 2: out = y % 100; outl = 2; break;
+			default: out = y % 10000; outl = 4; break;
+		} break;
+		case 109: /* 'm' month */
+		switch(fmt.length) {
+			case 1: case 2: out = val.m; outl = fmt.length; break;
+			case 3: return months[val.m-1][1];
+			case 5: return months[val.m-1][0];
+			default: return months[val.m-1][2];
+		} break;
+		case 100: /* 'd' day */
+		switch(fmt.length) {
+			case 1: case 2: out = val.d; outl = fmt.length; break;
+			case 3: return days[val.q][0];
+			default: return days[val.q][1];
+		} break;
+		case 104: /* 'h' 12-hour */
+		switch(fmt.length) {
+			case 1: case 2: out = 1+(val.H+11)%12; outl = fmt.length; break;
+			default: throw 'bad hour format: ' + fmt;
+		} break;
+		case 72: /* 'H' 24-hour */
+		switch(fmt.length) {
+			case 1: case 2: out = val.H; outl = fmt.length; break;
+			default: throw 'bad hour format: ' + fmt;
+		} break;
+		case 77: /* 'M' minutes */
+		switch(fmt.length) {
+			case 1: case 2: out = val.M; outl = fmt.length; break;
+			default: throw 'bad minute format: ' + fmt;
+		} break;
+		case 115: /* 's' seconds */
+		if(val.u === 0) switch(fmt) {
+			case 's': case 'ss': return pad0(val.S, fmt.length);
+			case '.0': case '.00': case '.000':
+		}
+		switch(fmt) {
+			case 's': case 'ss': case '.0': case '.00': case '.000':
+				if(ss0 >= 2) tt = ss0 === 3 ? 1000 : 100;
+				else tt = ss0 === 1 ? 10 : 1;
+				ss = Math.round((tt)*(val.S + val.u));
+				if(ss >= 60*tt) ss = 0;
+				if(fmt === 's') return ss === 0 ? "0" : ""+ss/tt;
+				o = pad0(ss,2 + ss0);
+				if(fmt === 'ss') return o.substr(0,2);
+				return "." + o.substr(2,fmt.length-1);
+			default: throw 'bad second format: ' + fmt;
+		}
+		case 90: /* 'Z' absolute time */
+		switch(fmt) {
+			case '[h]': case '[hh]': out = val.D*24+val.H; break;
+			case '[m]': case '[mm]': out = (val.D*24+val.H)*60+val.M; break;
+			case '[s]': case '[ss]': out = ((val.D*24+val.H)*60+val.M)*60+Math.round(val.S+val.u); break;
+			default: throw 'bad abstime format: ' + fmt;
+		} outl = fmt.length === 3 ? 1 : 2; break;
+		case 101: /* 'e' era */
+			out = y; outl = 1;
+	}
+	if(outl > 0) return pad0(out, outl); else return "";
+}
+/*jshint +W086 */
+function commaify(s) {
+	if(s.length <= 3) return s;
+	var j = (s.length % 3), o = s.substr(0,j);
+	for(; j!=s.length; j+=3) o+=(o.length > 0 ? "," : "") + s.substr(j,3);
+	return o;
+}
+var write_num = (function make_write_num(){
+var pct1 = /%/g;
+function write_num_pct(type, fmt, val){
+	var sfmt = fmt.replace(pct1,""), mul = fmt.length - sfmt.length;
+	return write_num(type, sfmt, val * Math.pow(10,2*mul)) + fill("%",mul);
+}
+function write_num_cm(type, fmt, val){
+	var idx = fmt.length - 1;
+	while(fmt.charCodeAt(idx-1) === 44) --idx;
+	return write_num(type, fmt.substr(0,idx), val / Math.pow(10,3*(fmt.length-idx)));
+}
+function write_num_exp(fmt, val){
+	var o;
+	var idx = fmt.indexOf("E") - fmt.indexOf(".") - 1;
+	if(fmt.match(/^#+0.0E\+0$/)) {
+		var period = fmt.indexOf("."); if(period === -1) period=fmt.indexOf('E');
+		var ee = Math.floor(Math.log(Math.abs(val))*Math.LOG10E)%period;
+		if(ee < 0) ee += period;
+		o = (val/Math.pow(10,ee)).toPrecision(idx+1+(period+ee)%period);
+		if(o.indexOf("e") === -1) {
+			var fakee = Math.floor(Math.log(Math.abs(val))*Math.LOG10E);
+			if(o.indexOf(".") === -1) o = o[0] + "." + o.substr(1) + "E+" + (fakee - o.length+ee);
+			else o += "E+" + (fakee - ee);
+			while(o.substr(0,2) === "0.") {
+				o = o[0] + o.substr(2,period) + "." + o.substr(2+period);
+				o = o.replace(/^0+([1-9])/,"$1").replace(/^0+\./,"0.");
+			}
+			o = o.replace(/\+-/,"-");
+		}
+		o = o.replace(/^([+-]?)(\d*)\.(\d*)[Ee]/,function($$,$1,$2,$3) { return $1 + $2 + $3.substr(0,(period+ee)%period) + "." + $3.substr(ee) + "E"; });
+	} else o = val.toExponential(idx);
+	if(fmt.match(/E\+00$/) && o.match(/e[+-]\d$/)) o = o.substr(0,o.length-1) + "0" + o[o.length-1];
+	if(fmt.match(/E\-/) && o.match(/e\+/)) o = o.replace(/e\+/,"e");
+	return o.replace("e","E");
+}
+var frac1 = /# (\?+)( ?)\/( ?)(\d+)/;
+function write_num_f1(r, aval, sign) {
+	var den = parseInt(r[4]), rr = Math.round(aval * den), base = Math.floor(rr/den);
+	var myn = (rr - base*den), myd = den;
+	return sign + (base === 0 ? "" : ""+base) + " " + (myn === 0 ? fill(" ", r[1].length + 1 + r[4].length) : pad_(myn,r[1].length) + r[2] + "/" + r[3] + pad0(myd,r[4].length));
+}
+function write_num_f2(r, aval, sign) {
+	return sign + (aval === 0 ? "" : ""+aval) + fill(" ", r[1].length + 2 + r[4].length);
+}
+var dec1 = /^#*0*\.(0+)/;
+var closeparen = /\).*[0#]/;
+var phone = /\(###\) ###\\?-####/;
+function hashq(str) {
+	var o = "", cc;
+	for(var i = 0; i != str.length; ++i) switch((cc=str.charCodeAt(i))) {
+		case 35: break;
+		case 63: o+= " "; break;
+		case 48: o+= "0"; break;
+		default: o+= String.fromCharCode(cc);
+	}
+	return o;
+}
+function rnd(val, d) { var dd = Math.pow(10,d); return ""+(Math.round(val * dd)/dd); }
+function dec(val, d) { return Math.round((val-Math.floor(val))*Math.pow(10,d)); }
+function flr(val) { if(val < 2147483647 && val > -2147483648) return ""+(val >= 0 ? (val|0) : (val-1|0)); return ""+Math.floor(val); }
+function write_num_flt(type, fmt, val) {
+	if(type.charCodeAt(0) === 40 && !fmt.match(closeparen)) {
+		var ffmt = fmt.replace(/\( */,"").replace(/ \)/,"").replace(/\)/,"");
+		if(val >= 0) return write_num_flt('n', ffmt, val);
+		return '(' + write_num_flt('n', ffmt, -val) + ')';
+	}
+	if(fmt.charCodeAt(fmt.length - 1) === 44) return write_num_cm(type, fmt, val);
+	if(fmt.indexOf('%') !== -1) return write_num_pct(type, fmt, val);
+	if(fmt.indexOf('E') !== -1) return write_num_exp(fmt, val);
+	if(fmt.charCodeAt(0) === 36) return "$"+write_num_flt(type,fmt.substr(fmt[1]==' '?2:1),val);
+	var o, oo;
+	var r, ri, ff, aval = Math.abs(val), sign = val < 0 ? "-" : "";
+	if(fmt.match(/^00+$/)) return sign + pad0r(aval,fmt.length);
+	if(fmt.match(/^[#?]+$/)) {
+		o = pad0r(val,0); if(o === "0") o = "";
+		return o.length > fmt.length ? o : hashq(fmt.substr(0,fmt.length-o.length)) + o;
+	}
+	if((r = fmt.match(frac1)) !== null) return write_num_f1(r, aval, sign);
+	if(fmt.match(/^#+0+$/) !== null) return sign + pad0r(aval,fmt.length - fmt.indexOf("0"));
+	if((r = fmt.match(dec1)) !== null) {
+		o = rnd(val, r[1].length).replace(/^([^\.]+)$/,"$1."+r[1]).replace(/\.$/,"."+r[1]).replace(/\.(\d*)$/,function($$, $1) { return "." + $1 + fill("0", r[1].length-$1.length); });
+		return fmt.indexOf("0.") !== -1 ? o : o.replace(/^0\./,".");
+	}
+	fmt = fmt.replace(/^#+([0.])/, "$1");
+	if((r = fmt.match(/^(0*)\.(#*)$/)) !== null) {
+		return sign + rnd(aval, r[2].length).replace(/\.(\d*[1-9])0*$/,".$1").replace(/^(-?\d*)$/,"$1.").replace(/^0\./,r[1].length?"0.":".");
+	}
+	if((r = fmt.match(/^#,##0(\.?)$/)) !== null) return sign + commaify(pad0r(aval,0));
+	if((r = fmt.match(/^#,##0\.([#0]*0)$/)) !== null) {
+		return val < 0 ? "-" + write_num_flt(type, fmt, -val) : commaify(""+(Math.floor(val))) + "." + pad0(dec(val, r[1].length),r[1].length);
+	}
+	if((r = fmt.match(/^#,#*,#0/)) !== null) return write_num_flt(type,fmt.replace(/^#,#*,/,""),val);
+	if((r = fmt.match(/^([0#]+)(\\?-([0#]+))+$/)) !== null) {
+		o = _strrev(write_num_flt(type, fmt.replace(/[\\-]/g,""), val));
+		ri = 0;
+		return _strrev(_strrev(fmt.replace(/\\/g,"")).replace(/[0#]/g,function(x){return ri<o.length?o[ri++]:x==='0'?'0':"";}));
+	}
+	if(fmt.match(phone) !== null) {
+		o = write_num_flt(type, "##########", val);
+		return "(" + o.substr(0,3) + ") " + o.substr(3, 3) + "-" + o.substr(6);
+	}
+	var oa = "";
+	if((r = fmt.match(/^([#0?]+)( ?)\/( ?)([#0?]+)/)) !== null) {
+		ri = Math.min(r[4].length,7);
+		ff = frac(aval, Math.pow(10,ri)-1, false);
+		o = "" + sign;
+		oa = write_num("n", r[1], ff[1]);
+		if(oa[oa.length-1] == " ") oa = oa.substr(0,oa.length-1) + "0";
+		o += oa + r[2] + "/" + r[3];
+		oa = rpad_(ff[2],ri);
+		if(oa.length < r[4].length) oa = hashq(r[4].substr(r[4].length-oa.length)) + oa;
+		o += oa;
+		return o;
+	}
+	if((r = fmt.match(/^# ([#0?]+)( ?)\/( ?)([#0?]+)/)) !== null) {
+		ri = Math.min(Math.max(r[1].length, r[4].length),7);
+		ff = frac(aval, Math.pow(10,ri)-1, true);
+		return sign + (ff[0]||(ff[1] ? "" : "0")) + " " + (ff[1] ? pad_(ff[1],ri) + r[2] + "/" + r[3] + rpad_(ff[2],ri): fill(" ", 2*ri+1 + r[2].length + r[3].length));
+	}
+	if((r = fmt.match(/^[#0?]+$/)) !== null) {
+		o = pad0r(val, 0);
+		if(fmt.length <= o.length) return o;
+		return hashq(fmt.substr(0,fmt.length-o.length)) + o;
+	}
+  if((r = fmt.match(/^([#0?]+)\.([#0]+)$/)) !== null) {
+		o = "" + val.toFixed(Math.min(r[2].length,10)).replace(/([^0])0+$/,"$1");
+		ri = o.indexOf(".");
+		var lres = fmt.indexOf(".") - ri, rres = fmt.length - o.length - lres;
+		return hashq(fmt.substr(0,lres) + o + fmt.substr(fmt.length-rres));
+	}
+	if((r = fmt.match(/^00,000\.([#0]*0)$/)) !== null) {
+		ri = dec(val, r[1].length);
+		return val < 0 ? "-" + write_num_flt(type, fmt, -val) : commaify(flr(val)).replace(/^\d,\d{3}$/,"0$&").replace(/^\d*$/,function($$) { return "00," + ($$.length < 3 ? pad0(0,3-$$.length) : "") + $$; }) + "." + pad0(ri,r[1].length);
+	}
+	switch(fmt) {
+		case "#,###": var x = commaify(pad0r(aval,0)); return x !== "0" ? sign + x : "";
+		default:
+	}
+	throw new Error("unsupported format |" + fmt + "|");
+}
+function write_num_cm2(type, fmt, val){
+	var idx = fmt.length - 1;
+	while(fmt.charCodeAt(idx-1) === 44) --idx;
+	return write_num(type, fmt.substr(0,idx), val / Math.pow(10,3*(fmt.length-idx)));
+}
+function write_num_pct2(type, fmt, val){
+	var sfmt = fmt.replace(pct1,""), mul = fmt.length - sfmt.length;
+	return write_num(type, sfmt, val * Math.pow(10,2*mul)) + fill("%",mul);
+}
+function write_num_exp2(fmt, val){
+	var o;
+	var idx = fmt.indexOf("E") - fmt.indexOf(".") - 1;
+	if(fmt.match(/^#+0.0E\+0$/)) {
+		var period = fmt.indexOf("."); if(period === -1) period=fmt.indexOf('E');
+		var ee = Math.floor(Math.log(Math.abs(val))*Math.LOG10E)%period;
+		if(ee < 0) ee += period;
+		o = (val/Math.pow(10,ee)).toPrecision(idx+1+(period+ee)%period);
+		if(!o.match(/[Ee]/)) {
+			var fakee = Math.floor(Math.log(Math.abs(val))*Math.LOG10E);
+			if(o.indexOf(".") === -1) o = o[0] + "." + o.substr(1) + "E+" + (fakee - o.length+ee);
+			else o += "E+" + (fakee - ee);
+			o = o.replace(/\+-/,"-");
+		}
+		o = o.replace(/^([+-]?)(\d*)\.(\d*)[Ee]/,function($$,$1,$2,$3) { return $1 + $2 + $3.substr(0,(period+ee)%period) + "." + $3.substr(ee) + "E"; });
+	} else o = val.toExponential(idx);
+	if(fmt.match(/E\+00$/) && o.match(/e[+-]\d$/)) o = o.substr(0,o.length-1) + "0" + o[o.length-1];
+	if(fmt.match(/E\-/) && o.match(/e\+/)) o = o.replace(/e\+/,"e");
+	return o.replace("e","E");
+}
+function write_num_int(type, fmt, val) {
+	if(type.charCodeAt(0) === 40 && !fmt.match(closeparen)) {
+		var ffmt = fmt.replace(/\( */,"").replace(/ \)/,"").replace(/\)/,"");
+		if(val >= 0) return write_num_int('n', ffmt, val);
+		return '(' + write_num_int('n', ffmt, -val) + ')';
+	}
+	if(fmt.charCodeAt(fmt.length - 1) === 44) return write_num_cm2(type, fmt, val);
+	if(fmt.indexOf('%') !== -1) return write_num_pct2(type, fmt, val);
+	if(fmt.indexOf('E') !== -1) return write_num_exp2(fmt, val);
+	if(fmt.charCodeAt(0) === 36) return "$"+write_num_int(type,fmt.substr(fmt[1]==' '?2:1),val);
+	var o;
+	var r, ri, ff, aval = Math.abs(val), sign = val < 0 ? "-" : "";
+	if(fmt.match(/^00+$/)) return sign + pad0(aval,fmt.length);
+	if(fmt.match(/^[#?]+$/)) {
+		o = (""+val); if(val === 0) o = "";
+		return o.length > fmt.length ? o : hashq(fmt.substr(0,fmt.length-o.length)) + o;
+	}
+	if((r = fmt.match(frac1)) !== null) return write_num_f2(r, aval, sign);
+	if(fmt.match(/^#+0+$/) !== null) return sign + pad0(aval,fmt.length - fmt.indexOf("0"));
+	if((r = fmt.match(dec1)) !== null) {
+		o = (""+val).replace(/^([^\.]+)$/,"$1."+r[1]).replace(/\.$/,"."+r[1]).replace(/\.(\d*)$/,function($$, $1) { return "." + $1 + fill("0", r[1].length-$1.length); });
+		return fmt.indexOf("0.") !== -1 ? o : o.replace(/^0\./,".");
+	}
+	fmt = fmt.replace(/^#+([0.])/, "$1");
+	if((r = fmt.match(/^(0*)\.(#*)$/)) !== null) {
+		return sign + (""+aval).replace(/\.(\d*[1-9])0*$/,".$1").replace(/^(-?\d*)$/,"$1.").replace(/^0\./,r[1].length?"0.":".");
+	}
+	if((r = fmt.match(/^#,##0(\.?)$/)) !== null) return sign + commaify((""+aval));
+	if((r = fmt.match(/^#,##0\.([#0]*0)$/)) !== null) {
+		return val < 0 ? "-" + write_num_int(type, fmt, -val) : commaify((""+val)) + "." + fill('0',r[1].length);
+	}
+	if((r = fmt.match(/^#,#*,#0/)) !== null) return write_num_int(type,fmt.replace(/^#,#*,/,""),val);
+	if((r = fmt.match(/^([0#]+)(\\?-([0#]+))+$/)) !== null) {
+		o = _strrev(write_num_int(type, fmt.replace(/[\\-]/g,""), val));
+		ri = 0;
+		return _strrev(_strrev(fmt.replace(/\\/g,"")).replace(/[0#]/g,function(x){return ri<o.length?o[ri++]:x==='0'?'0':"";}));
+	}
+	if(fmt.match(phone) !== null) {
+		o = write_num_int(type, "##########", val);
+		return "(" + o.substr(0,3) + ") " + o.substr(3, 3) + "-" + o.substr(6);
+	}
+	var oa = "";
+	if((r = fmt.match(/^([#0?]+)( ?)\/( ?)([#0?]+)/)) !== null) {
+		ri = Math.min(r[4].length,7);
+		ff = frac(aval, Math.pow(10,ri)-1, false);
+		o = "" + sign;
+		oa = write_num("n", r[1], ff[1]);
+		if(oa[oa.length-1] == " ") oa = oa.substr(0,oa.length-1) + "0";
+		o += oa + r[2] + "/" + r[3];
+		oa = rpad_(ff[2],ri);
+		if(oa.length < r[4].length) oa = hashq(r[4].substr(r[4].length-oa.length)) + oa;
+		o += oa;
+		return o;
+	}
+	if((r = fmt.match(/^# ([#0?]+)( ?)\/( ?)([#0?]+)/)) !== null) {
+		ri = Math.min(Math.max(r[1].length, r[4].length),7);
+		ff = frac(aval, Math.pow(10,ri)-1, true);
+		return sign + (ff[0]||(ff[1] ? "" : "0")) + " " + (ff[1] ? pad_(ff[1],ri) + r[2] + "/" + r[3] + rpad_(ff[2],ri): fill(" ", 2*ri+1 + r[2].length + r[3].length));
+	}
+	if((r = fmt.match(/^[#0?]+$/)) !== null) {
+		o = "" + val;
+		if(fmt.length <= o.length) return o;
+		return hashq(fmt.substr(0,fmt.length-o.length)) + o;
+	}
+	if((r = fmt.match(/^([#0]+)\.([#0]+)$/)) !== null) {
+		o = "" + val.toFixed(Math.min(r[2].length,10)).replace(/([^0])0+$/,"$1");
+		ri = o.indexOf(".");
+		var lres = fmt.indexOf(".") - ri, rres = fmt.length - o.length - lres;
+		return hashq(fmt.substr(0,lres) + o + fmt.substr(fmt.length-rres));
+	}
+	if((r = fmt.match(/^00,000\.([#0]*0)$/)) !== null) {
+		return val < 0 ? "-" + write_num_int(type, fmt, -val) : commaify(""+val).replace(/^\d,\d{3}$/,"0$&").replace(/^\d*$/,function($$) { return "00," + ($$.length < 3 ? pad0(0,3-$$.length) : "") + $$; }) + "." + pad0(0,r[1].length);
+	}
+	switch(fmt) {
+		case "#,###": var x = commaify(""+aval); return x !== "0" ? sign + x : "";
+		default:
+	}
+	throw new Error("unsupported format |" + fmt + "|");
+}
+return function write_num(type, fmt, val) {
+	return (val|0) === val ? write_num_int(type, fmt, val) : write_num_flt(type, fmt, val);
+};})();
+function split_fmt(fmt) {
+	var out = [];
+	var in_str = false, cc;
+	for(var i = 0, j = 0; i < fmt.length; ++i) switch((cc=fmt.charCodeAt(i))) {
+		case 34: /* '"' */
+			in_str = !in_str; break;
+		case 95: case 42: case 92: /* '_' '*' '\\' */
+			++i; break;
+		case 59: /* ';' */
+			out[out.length] = fmt.substr(j,i-j);
+			j = i+1;
+	}
+	out[out.length] = fmt.substr(j);
+	if(in_str === true) throw new Error("Format |" + fmt + "| unterminated string ");
+	return out;
+}
+SSF._split = split_fmt;
+var abstime = /\[[HhMmSs]*\]/;
+function eval_fmt(fmt, v, opts, flen) {
+	var out = [], o = "", i = 0, c = "", lst='t', q, dt, j, cc;
+	var hr='H';
+	/* Tokenize */
+	while(i < fmt.length) {
+		switch((c = fmt[i])) {
+			case 'G': /* General */
+				if(!isgeneral(fmt, i)) throw new Error('unrecognized character ' + c + ' in ' +fmt);
+				out[out.length] = {t:'G', v:'General'}; i+=7; break;
+			case '"': /* Literal text */
+				for(o="";(cc=fmt.charCodeAt(++i)) !== 34 && i < fmt.length;) o += String.fromCharCode(cc);
+				out[out.length] = {t:'t', v:o}; ++i; break;
+			case '\\': var w = fmt[++i], t = (w === "(" || w === ")") ? w : 't';
+				out[out.length] = {t:t, v:w}; ++i; break;
+			case '_': out[out.length] = {t:'t', v:" "}; i+=2; break;
+			case '@': /* Text Placeholder */
+				out[out.length] = {t:'T', v:v}; ++i; break;
+			case 'B': case 'b':
+				if(fmt[i+1] === "1" || fmt[i+1] === "2") {
+          if(dt==null) { dt=parse_date_code(v, opts, fmt[i+1] === "2"); if(dt==null) return ""; }
+					out[out.length] = {t:'X', v:fmt.substr(i,2)}; lst = c; i+=2; break;
+				}
+				/* falls through */
+			case 'M': case 'D': case 'Y': case 'H': case 'S': case 'E':
+				c = c.toLowerCase();
+				/* falls through */
+			case 'm': case 'd': case 'y': case 'h': case 's': case 'e': case 'g':
+				if(v < 0) return "";
+				if(dt==null) { dt=parse_date_code(v, opts); if(dt==null) return ""; }
+				o = c; while(++i<fmt.length && fmt[i].toLowerCase() === c) o+=c;
+				if(c === 'm' && lst.toLowerCase() === 'h') c = 'M'; /* m = minute */
+				if(c === 'h') c = hr;
+				out[out.length] = {t:c, v:o}; lst = c; break;
+			case 'A':
+				q={t:c, v:"A"};
+				if(dt==null) dt=parse_date_code(v, opts);
+        if(fmt.substr(i, 3) === "A/P") { if(dt!=null) q.v = dt.H >= 12 ? "P" : "A"; q.t = 'T'; hr='h';i+=3;}
+        else if(fmt.substr(i,5) === "AM/PM") { if(dt!=null) q.v = dt.H >= 12 ? "PM" : "AM"; q.t = 'T'; i+=5; hr='h'; }
+				else { q.t = "t"; ++i; }
+				if(dt==null && q.t === 'T') return "";
+				out[out.length] = q; lst = c; break;
+			case '[':
+				o = c;
+				while(fmt[i++] !== ']' && i < fmt.length) o += fmt[i];
+				if(o.substr(-1) !== ']') throw 'unterminated "[" block: |' + o + '|';
+				if(o.match(abstime)) {
+					if(dt==null) { dt=parse_date_code(v, opts); if(dt==null) return ""; }
+					out[out.length] = {t:'Z', v:o.toLowerCase()};
+				} else { o=""; }
+				break;
+			/* Numbers */
+			case '.':
+				if(dt != null) {
+					o = c; while((c=fmt[++i]) === "0") o += c;
+					out[out.length] = {t:'s', v:o}; break;
+				}
+				/* falls through */
+			case '0': case '#':
+				o = c; while("0#?.,E+-%".indexOf(c=fmt[++i]) > -1 || c=='\\' && fmt[i+1] == "-" && "0#".indexOf(fmt[i+2])>-1) o += c;
+				out[out.length] = {t:'n', v:o}; break;
+			case '?':
+				o = c; while(fmt[++i] === c) o+=c;
+				q={t:c, v:o}; out[out.length] = q; lst = c; break;
+			case '*': ++i; if(fmt[i] == ' ' || fmt[i] == '*') ++i; break; // **
+			case '(': case ')': out[out.length] = {t:(flen===1?'t':c), v:c}; ++i; break;
+			case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
+				o = c; while("0123456789".indexOf(fmt[++i]) > -1) o+=fmt[i];
+				out[out.length] = {t:'D', v:o}; break;
+			case ' ': out[out.length] = {t:c, v:c}; ++i; break;
+			default:
+				if(",$-+/():!^&'~{}<>=€acfijklopqrtuvwxz".indexOf(c) === -1) throw new Error('unrecognized character ' + c + ' in ' + fmt);
+				out[out.length] = {t:'t', v:c}; ++i; break;
+		}
+	}
+	var bt = 0, ss0 = 0, ssm;
+	for(i=out.length-1, lst='t'; i >= 0; --i) {
+		switch(out[i].t) {
+			case 'h': case 'H': out[i].t = hr; lst='h'; if(bt < 1) bt = 1; break;
+			case 's':
+				if((ssm=out[i].v.match(/\.0+$/))) ss0=Math.max(ss0,ssm[0].length-1);
+				if(bt < 3) bt = 3;
+			/* falls through */
+			case 'd': case 'y': case 'M': case 'e': lst=out[i].t; break;
+			case 'm': if(lst === 's') { out[i].t = 'M'; if(bt < 2) bt = 2; } break;
+			case 'X': if(out[i].v === "B2");
+				break;
+			case 'Z':
+				if(bt < 1 && out[i].v.match(/[Hh]/)) bt = 1;
+				if(bt < 2 && out[i].v.match(/[Mm]/)) bt = 2;
+				if(bt < 3 && out[i].v.match(/[Ss]/)) bt = 3;
+		}
+	}
+	switch(bt) {
+		case 0: break;
+		case 1:
+			if(dt.u >= 0.5) { dt.u = 0; ++dt.S; }
+			if(dt.S >=  60) { dt.S = 0; ++dt.M; }
+			if(dt.M >=  60) { dt.M = 0; ++dt.H; }
+			break;
+		case 2:
+			if(dt.u >= 0.5) { dt.u = 0; ++dt.S; }
+			if(dt.S >=  60) { dt.S = 0; ++dt.M; }
+			break;
+	}
+	/* replace fields */
+	var nstr = "", jj;
+	for(i=0; i < out.length; ++i) {
+		switch(out[i].t) {
+			case 't': case 'T': case ' ': case 'D': break;
+			case 'X': out[i] = undefined; break;
+			case 'd': case 'm': case 'y': case 'h': case 'H': case 'M': case 's': case 'e': case 'b': case 'Z':
+				out[i].v = write_date(out[i].t.charCodeAt(0), out[i].v, dt, ss0);
+				out[i].t = 't'; break;
+			case 'n': case '(': case '?':
+				jj = i+1;
+				while(out[jj] != null && (
+					(c=out[jj].t) === "?" || c === "D" ||
+					(c === " " || c === "t") && out[jj+1] != null && (out[jj+1].t === '?' || out[jj+1].t === "t" && out[jj+1].v === '/') ||
+					out[i].t === '(' && (c === ' ' || c === 'n' || c === ')') ||
+					c === 't' && (out[jj].v === '/' || '$€'.indexOf(out[jj].v) > -1 || out[jj].v === ' ' && out[jj+1] != null && out[jj+1].t == '?')
+				)) {
+					out[i].v += out[jj].v;
+					out[jj] = undefined; ++jj;
+				}
+				nstr += out[i].v;
+				i = jj-1; break;
+			case 'G': out[i].t = 't'; out[i].v = general_fmt(v,opts); break;
+		}
+	}
+	var vv = "", myv, ostr;
+	if(nstr.length > 0) {
+		myv = (v<0&&nstr.charCodeAt(0) === 45 ? -v : v); /* '-' */
+		ostr = write_num(nstr.charCodeAt(0) === 40 ? '(' : 'n', nstr, myv); /* '(' */
+		jj=ostr.length-1;
+		var decpt = out.length;
+		for(i=0; i < out.length; ++i) if(out[i] != null && out[i].v.indexOf(".") > -1) { decpt = i; break; }
+		var lasti=out.length;
+		if(decpt === out.length && ostr.indexOf("E") === -1) {
+			for(i=out.length-1; i>= 0;--i) {
+				if(out[i] == null || 'n?('.indexOf(out[i].t) === -1) continue;
+				if(jj>=out[i].v.length-1) { jj -= out[i].v.length; out[i].v = ostr.substr(jj+1, out[i].v.length); }
+				else if(jj < 0) out[i].v = "";
+				else { out[i].v = ostr.substr(0, jj+1); jj = -1; }
+				out[i].t = 't';
+				lasti = i;
+			}
+			if(jj>=0 && lasti<out.length) out[lasti].v = ostr.substr(0,jj+1) + out[lasti].v;
+		}
+		else if(decpt !== out.length && ostr.indexOf("E") === -1) {
+			jj = ostr.indexOf(".")-1;
+			for(i=decpt; i>= 0; --i) {
+				if(out[i] == null || 'n?('.indexOf(out[i].t) === -1) continue;
+				j=out[i].v.indexOf(".")>-1&&i===decpt?out[i].v.indexOf(".")-1:out[i].v.length-1;
+				vv = out[i].v.substr(j+1);
+				for(; j>=0; --j) {
+					if(jj>=0 && (out[i].v[j] === "0" || out[i].v[j] === "#")) vv = ostr[jj--] + vv;
+				}
+				out[i].v = vv;
+				out[i].t = 't';
+				lasti = i;
+			}
+			if(jj>=0 && lasti<out.length) out[lasti].v = ostr.substr(0,jj+1) + out[lasti].v;
+			jj = ostr.indexOf(".")+1;
+			for(i=decpt; i<out.length; ++i) {
+				if(out[i] == null || 'n?('.indexOf(out[i].t) === -1 && i !== decpt ) continue;
+				j=out[i].v.indexOf(".")>-1&&i===decpt?out[i].v.indexOf(".")+1:0;
+				vv = out[i].v.substr(0,j);
+				for(; j<out[i].v.length; ++j) {
+					if(jj<ostr.length) vv += ostr[jj++];
+				}
+				out[i].v = vv;
+				out[i].t = 't';
+				lasti = i;
+			}
+		}
+	}
+	for(i=0; i<out.length; ++i) if(out[i] != null && 'n(?'.indexOf(out[i].t)>-1) {
+		myv = (flen >1 && v < 0 && i>0 && out[i-1].v === "-" ? -v:v);
+		out[i].v = write_num(out[i].t, out[i].v, myv);
+		out[i].t = 't';
+	}
+	var retval = "";
+	for(i=0; i !== out.length; ++i) if(out[i] != null) retval += out[i].v;
+	return retval;
+}
+SSF._eval = eval_fmt;
+var cfregex = /\[[=<>]/;
+var cfregex2 = /\[([=<>]*)(-?\d+\.?\d*)\]/;
+function chkcond(v, rr) {
+	if(rr == null) return false;
+	var thresh = parseFloat(rr[2]);
+	switch(rr[1]) {
+		case "=":  if(v == thresh) return true; break;
+		case ">":  if(v >  thresh) return true; break;
+		case "<":  if(v <  thresh) return true; break;
+		case "<>": if(v != thresh) return true; break;
+		case ">=": if(v >= thresh) return true; break;
+		case "<=": if(v <= thresh) return true; break;
+	}
+	return false;
+}
+function choose_fmt(f, v) {
+	var fmt = split_fmt(f);
+	var l = fmt.length, lat = fmt[l-1].indexOf("@");
+	if(l<4 && lat>-1) --l;
+	if(fmt.length > 4) throw "cannot find right format for |" + fmt + "|";
+	if(typeof v !== "number") return [4, fmt.length === 4 || lat>-1?fmt[fmt.length-1]:"@"];
+	switch(fmt.length) {
+		case 1: fmt = lat>-1 ? ["General", "General", "General", fmt[0]] : [fmt[0], fmt[0], fmt[0], "@"]; break;
+		case 2: fmt = lat>-1 ? [fmt[0], fmt[0], fmt[0], fmt[1]] : [fmt[0], fmt[1], fmt[0], "@"]; break;
+		case 3: fmt = lat>-1 ? [fmt[0], fmt[1], fmt[0], fmt[2]] : [fmt[0], fmt[1], fmt[2], "@"]; break;
+		case 4: break;
+	}
+	var ff = v > 0 ? fmt[0] : v < 0 ? fmt[1] : fmt[2];
+	if(fmt[0].indexOf("[") === -1 && fmt[1].indexOf("[") === -1) return [l, ff];
+	if(fmt[0].match(cfregex) != null || fmt[1].match(cfregex) != null) {
+		var m1 = fmt[0].match(cfregex2);
+		var m2 = fmt[1].match(cfregex2);
+		return chkcond(v, m1) ? [l, fmt[0]] : chkcond(v, m2) ? [l, fmt[1]] : [l, fmt[m1 != null && m2 != null ? 2 : 1]];
+	}
+	return [l, ff];
+}
+function format(fmt,v,o) {
+	fixopts(o != null ? o : (o=[]));
+	var sfmt = "";
+	switch(typeof fmt) {
+		case "string": sfmt = fmt; break;
+		case "number": sfmt = (o.table != null ? o.table : table_fmt)[fmt]; break;
+	}
+	if(isgeneral(sfmt,0)) return general_fmt(v, o);
+	var f = choose_fmt(sfmt, v);
+	if(isgeneral(f[1])) return general_fmt(v, o);
+	if(v === true) v = "TRUE"; else if(v === false) v = "FALSE";
+	else if(v === "" || v == null) return "";
+	return eval_fmt(f[1], v, o, f[0]);
+}
+SSF._table = table_fmt;
+SSF.load = function load_entry(fmt, idx) { table_fmt[idx] = fmt; };
+SSF.format = format;
+SSF.get_table = function get_table() { return table_fmt; };
+SSF.load_table = function load_table(tbl) { for(var i=0; i!=0x0188; ++i) if(tbl[i] !== undefined) SSF.load(tbl[i], i); };
+};
+make_ssf(SSF);
+function isval(x) { return x !== undefined && x !== null; }
+
+function keys(o) { return Object.keys(o); }
+
+function evert_key(obj, key) {
+	var o = [], K = keys(obj);
+	for(var i = 0; i !== K.length; ++i) o[obj[K[i]][key]] = K[i];
+	return o;
+}
+
+function evert(obj) {
+	var o = [], K = keys(obj);
+	for(var i = 0; i !== K.length; ++i) o[obj[K[i]]] = K[i];
+	return o;
+}
+
+function evert_num(obj) {
+	var o = [], K = keys(obj);
+	for(var i = 0; i !== K.length; ++i) o[obj[K[i]]] = parseInt(K[i],10);
+	return o;
+}
+
+function evert_arr(obj) {
+	var o = [], K = keys(obj);
+	for(var i = 0; i !== K.length; ++i) {
+		if(o[obj[K[i]]] == null) o[obj[K[i]]] = [];
+		o[obj[K[i]]].push(K[i]);
+	}
+	return o;
+}
+
+/* TODO: date1904 logic */
+function datenum(v, date1904) {
+	if(date1904) v+=1462;
+	var epoch = Date.parse(v);
+	return (epoch + 2209161600000) / (24 * 60 * 60 * 1000);
+}
+
+function cc2str(arr) {
+	var o = "";
+	for(var i = 0; i != arr.length; ++i) o += String.fromCharCode(arr[i]);
+	return o;
+}
+
+var has_buf = (typeof Buffer !== 'undefined');
+function getdata(data) {
+	if(!data) return null;
+	if(data.name.substr(-4) === ".bin") {
+		if(data.data) return char_codes(data.data);
+		if(data.asNodeBuffer && has_buf) return data.asNodeBuffer();
+		if(data._data && data._data.getContent) return Array.prototype.slice.call(data._data.getContent());
 	} 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.
- */
-var zip_build_bl_tree = function() {
-    var max_blindex;  // index of last bit length code of non zero freq
-
-    // Determine the bit length frequencies for literal and distance trees
-    zip_scan_tree(zip_dyn_ltree, zip_l_desc.max_code);
-    zip_scan_tree(zip_dyn_dtree, zip_d_desc.max_code);
-
-    // Build the bit length tree:
-    zip_build_tree(zip_bl_desc);
-    /* 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 = zip_BL_CODES-1; max_blindex >= 3; max_blindex--) {
-	if(zip_bl_tree[zip_bl_order[max_blindex]].dl != 0) break;
-    }
-    /* Update opt_len to include the bit length tree and counts */
-    zip_opt_len += 3*(max_blindex+1) + 5+5+4;
-//    Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld",
-//	    encoder->opt_len, encoder->static_len));
-
-    return max_blindex;
-}
-
-/* ==========================================================================
- * 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.
- */
-var zip_send_all_trees = function(lcodes, dcodes, blcodes) { // number of codes for each tree
-    var rank; // index in bl_order
-
-//    Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes");
-//    Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES,
-//	    "too many codes");
-//    Tracev((stderr, "\nbl counts: "));
-    zip_send_bits(lcodes-257, 5); // not +255 as stated in appnote.txt
-    zip_send_bits(dcodes-1,   5);
-    zip_send_bits(blcodes-4,  4); // not -3 as stated in appnote.txt
-    for(rank = 0; rank < blcodes; rank++) {
-//      Tracev((stderr, "\nbl code %2d ", bl_order[rank]));
-	zip_send_bits(zip_bl_tree[zip_bl_order[rank]].dl, 3);
-    }
-
-    // send the literal tree
-    zip_send_tree(zip_dyn_ltree,lcodes-1);
-
-    // send the distance tree
-    zip_send_tree(zip_dyn_dtree,dcodes-1);
-}
-
-/* ==========================================================================
- * Determine the best encoding for the current block: dynamic trees, static
- * trees or store, and output the encoded block to the zip file.
- */
-var zip_flush_block = function(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;	// index of last bit length code of non zero freq
-    var stored_len;	// length of input block
-
-    stored_len = zip_strstart - zip_block_start;
-    zip_flag_buf[zip_last_flags] = zip_flags; // Save the flags for the last 8 items
-
-    // Construct the literal and distance trees
-    zip_build_tree(zip_l_desc);
-//    Tracev((stderr, "\nlit data: dyn %ld, stat %ld",
-//	    encoder->opt_len, encoder->static_len));
-
-    zip_build_tree(zip_d_desc);
-//    Tracev((stderr, "\ndist data: dyn %ld, stat %ld",
-//	    encoder->opt_len, encoder->static_len));
-    /* 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 = zip_build_bl_tree();
-
-    // Determine the best encoding. Compute first the block length in bytes
-    opt_lenb	= (zip_opt_len   +3+7)>>3;
-    static_lenb = (zip_static_len+3+7)>>3;
-
-//    Trace((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u dist %u ",
-//	   opt_lenb, encoder->opt_len,
-//	   static_lenb, encoder->static_len, stored_len,
-//	   encoder->last_lit, encoder->last_dist));
-
-    if(static_lenb <= opt_lenb)
-	opt_lenb = static_lenb;
-    if(stored_len + 4 <= opt_lenb // 4: two words for the lengths
-       && zip_block_start >= 0) {
-	var i;
-
-	/* 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.
-	 */
-	zip_send_bits((zip_STORED_BLOCK<<1)+eof, 3);  /* send block type */
-	zip_bi_windup();		 /* align on byte boundary */
-	zip_put_short(stored_len);
-	zip_put_short(~stored_len);
-
-      // copy block
-/*
-      p = &window[block_start];
-      for(i = 0; i < stored_len; i++)
-	put_byte(p[i]);
-*/
-	for(i = 0; i < stored_len; i++)
-	    zip_put_byte(zip_window[zip_block_start + i]);
-
-    } else if(static_lenb == opt_lenb) {
-	zip_send_bits((zip_STATIC_TREES<<1)+eof, 3);
-	zip_compress_block(zip_static_ltree, zip_static_dtree);
-    } else {
-	zip_send_bits((zip_DYN_TREES<<1)+eof, 3);
-	zip_send_all_trees(zip_l_desc.max_code+1,
-			   zip_d_desc.max_code+1,
-			   max_blindex+1);
-	zip_compress_block(zip_dyn_ltree, zip_dyn_dtree);
-    }
-
-    zip_init_block();
-
-    if(eof != 0)
-	zip_bi_windup();
-}
-
-/* ==========================================================================
- * Save the match info and tally the frequency counts. Return true if
- * the current block must be flushed.
- */
-var zip_ct_tally = function(
-	dist, // distance of matched string
-	lc) { // match length-MIN_MATCH or unmatched char (if dist==0)
-    zip_l_buf[zip_last_lit++] = lc;
-    if(dist == 0) {
-	// lc is the unmatched char
-	zip_dyn_ltree[lc].fc++;
-    } else {
-	// Here, lc is the match length - MIN_MATCH
-	dist--;		    // dist = match distance - 1
-//      Assert((ush)dist < (ush)MAX_DIST &&
-//	     (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) &&
-//	     (ush)D_CODE(dist) < (ush)D_CODES,  "ct_tally: bad match");
-
-	zip_dyn_ltree[zip_length_code[lc]+zip_LITERALS+1].fc++;
-	zip_dyn_dtree[zip_D_CODE(dist)].fc++;
-
-	zip_d_buf[zip_last_dist++] = dist;
-	zip_flags |= zip_flag_bit;
-    }
-    zip_flag_bit <<= 1;
-
-    // Output the flags if they fill a byte
-    if((zip_last_lit & 7) == 0) {
-	zip_flag_buf[zip_last_flags++] = zip_flags;
-	zip_flags = 0;
-	zip_flag_bit = 1;
-    }
-    // Try to guess if it is profitable to stop the current block here
-    if(zip_compr_level > 2 && (zip_last_lit & 0xfff) == 0) {
-	// Compute an upper bound for the compressed length
-	var out_length = zip_last_lit * 8;
-	var in_length = zip_strstart - zip_block_start;
-	var dcode;
-
-	for(dcode = 0; dcode < zip_D_CODES; dcode++) {
-	    out_length += zip_dyn_dtree[dcode].fc * (5 + zip_extra_dbits[dcode]);
-	}
-	out_length >>= 3;
-//      Trace((stderr,"\nlast_lit %u, last_dist %u, in %ld, out ~%ld(%ld%%) ",
-//	     encoder->last_lit, encoder->last_dist, in_length, out_length,
-//	     100L - out_length*100L/in_length));
-	if(zip_last_dist < parseInt(zip_last_lit/2) &&
-	   out_length < parseInt(in_length/2))
-	    return true;
-    }
-    return (zip_last_lit == zip_LIT_BUFSIZE-1 ||
-	    zip_last_dist == zip_DIST_BUFSIZE);
-    /* 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
-   */
-var zip_compress_block = function(
-	ltree,	// literal tree
-	dtree) {	// distance tree
-    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 dx = 0;		// running index in d_buf
-    var fx = 0;		// running index in flag_buf
-    var flag = 0;	// current flags
-    var code;		// the code to send
-    var extra;		// number of extra bits to send
-
-    if(zip_last_lit != 0) do {
-	if((lx & 7) == 0)
-	    flag = zip_flag_buf[fx++];
-	lc = zip_l_buf[lx++] & 0xff;
-	if((flag & 1) == 0) {
-	    zip_SEND_CODE(lc, ltree); /* send a literal byte */
-//	Tracecv(isgraph(lc), (stderr," '%c' ", lc));
-	} else {
-	    // Here, lc is the match length - MIN_MATCH
-	    code = zip_length_code[lc];
-	    zip_SEND_CODE(code+zip_LITERALS+1, ltree); // send the length code
-	    extra = zip_extra_lbits[code];
-	    if(extra != 0) {
-		lc -= zip_base_length[code];
-		zip_send_bits(lc, extra); // send the extra length bits
-	    }
-	    dist = zip_d_buf[dx++];
-	    // Here, dist is the match distance - 1
-	    code = zip_D_CODE(dist);
-//	Assert (code < D_CODES, "bad d_code");
-
-	    zip_SEND_CODE(code, dtree);	  // send the distance code
-	    extra = zip_extra_dbits[code];
-	    if(extra != 0) {
-		dist -= zip_base_dist[code];
-		zip_send_bits(dist, extra);   // send the extra distance bits
-	    }
-	} // literal or match pair ?
-	flag >>= 1;
-    } while(lx < zip_last_lit);
-
-    zip_SEND_CODE(zip_END_BLOCK, ltree);
-}
-
-/* ==========================================================================
- * Send a value on a given number of bits.
- * IN assertion: length <= 16 and value fits in length bits.
- */
-var zip_Buf_size = 16; // bit size of bi_buf
-var zip_send_bits = function(
-	value,	// value to send
-	length) {	// number of bits
-    /* If not enough room in bi_buf, use (valid) bits from bi_buf and
-     * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid))
-     * unused bits in value.
-     */
-    if(zip_bi_valid > zip_Buf_size - length) {
-	zip_bi_buf |= (value << zip_bi_valid);
-	zip_put_short(zip_bi_buf);
-	zip_bi_buf = (value >> (zip_Buf_size - zip_bi_valid));
-	zip_bi_valid += length - zip_Buf_size;
-    } else {
-	zip_bi_buf |= value << zip_bi_valid;
-	zip_bi_valid += length;
-    }
-}
-
-/* ==========================================================================
- * Reverse the first len bits of a code, using straightforward code (a faster
- * method would use a table)
- * IN assertion: 1 <= len <= 15
- */
-var zip_bi_reverse = function(
-	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;
-}
-
-/* ==========================================================================
- * Write out any remaining bits in an incomplete byte.
- */
-var zip_bi_windup = function() {
-    if(zip_bi_valid > 8) {
-	zip_put_short(zip_bi_buf);
-    } else if(zip_bi_valid > 0) {
-	zip_put_byte(zip_bi_buf);
-    }
-    zip_bi_buf = 0;
-    zip_bi_valid = 0;
-}
-
-var zip_qoutbuf = function() {
-    if(zip_outcnt != 0) {
-	var q, i;
-	q = zip_new_queue();
-	if(zip_qhead == null)
-	    zip_qhead = zip_qtail = q;
-	else
-	    zip_qtail = zip_qtail.next = q;
-	q.len = zip_outcnt - zip_outoff;
-//      System.arraycopy(zip_outbuf, zip_outoff, q.ptr, 0, q.len);
-	for(i = 0; i < q.len; i++)
-	    q.ptr[i] = zip_outbuf[zip_outoff + i];
-	zip_outcnt = zip_outoff = 0;
-    }
-}
-
-var zip_deflate = function(str, level) {
-    var i, j;
-
-    zip_deflate_data = str;
-    zip_deflate_pos = 0;
-    if(typeof level == "undefined")
-	level = zip_DEFAULT_LEVEL;
-    zip_deflate_start(level);
-
-    var buff = new Array(1024);
-    var aout = [];
-    while((i = zip_deflate_internal(buff, 0, buff.length)) > 0) {
-	var cbuf = new Array(i);
-	for(j = 0; j < i; j++){
-	    cbuf[j] = String.fromCharCode(buff[j]);
-	}
-	aout[aout.length] = cbuf.join("");
-    }
-    zip_deflate_data = null; // G.C.
-    return aout.join("");
-}
-
-//
-// end of the script of Masanao Izumo.
-//
-
-// we add the compression method for JSZip
-if(!JSZip.compressions["DEFLATE"]) {
-  JSZip.compressions["DEFLATE"] = {
-    magic : "\x08\x00",
-    compress : zip_deflate
-  }
-} else {
-  JSZip.compressions["DEFLATE"].compress = zip_deflate;
-}
-
+		if(data.data) return data.name.substr(-4) !== ".bin" ? debom_xml(data.data) : char_codes(data.data);
+		if(data.asNodeBuffer && has_buf) return debom_xml(data.asNodeBuffer().toString('binary'));
+		if(data.asBinary) return debom_xml(data.asBinary());
+		if(data._data && data._data.getContent) return debom_xml(cc2str(Array.prototype.slice.call(data._data.getContent(),0)));
+	}
+	return null;
+}
+
+function getzipfile(zip, file) {
+	var f = file; if(zip.files[f]) return zip.files[f];
+	f = file.toLowerCase(); if(zip.files[f]) return zip.files[f];
+	f = f.replace(/\//g,'\\'); if(zip.files[f]) return zip.files[f];
+	throw new Error("Cannot find file " + file + " in zip");
+}
+
+function getzipdata(zip, file, safe) {
+	if(!safe) return getdata(getzipfile(zip, file));
+	if(!file) return null;
+	try { return getzipdata(zip, file); } catch(e) { return null; }
+}
+
+var _fs, jszip;
+if(typeof JSZip !== 'undefined') jszip = JSZip;
+if (typeof exports !== 'undefined') {
+	if (typeof module !== 'undefined' && module.exports) {
+		if(has_buf && typeof jszip === 'undefined') jszip = require('js'+'zip');
+		if(typeof jszip === 'undefined') jszip = require('./js'+'zip').JSZip;
+		_fs = require('f'+'s');
+	}
+}
+var attregexg=/\b[\w:]+=["'][^"]*['"]/g;
+var tagregex=/<[^>]*>/g;
+var nsregex=/<\w*:/, nsregex2 = /<(\/?)\w+:/;
+function parsexmltag(tag, skip_root) {
+	var z = [];
+	var eq = 0, c = 0;
+	for(; eq !== tag.length; ++eq) if((c = tag.charCodeAt(eq)) === 32 || c === 10 || c === 13) break;
+	if(!skip_root) z[0] = tag.substr(0, eq);
+	if(eq === tag.length) return z;
+	var m = tag.match(attregexg), j=0, w="", v="", i=0, q="", cc="";
+	if(m) for(i = 0; i != m.length; ++i) {
+		cc = m[i];
+		for(c=0; c != cc.length; ++c) if(cc.charCodeAt(c) === 61) break;
+		q = cc.substr(0,c); v = cc.substring(c+2, cc.length-1);
+		for(j=0;j!=q.length;++j) if(q.charCodeAt(j) === 58) break;
+		if(j===q.length) z[q] = v;
+		else z[(j===5 && q.substr(0,5)==="xmlns"?"xmlns":"")+q.substr(j+1)] = v;
+	}
+	return z;
+}
+function strip_ns(x) { return x.replace(nsregex2, "<$1"); }
+
+var encodings = {
+	'&quot;': '"',
+	'&apos;': "'",
+	'&gt;': '>',
+	'&lt;': '<',
+	'&amp;': '&'
+};
+var rencoding = evert(encodings);
+var rencstr = "&<>'\"".split("");
+
+// TODO: CP remap (need to read file version to determine OS)
+var encregex = /&[a-z]*;/g, coderegex = /_x([\da-fA-F]+)_/g;
+function unescapexml(text){
+	var s = text + '';
+	return s.replace(encregex, function($$) { return encodings[$$]; }).replace(coderegex,function(m,c) {return String.fromCharCode(parseInt(c,16));});
+}
+var decregex=/[&<>'"]/g, charegex = /[\u0000-\u0008\u000b-\u001f]/g;
+function escapexml(text){
+	var s = text + '';
+	return s.replace(decregex, function(y) { return rencoding[y]; }).replace(charegex,function(s) { return "_x" + ("000"+s.charCodeAt(0).toString(16)).substr(-4) + "_";});
+}
+
+function parsexmlbool(value, tag) {
+	switch(value) {
+		case '1': case 'true': case 'TRUE': return true;
+		/* case '0': case 'false': case 'FALSE':*/
+		default: return false;
+	}
+}
+
+var utf8read = function utf8reada(orig) {
+	var out = "", i = 0, c = 0, d = 0, e = 0, f = 0, w = 0;
+	while (i < orig.length) {
+		c = orig.charCodeAt(i++);
+		if (c < 128) { out += String.fromCharCode(c); continue; }
+		d = orig.charCodeAt(i++);
+		if (c>191 && c<224) { out += String.fromCharCode(((c & 31) << 6) | (d & 63)); continue; }
+		e = orig.charCodeAt(i++);
+		if (c < 240) { out += String.fromCharCode(((c & 15) << 12) | ((d & 63) << 6) | (e & 63)); continue; }
+		f = orig.charCodeAt(i++);
+		w = (((c & 7) << 18) | ((d & 63) << 12) | ((e & 63) << 6) | (f & 63))-65536;
+		out += String.fromCharCode(0xD800 + ((w>>>10)&1023));
+		out += String.fromCharCode(0xDC00 + (w&1023));
+	}
+	return out;
+};
+
+
+if(has_buf) {
+	var utf8readb = function utf8readb(data) {
+		var out = new Buffer(2*data.length), w, i, j = 1, k = 0, ww=0, c;
+		for(i = 0; i < data.length; i+=j) {
+			j = 1;
+			if((c=data.charCodeAt(i)) < 128) w = c;
+			else if(c < 224) { w = (c&31)*64+(data.charCodeAt(i+1)&63); j=2; }
+			else if(c < 240) { w=(c&15)*4096+(data.charCodeAt(i+1)&63)*64+(data.charCodeAt(i+2)&63); j=3; }
+			else { j = 4;
+				w = (c & 7)*262144+(data.charCodeAt(i+1)&63)*4096+(data.charCodeAt(i+2)&63)*64+(data.charCodeAt(i+3)&63);
+				w -= 65536; ww = 0xD800 + ((w>>>10)&1023); w = 0xDC00 + (w&1023);
+			}
+			if(ww !== 0) { out[k++] = ww&255; out[k++] = ww>>>8; ww = 0; }
+			out[k++] = w%256; out[k++] = w>>>8;
+		}
+		out.length = k;
+		return out.toString('ucs2');
+	};
+	var corpus = "foo bar baz\u00e2\u0098\u0083\u00f0\u009f\u008d\u00a3";
+	if(utf8read(corpus) == utf8readb(corpus)) utf8read = utf8readb;
+	var utf8readc = function utf8readc(data) { return Buffer(data, 'binary').toString('utf8'); };
+	if(utf8read(corpus) == utf8readc(corpus)) utf8read = utf8readc;
+}
+
+// matches <foo>...</foo> extracts content
+var matchtag = (function() {
+	var mtcache = {};
+	return function matchtag(f,g) {
+		var t = f+"|"+g;
+		if(mtcache[t] !== undefined) return mtcache[t];
+		return (mtcache[t] = new RegExp('<(?:\\w+:)?'+f+'(?: xml:space="preserve")?(?:[^>]*)>([^\u2603]*)</(?:\\w+:)?'+f+'>',(g||"")));
+	};
+})();
+
+var vtregex = (function(){ var vt_cache = {};
+	return function vt_regex(bt) {
+		if(vt_cache[bt] !== undefined) return vt_cache[bt];
+		return (vt_cache[bt] = new RegExp("<vt:" + bt + ">(.*?)</vt:" + bt + ">", 'g') );
+};})();
+var vtvregex = /<\/?vt:variant>/g, vtmregex = /<vt:([^>]*)>(.*)</;
+function parseVector(data) {
+	var h = parsexmltag(data);
+
+	var matches = data.match(vtregex(h.baseType))||[];
+	if(matches.length != h.size) throw "unexpected vector length " + matches.length + " != " + h.size;
+	var res = [];
+	matches.forEach(function(x) {
+		var v = x.replace(vtvregex,"").match(vtmregex);
+		res.push({v:v[2], t:v[1]});
+	});
+	return res;
+}
+
+var wtregex = /(^\s|\s$|\n)/;
+function writetag(f,g) {return '<' + f + (g.match(wtregex)?' xml:space="preserve"' : "") + '>' + g + '</' + f + '>';}
+
+function wxt_helper(h) { return keys(h).map(function(k) { return " " + k + '="' + h[k] + '"';}).join(""); }
+function writextag(f,g,h) { return '<' + f + (isval(h) ? wxt_helper(h) : "") + (isval(g) ? (g.match(wtregex)?' xml:space="preserve"' : "") + '>' + g + '</' + f : "/") + '>';}
+
+function write_w3cdtf(d, t) { try { return d.toISOString().replace(/\.\d*/,""); } catch(e) { if(t) throw e; } }
+
+function write_vt(s) {
+	switch(typeof s) {
+		case 'string': return writextag('vt:lpwstr', s);
+		case 'number': return writextag((s|0)==s?'vt:i4':'vt:r8', String(s));
+		case 'boolean': return writextag('vt:bool',s?'true':'false');
+	}
+	if(s instanceof Date) return writextag('vt:filetime', write_w3cdtf(s));
+	throw new Error("Unable to serialize " + s);
+}
+
+var XML_HEADER = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\r\n';
+var XMLNS = {
+	'dc': 'http://purl.org/dc/elements/1.1/',
+	'dcterms': 'http://purl.org/dc/terms/',
+	'dcmitype': 'http://purl.org/dc/dcmitype/',
+	'mx': 'http://schemas.microsoft.com/office/mac/excel/2008/main',
+	'r': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships',
+	'sjs': 'http://schemas.openxmlformats.org/package/2006/sheetjs/core-properties',
+	'vt': 'http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes',
+	'xsi': 'http://www.w3.org/2001/XMLSchema-instance',
+	'xsd': 'http://www.w3.org/2001/XMLSchema'
+};
+
+XMLNS.main = [
+	'http://schemas.openxmlformats.org/spreadsheetml/2006/main',
+	'http://purl.oclc.org/ooxml/spreadsheetml/main',
+	'http://schemas.microsoft.com/office/excel/2006/main',
+	'http://schemas.microsoft.com/office/excel/2006/2'
+];
+function readIEEE754(buf, idx, isLE, nl, ml) {
+	if(isLE === undefined) isLE = true;
+	if(!nl) nl = 8;
+	if(!ml && nl === 8) ml = 52;
+	var e, m, el = nl * 8 - ml - 1, eMax = (1 << el) - 1, eBias = eMax >> 1;
+	var bits = -7, d = isLE ? -1 : 1, i = isLE ? (nl - 1) : 0, s = buf[idx + i];
+
+	i += d;
+	e = s & ((1 << (-bits)) - 1); s >>>= (-bits); bits += el;
+	for (; bits > 0; e = e * 256 + buf[idx + i], i += d, bits -= 8);
+	m = e & ((1 << (-bits)) - 1); e >>>= (-bits); bits += ml;
+	for (; bits > 0; m = m * 256 + buf[idx + i], i += d, bits -= 8);
+	if (e === eMax) return m ? NaN : ((s ? -1 : 1) * Infinity);
+	else if (e === 0) e = 1 - eBias;
+	else { m = m + Math.pow(2, ml); e = e - eBias; }
+	return (s ? -1 : 1) * m * Math.pow(2, e - ml);
+}
+
+var __toBuffer, ___toBuffer;
+__toBuffer = ___toBuffer = function toBuffer_(bufs) { var x = []; for(var i = 0; i < bufs[0].length; ++i) { x.push.apply(x, bufs[0][i]); } return x; };
+var __double, ___double;
+__double = ___double = function(b, idx) { return readIEEE754(b, idx);};
+
+var is_buf = function is_buf_a(a) { return Array.isArray(a); };
+if(has_buf) {
+	__toBuffer = function(bufs) { return (bufs[0].length > 0 && Buffer.isBuffer(bufs[0][0])) ? Buffer.concat(bufs[0]) : ___toBuffer(bufs);};
+	__double = function double_(b,i) { if(Buffer.isBuffer(b)) return b.readDoubleLE(i); return ___double(b,i); };
+	is_buf = function is_buf_b(a) { return Buffer.isBuffer(a) || Array.isArray(a); };
+}
+
+
+var __readUInt8 = function(b, idx) { return b[idx]; };
+var __readUInt16LE = function(b, idx) { return b[idx+1]*(1<<8)+b[idx]; };
+var __readInt16LE = function(b, idx) { var u = b[idx+1]*(1<<8)+b[idx]; return (u < 0x8000) ? u : (0xffff - u + 1) * -1; };
+var __readUInt32LE = function(b, idx) { return b[idx+3]*(1<<24)+(b[idx+2]<<16)+(b[idx+1]<<8)+b[idx]; };
+var __readInt32LE = function(b, idx) { return (b[idx+3]<<24)|(b[idx+2]<<16)|(b[idx+1]<<8)|b[idx]; };
+
+
+function ReadShift(size, t) {
+	var o="", oo=[], w, vv, i, loc;
+	if(t === 'dbcs') {
+		loc = this.l;
+		if(has_buf && Buffer.isBuffer(this)) o = this.slice(this.l, this.l+2*size).toString("utf16le");
+		else for(i = 0; i != size; ++i) { o+=String.fromCharCode(__readUInt16LE(this, loc)); loc+=2; }
+		size *= 2;
+	} else switch(size) {
+		case 1: o = __readUInt8(this, this.l); break;
+		case 2: o = (t === 'i' ? __readInt16LE : __readUInt16LE)(this, this.l); break;
+		case 4: o = __readUInt32LE(this, this.l); break;
+		case 8: if(t === 'f') { o = __double(this, this.l); break; }
+	}
+	this.l+=size; return o;
+}
+
+function WriteShift(t, val, f) {
+	var size, i;
+	if(f === 'dbcs') {
+		for(i = 0; i != val.length; ++i) this.writeUInt16LE(val.charCodeAt(i), this.l + 2 * i);
+		size = 2 * val.length;
+	} else switch(t) {
+		case  1: size = 1; this[this.l] = val&255; break;
+		case  3: size = 3; this[this.l+2] = val & 255; val >>>= 8; this[this.l+1] = val&255; val >>>= 8; this[this.l] = val&255; break;
+		case  4: size = 4; this.writeUInt32LE(val, this.l); break;
+		case  8: size = 8; if(f === 'f') { this.writeDoubleLE(val, this.l); break; }
+		/* falls through */
+		case 16: break;
+		case -4: size = 4; this.writeInt32LE(val, this.l); break;
+	}
+	this.l += size; return this;
+}
+
+function prep_blob(blob, pos) {
+	blob.l = pos;
+	blob.read_shift = ReadShift;
+	blob.write_shift = WriteShift;
+}
+
+function parsenoop(blob, length) { blob.l += length; }
+
+function writenoop(blob, length) { blob.l += length; }
+
+function new_buf(sz) {
+	var o = has_buf ? new Buffer(sz) : new Array(sz);
+	prep_blob(o, 0);
+	return o;
+}
+
+/* [MS-XLSB] 2.1.4 Record */
+function recordhopper(data, cb, opts) {
+	var tmpbyte, cntbyte, length;
+	prep_blob(data, data.l || 0);
+	while(data.l < data.length) {
+		var RT = data.read_shift(1);
+		if(RT & 0x80) RT = (RT & 0x7F) + ((data.read_shift(1) & 0x7F)<<7);
+		var R = RecordEnum[RT] || RecordEnum[0xFFFF];
+		tmpbyte = data.read_shift(1);
+		length = tmpbyte & 0x7F;
+		for(cntbyte = 1; cntbyte <4 && (tmpbyte & 0x80); ++cntbyte) length += ((tmpbyte = data.read_shift(1)) & 0x7F)<<(7*cntbyte);
+		var d = R.f(data, length, opts);
+		if(cb(d, R, RT)) return;
+	}
+}
+
+/* control buffer usage for fixed-length buffers */
+function buf_array() {
+	var bufs = [], blksz = 2048;
+	var newblk = function ba_newblk(sz) {
+		var o = new_buf(sz);
+		prep_blob(o, 0);
+		return o;
+	};
+
+	var curbuf = newblk(blksz);
+
+	var endbuf = function ba_endbuf() {
+		curbuf.length = curbuf.l;
+		if(curbuf.length > 0) bufs.push(curbuf);
+		curbuf = null;
+	};
+
+	var next = function ba_next(sz) {
+		if(sz < curbuf.length - curbuf.l) return curbuf;
+		endbuf();
+		return (curbuf = newblk(Math.max(sz+1, blksz)));
+	};
+
+	var end = function ba_end() {
+		endbuf();
+		return __toBuffer([bufs]);
+	};
+
+	var push = function ba_push(buf) { endbuf(); curbuf = buf; next(blksz); };
+
+	return { next:next, push:push, end:end, _bufs:bufs };
+}
+
+function write_record(ba, type, payload, length) {
+	var t = evert_RE[type], l;
+	if(!length) length = RecordEnum[t].p || (payload||[]).length || 0;
+	l = 1 + (t >= 0x80 ? 1 : 0) + 1 + length;
+	if(length >= 0x80) ++l; if(length >= 0x4000) ++l; if(length >= 0x200000) ++l;
+	var o = ba.next(l);
+	if(t <= 0x7F) o.write_shift(1, t);
+	else {
+		o.write_shift(1, (t & 0x7F) + 0x80);
+		o.write_shift(1, (t >> 7));
+	}
+	for(var i = 0; i != 4; ++i) {
+		if(length >= 0x80) { o.write_shift(1, (length & 0x7F)+0x80); length >>= 7; }
+		else { o.write_shift(1, length); break; }
+	}
+	if(length > 0 && is_buf(payload)) ba.push(payload);
+}
+
+/* [MS-XLSB] 2.5.143 */
+function parse_StrRun(data, length) {
+	return { ich: data.read_shift(2), ifnt: data.read_shift(2) };
+}
+
+/* [MS-XLSB] 2.1.7.121 */
+function parse_RichStr(data, length) {
+	var start = data.l;
+	var flags = data.read_shift(1);
+	var str = parse_XLWideString(data);
+	var rgsStrRun = [];
+	var z = { t: str, h: str };
+	if((flags & 1) !== 0) { /* fRichStr */
+		/* TODO: formatted string */
+		var dwSizeStrRun = data.read_shift(4);
+		for(var i = 0; i != dwSizeStrRun; ++i) rgsStrRun.push(parse_StrRun(data));
+		z.r = rgsStrRun;
+	}
+	else z.r = "<t>" + escapexml(str) + "</t>";
+	if((flags & 2) !== 0) { /* fExtStr */
+		/* TODO: phonetic string */
+	}
+	data.l = start + length;
+	return z;
+}
+function write_RichStr(str, o) {
+	/* TODO: formatted string */
+	if(o == null) o = new_buf(5+2*str.t.length);
+	o.write_shift(1,0);
+	write_XLWideString(str.t, o);
+	return o;
+}
+
+/* [MS-XLSB] 2.5.9 */
+function parse_Cell(data) {
+	var col = data.read_shift(4);
+	var iStyleRef = data.read_shift(2);
+	iStyleRef += data.read_shift(1) <<16;
+	var fPhShow = data.read_shift(1);
+	return { c:col, iStyleRef: iStyleRef };
+}
+function write_Cell(cell, o) {
+	if(o == null) o = new_buf(8);
+	o.write_shift(-4, cell.c);
+	o.write_shift(3, cell.iStyleRef === undefined ? cell.iStyleRef : cell.s);
+	o.write_shift(1, 0); /* fPhShow */
+	return o;
+}
+
+
+/* [MS-XLSB] 2.5.21 */
+function parse_CodeName (data, length) { return parse_XLWideString(data, length); }
+
+/* [MS-XLSB] 2.5.166 */
+function parse_XLNullableWideString(data) {
+	var cchCharacters = data.read_shift(4);
+	return cchCharacters === 0 || cchCharacters === 0xFFFFFFFF ? "" : data.read_shift(cchCharacters, 'dbcs');
+}
+function write_XLNullableWideString(data, o) {
+	if(!o) o = new_buf(127);
+	o.write_shift(4, data.length > 0 ? data.length : 0xFFFFFFFF);
+	if(data.length > 0) o.write_shift(0, data, 'dbcs');
+	return o;
+}
+
+/* [MS-XLSB] 2.5.168 */
+function parse_XLWideString(data) {
+	var cchCharacters = data.read_shift(4);
+	return cchCharacters === 0 ? "" : data.read_shift(cchCharacters, 'dbcs');
+}
+function write_XLWideString(data, o) {
+	if(o == null) o = new_buf(4+2*data.length);
+	o.write_shift(4, data.length);
+	if(data.length > 0) o.write_shift(0, data, 'dbcs');
+	return o;
+}
+
+/* [MS-XLSB] 2.5.114 */
+var parse_RelID = parse_XLNullableWideString;
+var write_RelID = write_XLNullableWideString;
+
+
+/* [MS-XLSB] 2.5.122 */
+function parse_RkNumber(data) {
+	var b = data.slice(data.l, data.l+4);
+	var fX100 = b[0] & 1, fInt = b[0] & 2;
+	data.l+=4;
+	b[0] &= 0xFC;
+	var RK = fInt === 0 ? __double([0,0,0,0,b[0],b[1],b[2],b[3]],0) : __readInt32LE(b,0)>>2;
+	return fX100 ? RK/100 : RK;
+}
+
+/* [MS-XLSB] 2.5.153 */
+function parse_UncheckedRfX(data) {
+	var cell = {s: {}, e: {}};
+	cell.s.r = data.read_shift(4);
+	cell.e.r = data.read_shift(4);
+	cell.s.c = data.read_shift(4);
+	cell.e.c = data.read_shift(4);
+	return cell;
+}
+
+function write_UncheckedRfX(r, o) {
+	if(!o) o = new_buf(16);
+	o.write_shift(4, r.s.r);
+	o.write_shift(4, r.e.r);
+	o.write_shift(4, r.s.c);
+	o.write_shift(4, r.e.c);
+	return o;
+}
+
+/* [MS-XLSB] 2.5.171 */
+function parse_Xnum(data, length) { return data.read_shift(8, 'f'); }
+function write_Xnum(data, o) { return (o || new_buf(8)).write_shift(8, 'f', data); }
+
+/* [MS-XLSB] 2.5.198.2 */
+var BErr = {
+	0x00: "#NULL!",
+	0x07: "#DIV/0!",
+	0x0F: "#VALUE!",
+	0x17: "#REF!",
+	0x1D: "#NAME?",
+	0x24: "#NUM!",
+	0x2A: "#N/A",
+	0x2B: "#GETTING_DATA",
+	0xFF: "#WTF?"
+};
+var RBErr = evert_num(BErr);
+
+/* [MS-XLSB] 2.4.321 BrtColor */
+function parse_BrtColor(data, length) {
+	var out = {};
+	var d = data.read_shift(1);
+	out.fValidRGB = d & 1;
+	out.xColorType = d >>> 1;
+	out.index = data.read_shift(1);
+	out.nTintAndShade = data.read_shift(2, 'i');
+	out.bRed   = data.read_shift(1);
+	out.bGreen = data.read_shift(1);
+	out.bBlue  = data.read_shift(1);
+	out.bAlpha = data.read_shift(1);
+}
+
+/* [MS-XLSB] 2.5.52 */
+function parse_FontFlags(data, length) {
+	var d = data.read_shift(1);
+	data.l++;
+	var out = {
+		fItalic: d & 0x2,
+		fStrikeout: d & 0x8,
+		fOutline: d & 0x10,
+		fShadow: d & 0x20,
+		fCondense: d & 0x40,
+		fExtend: d & 0x80
+	};
+	return out;
+}
+/* Parts enumerated in OPC spec, MS-XLSB and MS-XLSX */
+/* 12.3 Part Summary <SpreadsheetML> */
+/* 14.2 Part Summary <DrawingML> */
+/* [MS-XLSX] 2.1 Part Enumerations */
+/* [MS-XLSB] 2.1.7 Part Enumeration */
+var ct2type = {
+	/* Workbook */
+	"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml": "workbooks",
+
+	/* Worksheet */
+	"application/vnd.ms-excel.binIndexWs": "TODO", /* Binary Index */
+
+	/* Chartsheet */
+	"application/vnd.ms-excel.chartsheet": "TODO",
+	"application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml": "TODO",
+
+	/* Dialogsheet */
+	"application/vnd.ms-excel.dialogsheet": "TODO",
+	"application/vnd.openxmlformats-officedocument.spreadsheetml.dialogsheet+xml": "TODO",
+
+	/* Macrosheet */
+	"application/vnd.ms-excel.macrosheet": "TODO",
+	"application/vnd.ms-excel.macrosheet+xml": "TODO",
+	"application/vnd.ms-excel.intlmacrosheet": "TODO",
+	"application/vnd.ms-excel.binIndexMs": "TODO", /* Binary Index */
+
+	/* File Properties */
+	"application/vnd.openxmlformats-package.core-properties+xml": "coreprops",
+	"application/vnd.openxmlformats-officedocument.custom-properties+xml": "custprops",
+	"application/vnd.openxmlformats-officedocument.extended-properties+xml": "extprops",
+
+	/* Custom Data Properties */
+	"application/vnd.openxmlformats-officedocument.customXmlProperties+xml": "TODO",
+
+	/* Comments */
+	"application/vnd.ms-excel.comments": "comments",
+	"application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml": "comments",
+
+	/* PivotTable */
+	"application/vnd.ms-excel.pivotTable": "TODO",
+	"application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml": "TODO",
+
+	/* Calculation Chain */
+	"application/vnd.ms-excel.calcChain": "calcchains",
+	"application/vnd.openxmlformats-officedocument.spreadsheetml.calcChain+xml": "calcchains",
+
+	/* Printer Settings */
+	"application/vnd.openxmlformats-officedocument.spreadsheetml.printerSettings": "TODO",
+
+	/* ActiveX */
+	"application/vnd.ms-office.activeX": "TODO",
+	"application/vnd.ms-office.activeX+xml": "TODO",
+
+	/* Custom Toolbars */
+	"application/vnd.ms-excel.attachedToolbars": "TODO",
+
+	/* External Data Connections */
+	"application/vnd.ms-excel.connections": "TODO",
+	"application/vnd.openxmlformats-officedocument.spreadsheetml.connections+xml": "TODO",
+
+	/* External Links */
+	"application/vnd.ms-excel.externalLink": "TODO",
+	"application/vnd.openxmlformats-officedocument.spreadsheetml.externalLink+xml": "TODO",
+
+	/* Metadata */
+	"application/vnd.ms-excel.sheetMetadata": "TODO",
+	"application/vnd.openxmlformats-officedocument.spreadsheetml.sheetMetadata+xml": "TODO",
+
+	/* PivotCache */
+	"application/vnd.ms-excel.pivotCacheDefinition": "TODO",
+	"application/vnd.ms-excel.pivotCacheRecords": "TODO",
+	"application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml": "TODO",
+	"application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheRecords+xml": "TODO",
+
+	/* Query Table */
+	"application/vnd.ms-excel.queryTable": "TODO",
+	"application/vnd.openxmlformats-officedocument.spreadsheetml.queryTable+xml": "TODO",
+
+	/* Shared Workbook */
+	"application/vnd.ms-excel.userNames": "TODO",
+	"application/vnd.ms-excel.revisionHeaders": "TODO",
+	"application/vnd.ms-excel.revisionLog": "TODO",
+	"application/vnd.openxmlformats-officedocument.spreadsheetml.revisionHeaders+xml": "TODO",
+	"application/vnd.openxmlformats-officedocument.spreadsheetml.revisionLog+xml": "TODO",
+	"application/vnd.openxmlformats-officedocument.spreadsheetml.userNames+xml": "TODO",
+
+	/* Single Cell Table */
+	"application/vnd.ms-excel.tableSingleCells": "TODO",
+	"application/vnd.openxmlformats-officedocument.spreadsheetml.tableSingleCells+xml": "TODO",
+
+	/* Slicer */
+	"application/vnd.ms-excel.slicer": "TODO",
+	"application/vnd.ms-excel.slicerCache": "TODO",
+	"application/vnd.ms-excel.slicer+xml": "TODO",
+	"application/vnd.ms-excel.slicerCache+xml": "TODO",
+
+	/* Sort Map */
+	"application/vnd.ms-excel.wsSortMap": "TODO",
+
+	/* Table */
+	"application/vnd.ms-excel.table": "TODO",
+	"application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml": "TODO",
+
+	/* Themes */
+	"application/vnd.openxmlformats-officedocument.theme+xml": "themes",
+
+	/* Timeline */
+	"application/vnd.ms-excel.Timeline+xml": "TODO", /* verify */
+	"application/vnd.ms-excel.TimelineCache+xml": "TODO", /* verify */
+
+	/* VBA */
+	"application/vnd.ms-office.vbaProject": "vba",
+	"application/vnd.ms-office.vbaProjectSignature": "vba",
+
+	/* Volatile Dependencies */
+	"application/vnd.ms-office.volatileDependencies": "TODO",
+	"application/vnd.openxmlformats-officedocument.spreadsheetml.volatileDependencies+xml": "TODO",
+
+	/* Control Properties */
+	"application/vnd.ms-excel.controlproperties+xml": "TODO",
+
+	/* Data Model */
+	"application/vnd.openxmlformats-officedocument.model+data": "TODO",
+
+	/* Survey */
+	"application/vnd.ms-excel.Survey+xml": "TODO",
+
+	/* Drawing */
+	"application/vnd.openxmlformats-officedocument.drawing+xml": "TODO",
+	"application/vnd.openxmlformats-officedocument.drawingml.chart+xml": "TODO",
+	"application/vnd.openxmlformats-officedocument.drawingml.chartshapes+xml": "TODO",
+	"application/vnd.openxmlformats-officedocument.drawingml.diagramColors+xml": "TODO",
+	"application/vnd.openxmlformats-officedocument.drawingml.diagramData+xml": "TODO",
+	"application/vnd.openxmlformats-officedocument.drawingml.diagramLayout+xml": "TODO",
+	"application/vnd.openxmlformats-officedocument.drawingml.diagramStyle+xml": "TODO",
+
+	/* VML */
+	"application/vnd.openxmlformats-officedocument.vmlDrawing": "TODO",
+
+	"application/vnd.openxmlformats-package.relationships+xml": "rels",
+	"application/vnd.openxmlformats-officedocument.oleObject": "TODO",
+
+	"sheet": "js"
+};
+
+var CT_LIST = (function(){
+	var o = {
+		workbooks: {
+			xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml",
+			xlsm: "application/vnd.ms-excel.sheet.macroEnabled.main+xml",
+			xlsb: "application/vnd.ms-excel.sheet.binary.macroEnabled.main",
+			xltx: "application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml"
+		},
+		strs: { /* Shared Strings */
+			xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml",
+			xlsb: "application/vnd.ms-excel.sharedStrings"
+		},
+		sheets: {
+			xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml",
+			xlsb: "application/vnd.ms-excel.worksheet"
+		},
+		styles: {/* Styles */
+			xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml",
+			xlsb: "application/vnd.ms-excel.styles"
+		}
+	};
+	keys(o).forEach(function(k) { if(!o[k].xlsm) o[k].xlsm = o[k].xlsx; });
+	keys(o).forEach(function(k){ keys(o[k]).forEach(function(v) { ct2type[o[k][v]] = k; }); });
+	return o;
+})();
+
+var type2ct = evert_arr(ct2type);
+
+XMLNS.CT = 'http://schemas.openxmlformats.org/package/2006/content-types';
+
+function parse_ct(data, opts) {
+	var ctext = {};
+	if(!data || !data.match) return data;
+	var ct = { workbooks: [], sheets: [], calcchains: [], themes: [], styles: [],
+		coreprops: [], extprops: [], custprops: [], strs:[], comments: [], vba: [],
+		TODO:[], rels:[], xmlns: "" };
+	(data.match(tagregex)||[]).forEach(function(x) {
+		var y = parsexmltag(x);
+		switch(y[0].replace(nsregex,"<")) {
+			case '<?xml': break;
+			case '<Types': ct.xmlns = y['xmlns' + (y[0].match(/<(\w+):/)||["",""])[1] ]; break;
+			case '<Default': ctext[y.Extension] = y.ContentType; break;
+			case '<Override':
+				if(ct[ct2type[y.ContentType]] !== undefined) ct[ct2type[y.ContentType]].push(y.PartName);
+				else if(opts.WTF) console.error(y);
+				break;
+		}
+	});
+	if(ct.xmlns !== XMLNS.CT) throw new Error("Unknown Namespace: " + ct.xmlns);
+	ct.calcchain = ct.calcchains.length > 0 ? ct.calcchains[0] : "";
+	ct.sst = ct.strs.length > 0 ? ct.strs[0] : "";
+	ct.style = ct.styles.length > 0 ? ct.styles[0] : "";
+	ct.defaults = ctext;
+	delete ct.calcchains;
+	return ct;
+}
+
+var CTYPE_XML_ROOT = writextag('Types', null, {
+	'xmlns': XMLNS.CT,
+	'xmlns:xsd': XMLNS.xsd,
+	'xmlns:xsi': XMLNS.xsi
+});
+
+var CTYPE_DEFAULTS = [
+	['xml', 'application/xml'],
+	['bin', 'application/vnd.ms-excel.sheet.binary.macroEnabled.main'],
+	['rels', type2ct.rels[0]]
+].map(function(x) {
+	return writextag('Default', null, {'Extension':x[0], 'ContentType': x[1]});
+});
+
+function write_ct(ct, opts) {
+	var o = [], v;
+	o[o.length] = (XML_HEADER);
+	o[o.length] = (CTYPE_XML_ROOT);
+	o = o.concat(CTYPE_DEFAULTS);
+	var f1 = function(w) {
+		if(ct[w] && ct[w].length > 0) {
+			v = ct[w][0];
+			o[o.length] = (writextag('Override', null, {
+				'PartName': (v[0] == '/' ? "":"/") + v,
+				'ContentType': CT_LIST[w][opts.bookType || 'xlsx']
+			}));
+		}
+	};
+	var f2 = function(w) {
+		ct[w].forEach(function(v) {
+			o[o.length] = (writextag('Override', null, {
+				'PartName': (v[0] == '/' ? "":"/") + v,
+				'ContentType': CT_LIST[w][opts.bookType || 'xlsx']
+			}));
+		});
+	};
+	var f3 = function(t) {
+		(ct[t]||[]).forEach(function(v) {
+			o[o.length] = (writextag('Override', null, {
+				'PartName': (v[0] == '/' ? "":"/") + v,
+				'ContentType': type2ct[t][0]
+			}));
+		});
+	};
+	f1('workbooks');
+	f2('sheets');
+	f3('themes');
+	['strs', 'styles'].forEach(f1);
+	['coreprops', 'extprops', 'custprops'].forEach(f3);
+	if(o.length>2){ o[o.length] = ('</Types>'); o[1]=o[1].replace("/>",">"); }
+	return o.join("");
+}
+/* 9.3.2 OPC Relationships Markup */
+var RELS = {
+	WB: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument",
+	SHEET: "http://sheetjs.openxmlformats.org/officeDocument/2006/relationships/officeDocument"
+};
+
+function parse_rels(data, currentFilePath) {
+	if (!data) return data;
+	if (currentFilePath.charAt(0) !== '/') {
+		currentFilePath = '/'+currentFilePath;
+	}
+	var rels = {};
+	var hash = {};
+	var resolveRelativePathIntoAbsolute = function (to) {
+		var toksFrom = currentFilePath.split('/');
+		toksFrom.pop(); // folder path
+		var toksTo = to.split('/');
+		var reversed = [];
+		while (toksTo.length !== 0) {
+			var tokTo = toksTo.shift();
+			if (tokTo === '..') {
+				toksFrom.pop();
+			} else if (tokTo !== '.') {
+				toksFrom.push(tokTo);
+			}
+		}
+		return toksFrom.join('/');
+	};
+
+	data.match(tagregex).forEach(function(x) {
+		var y = parsexmltag(x);
+		/* 9.3.2.2 OPC_Relationships */
+		if (y[0] === '<Relationship') {
+			var rel = {}; rel.Type = y.Type; rel.Target = y.Target; rel.Id = y.Id; rel.TargetMode = y.TargetMode;
+			var canonictarget = y.TargetMode === 'External' ? y.Target : resolveRelativePathIntoAbsolute(y.Target);
+			rels[canonictarget] = rel;
+			hash[y.Id] = rel;
+		}
+	});
+	rels["!id"] = hash;
+	return rels;
+}
+
+XMLNS.RELS = 'http://schemas.openxmlformats.org/package/2006/relationships';
+
+var RELS_ROOT = writextag('Relationships', null, {
+	//'xmlns:ns0': XMLNS.RELS,
+	'xmlns': XMLNS.RELS
+});
+
+/* TODO */
+function write_rels(rels) {
+	var o = [];
+	o[o.length] = (XML_HEADER);
+	o[o.length] = (RELS_ROOT);
+	keys(rels['!id']).forEach(function(rid) { var rel = rels['!id'][rid];
+		o[o.length] = (writextag('Relationship', null, rel));
+	});
+	if(o.length>2){ o[o.length] = ('</Relationships>'); o[1]=o[1].replace("/>",">"); }
+	return o.join("");
+}
+/* ECMA-376 Part II 11.1 Core Properties Part */
+/* [MS-OSHARED] 2.3.3.2.[1-2].1 (PIDSI/PIDDSI) */
+var CORE_PROPS = [
+	["cp:category", "Category"],
+	["cp:contentStatus", "ContentStatus"],
+	["cp:keywords", "Keywords"],
+	["cp:lastModifiedBy", "LastAuthor"],
+	["cp:lastPrinted", "LastPrinted"],
+	["cp:revision", "RevNumber"],
+	["cp:version", "Version"],
+	["dc:creator", "Author"],
+	["dc:description", "Comments"],
+	["dc:identifier", "Identifier"],
+	["dc:language", "Language"],
+	["dc:subject", "Subject"],
+	["dc:title", "Title"],
+	["dcterms:created", "CreatedDate", 'date'],
+	["dcterms:modified", "ModifiedDate", 'date']
+];
+
+XMLNS.CORE_PROPS = "http://schemas.openxmlformats.org/package/2006/metadata/core-properties";
+RELS.CORE_PROPS  = 'http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties';
+
+var CORE_PROPS_REGEX = (function() {
+	var r = new Array(CORE_PROPS.length);
+	for(var i = 0; i < CORE_PROPS.length; ++i) {
+		var f = CORE_PROPS[i];
+		var g = "(?:"+ f[0].substr(0,f[0].indexOf(":")) +":)"+ f[0].substr(f[0].indexOf(":")+1);
+		r[i] = new RegExp("<" + g + "[^>]*>(.*)<\/" + g + ">");
+	}
+	return r;
+})();
+
+function parse_core_props(data) {
+	var p = {};
+
+	for(var i = 0; i < CORE_PROPS.length; ++i) {
+		var f = CORE_PROPS[i], cur = data.match(CORE_PROPS_REGEX[i]);
+		if(cur != null && cur.length > 0) p[f[1]] = cur[1];
+		if(f[2] === 'date' && p[f[1]]) p[f[1]] = new Date(p[f[1]]);
+	}
+
+	return p;
+}
+
+var CORE_PROPS_XML_ROOT = writextag('cp:coreProperties', null, {
+	//'xmlns': XMLNS.CORE_PROPS,
+	'xmlns:cp': XMLNS.CORE_PROPS,
+	'xmlns:dc': XMLNS.dc,
+	'xmlns:dcterms': XMLNS.dcterms,
+	'xmlns:dcmitype': XMLNS.dcmitype,
+	'xmlns:xsi': XMLNS.xsi
+});
+
+function cp_doit(f, g, h, o, p) {
+	if(p[f] != null || g == null || g === "") return;
+	p[f] = g;
+	o[o.length] = (h ? writextag(f,g,h) : writetag(f,g));
+}
+
+function write_core_props(cp, opts) {
+	var o = [XML_HEADER, CORE_PROPS_XML_ROOT], p = {};
+	if(!cp) return o.join("");
+
+
+	if(cp.CreatedDate != null) cp_doit("dcterms:created", typeof cp.CreatedDate === "string" ? cp.CreatedDate : write_w3cdtf(cp.CreatedDate, opts.WTF), {"xsi:type":"dcterms:W3CDTF"}, o, p);
+	if(cp.ModifiedDate != null) cp_doit("dcterms:modified", typeof cp.ModifiedDate === "string" ? cp.ModifiedDate : write_w3cdtf(cp.ModifiedDate, opts.WTF), {"xsi:type":"dcterms:W3CDTF"}, o, p);
+
+	for(var i = 0; i != CORE_PROPS.length; ++i) { var f = CORE_PROPS[i]; cp_doit(f[0], cp[f[1]], null, o, p); }
+	if(o.length>2){ o[o.length] = ('</cp:coreProperties>'); o[1]=o[1].replace("/>",">"); }
+	return o.join("");
+}
+/* 15.2.12.3 Extended File Properties Part */
+/* [MS-OSHARED] 2.3.3.2.[1-2].1 (PIDSI/PIDDSI) */
+var EXT_PROPS = [
+	["Application", "Application", "string"],
+	["AppVersion", "AppVersion", "string"],
+	["Company", "Company", "string"],
+	["DocSecurity", "DocSecurity", "string"],
+	["Manager", "Manager", "string"],
+	["HyperlinksChanged", "HyperlinksChanged", "bool"],
+	["SharedDoc", "SharedDoc", "bool"],
+	["LinksUpToDate", "LinksUpToDate", "bool"],
+	["ScaleCrop", "ScaleCrop", "bool"],
+	["HeadingPairs", "HeadingPairs", "raw"],
+	["TitlesOfParts", "TitlesOfParts", "raw"]
+];
+
+XMLNS.EXT_PROPS = "http://schemas.openxmlformats.org/officeDocument/2006/extended-properties";
+RELS.EXT_PROPS  = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties';
+
+function parse_ext_props(data, p) {
+	var q = {}; if(!p) p = {};
+
+	EXT_PROPS.forEach(function(f) {
+		switch(f[2]) {
+			case "string": p[f[1]] = (data.match(matchtag(f[0]))||[])[1]; break;
+			case "bool": p[f[1]] = (data.match(matchtag(f[0]))||[])[1] === "true"; break;
+			case "raw":
+				var cur = data.match(new RegExp("<" + f[0] + "[^>]*>(.*)<\/" + f[0] + ">"));
+				if(cur && cur.length > 0) q[f[1]] = cur[1];
+				break;
+		}
+	});
+
+	if(q.HeadingPairs && q.TitlesOfParts) {
+		var v = parseVector(q.HeadingPairs);
+		var j = 0, widx = 0;
+		for(var i = 0; i !== v.length; ++i) {
+			switch(v[i].v) {
+				case "Worksheets": widx = j; p.Worksheets = +(v[++i].v); break;
+				case "Named Ranges": ++i; break; // TODO: Handle Named Ranges
+			}
+		}
+		var parts = parseVector(q.TitlesOfParts).map(function(x) { return utf8read(x.v); });
+		p.SheetNames = parts.slice(widx, widx + p.Worksheets);
+	}
+	return p;
+}
+
+var EXT_PROPS_XML_ROOT = writextag('Properties', null, {
+	'xmlns': XMLNS.EXT_PROPS,
+	'xmlns:vt': XMLNS.vt
+});
+
+function write_ext_props(cp, opts) {
+	var o = [], p = {}, W = writextag;
+	if(!cp) cp = {};
+	cp.Application = "SheetJS";
+	o[o.length] = (XML_HEADER);
+	o[o.length] = (EXT_PROPS_XML_ROOT);
+
+	EXT_PROPS.forEach(function(f) {
+		if(cp[f[1]] === undefined) return;
+		var v;
+		switch(f[2]) {
+			case 'string': v = cp[f[1]]; break;
+			case 'bool': v = cp[f[1]] ? 'true' : 'false'; break;
+		}
+		if(v !== undefined) o[o.length] = (W(f[0], v));
+	});
+
+	/* TODO: HeadingPairs, TitlesOfParts */
+	o[o.length] = (W('HeadingPairs', W('vt:vector', W('vt:variant', '<vt:lpstr>Worksheets</vt:lpstr>')+W('vt:variant', W('vt:i4', String(cp.Worksheets))), {size:2, baseType:"variant"})));
+	o[o.length] = (W('TitlesOfParts', W('vt:vector', cp.SheetNames.map(function(s) { return "<vt:lpstr>" + s + "</vt:lpstr>"; }).join(""), {size: cp.Worksheets, baseType:"lpstr"})));
+	if(o.length>2){ o[o.length] = ('</Properties>'); o[1]=o[1].replace("/>",">"); }
+	return o.join("");
+}
+/* 15.2.12.2 Custom File Properties Part */
+XMLNS.CUST_PROPS = "http://schemas.openxmlformats.org/officeDocument/2006/custom-properties";
+RELS.CUST_PROPS  = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties';
+
+var custregex = /<[^>]+>[^<]*/g;
+function parse_cust_props(data, opts) {
+	var p = {}, name;
+	var m = data.match(custregex);
+	if(m) for(var i = 0; i != m.length; ++i) {
+		var x = m[i], y = parsexmltag(x);
+		switch(y[0]) {
+			case '<?xml': break;
+			case '<Properties':
+				if(y.xmlns !== XMLNS.CUST_PROPS) throw "unrecognized xmlns " + y.xmlns;
+				if(y.xmlnsvt && y.xmlnsvt !== XMLNS.vt) throw "unrecognized vt " + y.xmlnsvt;
+				break;
+			case '<property': name = y.name; break;
+			case '</property>': name = null; break;
+			default: if (x.indexOf('<vt:') === 0) {
+				var toks = x.split('>');
+				var type = toks[0].substring(4), text = toks[1];
+				/* 22.4.2.32 (CT_Variant). Omit the binary types from 22.4 (Variant Types) */
+				switch(type) {
+					case 'lpstr': case 'lpwstr': case 'bstr': case 'lpwstr':
+						p[name] = unescapexml(text);
+						break;
+					case 'bool':
+						p[name] = parsexmlbool(text, '<vt:bool>');
+						break;
+					case 'i1': case 'i2': case 'i4': case 'i8': case 'int': case 'uint':
+						p[name] = parseInt(text, 10);
+						break;
+					case 'r4': case 'r8': case 'decimal':
+						p[name] = parseFloat(text);
+						break;
+					case 'filetime': case 'date':
+						p[name] = new Date(text);
+						break;
+					case 'cy': case 'error':
+						p[name] = unescapexml(text);
+						break;
+					default:
+						if(typeof console !== 'undefined') console.warn('Unexpected', x, type, toks);
+				}
+			} else if(x.substr(0,2) === "</") {
+			} else if(opts.WTF) throw new Error(x);
+		}
+	}
+	return p;
+}
+
+var CUST_PROPS_XML_ROOT = writextag('Properties', null, {
+	'xmlns': XMLNS.CUST_PROPS,
+	'xmlns:vt': XMLNS.vt
+});
+
+function write_cust_props(cp, opts) {
+	var o = [XML_HEADER, CUST_PROPS_XML_ROOT];
+	if(!cp) return o.join("");
+	var pid = 1;
+	keys(cp).forEach(function custprop(k) { ++pid;
+		o[o.length] = (writextag('property', write_vt(cp[k]), {
+			'fmtid': '{D5CDD505-2E9C-101B-9397-08002B2CF9AE}',
+			'pid': pid,
+			'name': k
+		}));
+	});
+	if(o.length>2){ o[o.length] = '</Properties>'; o[1]=o[1].replace("/>",">"); }
+	return o.join("");
+}
+/* 18.4.1 charset to codepage mapping */
+var CS2CP = {
+	0:    1252, /* ANSI */
+	1:   65001, /* DEFAULT */
+	2:   65001, /* SYMBOL */
+	77:  10000, /* MAC */
+	128:   932, /* SHIFTJIS */
+	129:   949, /* HANGUL */
+	130:  1361, /* JOHAB */
+	134:   936, /* GB2312 */
+	136:   950, /* CHINESEBIG5 */
+	161:  1253, /* GREEK */
+	162:  1254, /* TURKISH */
+	163:  1258, /* VIETNAMESE */
+	177:  1255, /* HEBREW */
+	178:  1256, /* ARABIC */
+	186:  1257, /* BALTIC */
+	204:  1251, /* RUSSIAN */
+	222:   874, /* THAI */
+	238:  1250, /* EASTEUROPE */
+	255:  1252, /* OEM */
+	69:   6969  /* MISC */
+};
+
+/* Parse a list of <r> tags */
+var parse_rs = (function parse_rs_factory() {
+	var tregex = matchtag("t"), rpregex = matchtag("rPr"), rregex = /<r>/g, rend = /<\/r>/, nlregex = /\r\n/g;
+	/* 18.4.7 rPr CT_RPrElt */
+	var parse_rpr = function parse_rpr(rpr, intro, outro) {
+		var font = {}, cp = 65001;
+		var m = rpr.match(tagregex), i = 0;
+		if(m) for(;i!=m.length; ++i) {
+			var y = parsexmltag(m[i]);
+			switch(y[0]) {
+				/* 18.8.12 condense CT_BooleanProperty */
+				/* ** not required . */
+				case '<condense': break;
+				/* 18.8.17 extend CT_BooleanProperty */
+				/* ** not required . */
+				case '<extend': break;
+				/* 18.8.36 shadow CT_BooleanProperty */
+				/* ** not required . */
+				case '<shadow':
+					/* falls through */
+				case '<shadow/>': break;
+
+				/* 18.4.1 charset CT_IntProperty TODO */
+				case '<charset':
+					if(y.val == '1') break;
+					cp = CS2CP[parseInt(y.val, 10)];
+					break;
+
+				/* 18.4.2 outline CT_BooleanProperty TODO */
+				case '<outline':
+					/* falls through */
+				case '<outline/>': break;
+
+				/* 18.4.5 rFont CT_FontName */
+				case '<rFont': font.name = y.val; break;
+
+				/* 18.4.11 sz CT_FontSize */
+				case '<sz': font.sz = y.val; break;
+
+				/* 18.4.10 strike CT_BooleanProperty */
+				case '<strike':
+					if(!y.val) break;
+					/* falls through */
+				case '<strike/>': font.strike = 1; break;
+				case '</strike>': break;
+
+				/* 18.4.13 u CT_UnderlineProperty */
+				case '<u':
+					if(!y.val) break;
+					/* falls through */
+				case '<u/>': font.u = 1; break;
+				case '</u>': break;
+
+				/* 18.8.2 b */
+				case '<b':
+					if(!y.val) break;
+					/* falls through */
+				case '<b/>': font.b = 1; break;
+				case '</b>': break;
+
+				/* 18.8.26 i */
+				case '<i':
+					if(!y.val) break;
+					/* falls through */
+				case '<i/>': font.i = 1; break;
+				case '</i>': break;
+
+				/* 18.3.1.15 color CT_Color TODO: tint, theme, auto, indexed */
+				case '<color':
+					if(y.rgb) font.color = y.rgb.substr(2,6);
+					break;
+
+				/* 18.8.18 family ST_FontFamily */
+				case '<family': font.family = y.val; break;
+
+				/* 18.4.14 vertAlign CT_VerticalAlignFontProperty TODO */
+				case '<vertAlign': break;
+
+				/* 18.8.35 scheme CT_FontScheme TODO */
+				case '<scheme': break;
+
+				default:
+					if(y[0].charCodeAt(1) !== 47) throw 'Unrecognized rich format ' + y[0];
+			}
+		}
+		/* TODO: These should be generated styles, not inline */
+		var style = [];
+		if(font.b) style.push("font-weight: bold;");
+		if(font.i) style.push("font-style: italic;");
+		intro.push('<span style="' + style.join("") + '">');
+		outro.push("</span>");
+		return cp;
+	};
+
+	/* 18.4.4 r CT_RElt */
+	function parse_r(r) {
+		var terms = [[],"",[]];
+		/* 18.4.12 t ST_Xstring */
+		var t = r.match(tregex), cp = 65001;
+		if(!isval(t)) return "";
+		terms[1] = t[1];
+
+		var rpr = r.match(rpregex);
+		if(isval(rpr)) cp = parse_rpr(rpr[1], terms[0], terms[2]);
+
+		return terms[0].join("") + terms[1].replace(nlregex,'<br/>') + terms[2].join("");
+	}
+	return function parse_rs(rs) {
+		return rs.replace(rregex,"").split(rend).map(parse_r).join("");
+	};
+})();
+
+/* 18.4.8 si CT_Rst */
+var sitregex = /<t[^>]*>([^<]*)<\/t>/g, sirregex = /<r>/;
+function parse_si(x, opts) {
+	var html = opts ? opts.cellHTML : true;
+	var z = {};
+	if(!x) return null;
+	var y;
+	/* 18.4.12 t ST_Xstring (Plaintext String) */
+	if(x.charCodeAt(1) === 116) {
+		z.t = utf8read(unescapexml(x.substr(x.indexOf(">")+1).split(/<\/t>/)[0]));
+		z.r = x;
+		if(html) z.h = z.t;
+	}
+	/* 18.4.4 r CT_RElt (Rich Text Run) */
+	else if((y = x.match(sirregex))) {
+		z.r = x;
+		z.t = utf8read(unescapexml(x.match(sitregex).join("").replace(tagregex,"")));
+		if(html) z.h = parse_rs(x);
+	}
+	/* 18.4.3 phoneticPr CT_PhoneticPr (TODO: needed for Asian support) */
+	/* 18.4.6 rPh CT_PhoneticRun (TODO: needed for Asian support) */
+	return z;
+}
+
+/* 18.4 Shared String Table */
+var sstr0 = /<sst([^>]*)>([\s\S]*)<\/sst>/;
+var sstr1 = /<(?:si|sstItem)>/g;
+var sstr2 = /<\/(?:si|sstItem)>/;
+function parse_sst_xml(data, opts) {
+	var s = [], ss;
+	/* 18.4.9 sst CT_Sst */
+	var sst = data.match(sstr0);
+	if(isval(sst)) {
+		ss = sst[2].replace(sstr1,"").split(sstr2);
+		for(var i = 0; i != ss.length; ++i) {
+			var o = parse_si(ss[i], opts);
+			if(o != null) s[s.length] = o;
+		}
+		sst = parsexmltag(sst[1]); s.Count = sst.count; s.Unique = sst.uniqueCount;
+	}
+	return s;
+}
+
+RELS.SST = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings";
+var straywsregex = /^\s|\s$|[\t\n\r]/;
+function write_sst_xml(sst, opts) {
+	if(!opts.bookSST) return "";
+	var o = [XML_HEADER];
+	o[o.length] = (writextag('sst', null, {
+		xmlns: XMLNS.main[0],
+		count: sst.Count,
+		uniqueCount: sst.Unique
+	}));
+	for(var i = 0; i != sst.length; ++i) { if(sst[i] == null) continue;
+		var s = sst[i];
+		var sitag = "<si>";
+		if(s.r) sitag += s.r;
+		else {
+			sitag += "<t";
+			if(s.t.match(straywsregex)) sitag += ' xml:space="preserve"';
+			sitag += ">" + escapexml(s.t) + "</t>";
+		}
+		sitag += "</si>";
+		o[o.length] = (sitag);
+	}
+	if(o.length>2){ o[o.length] = ('</sst>'); o[1]=o[1].replace("/>",">"); }
+	return o.join("");
+}
+/* [MS-XLSB] 2.4.219 BrtBeginSst */
+function parse_BrtBeginSst(data, length) {
+	return [data.read_shift(4), data.read_shift(4)];
+}
+
+/* [MS-XLSB] 2.1.7.45 Shared Strings */
+function parse_sst_bin(data, opts) {
+	var s = [];
+	var pass = false;
+	recordhopper(data, function hopper_sst(val, R, RT) {
+		switch(R.n) {
+			case 'BrtBeginSst': s.Count = val[0]; s.Unique = val[1]; break;
+			case 'BrtSSTItem': s.push(val); break;
+			case 'BrtEndSst': return true;
+			/* TODO: produce a test case with a future record */
+			case 'BrtFRTBegin': pass = true; break;
+			case 'BrtFRTEnd': pass = false; break;
+			default: if(!pass || opts.WTF) throw new Error("Unexpected record " + RT + " " + R.n);
+		}
+	});
+	return s;
+}
+
+function write_BrtBeginSst(sst, o) {
+	if(!o) o = new_buf(8);
+	o.write_shift(4, sst.Count);
+	o.write_shift(4, sst.Unique);
+	return o;
+}
+
+var write_BrtSSTItem = write_RichStr;
+
+function write_sst_bin(sst, opts) {
+	var ba = buf_array();
+	write_record(ba, "BrtBeginSst", write_BrtBeginSst(sst));
+	for(var i = 0; i < sst.length; ++i) write_record(ba, "BrtSSTItem", write_BrtSSTItem(sst[i]));
+	write_record(ba, "BrtEndSst");
+	return ba.end();
+}
+function hex2RGB(h) {
+	var o = h.substr(h[0]==="#"?1:0,6);
+	return [parseInt(o.substr(0,2),16),parseInt(o.substr(0,2),16),parseInt(o.substr(0,2),16)];
+}
+function rgb2Hex(rgb) {
+	for(var i=0,o=1; i!=3; ++i) o = o*256 + (rgb[i]>255?255:rgb[i]<0?0:rgb[i]);
+	return o.toString(16).toUpperCase().substr(1);
+}
+
+function rgb2HSL(rgb) {
+	var R = rgb[0]/255, G = rgb[1]/255, B=rgb[2]/255;
+	var M = Math.max(R, G, B), m = Math.min(R, G, B), C = M - m;
+	if(C === 0) return [0, 0, R];
+
+	var H6 = 0, S = 0, L2 = (M + m);
+	S = C / (L2 > 1 ? 2 - L2 : L2);
+	switch(M){
+		case R: H6 = ((G - B) / C + 6)%6; break;
+		case G: H6 = ((B - R) / C + 2); break;
+		case B: H6 = ((R - G) / C + 4); break;
+	}
+	return [H6 / 6, S, L2 / 2];
+}
+
+function hsl2RGB(hsl){
+	var H = hsl[0], S = hsl[1], L = hsl[2];
+	var C = S * 2 * (L < 0.5 ? L : 1 - L), m = L - C/2;
+	var rgb = [m,m,m], h6 = 6*H;
+
+	var X;
+	if(S !== 0) switch(h6|0) {
+		case 0: case 6: X = C * h6; rgb[0] += C; rgb[1] += X; break;
+		case 1: X = C * (2 - h6);   rgb[0] += X; rgb[1] += C; break;
+		case 2: X = C * (h6 - 2);   rgb[1] += C; rgb[2] += X; break;
+		case 3: X = C * (4 - h6);   rgb[1] += X; rgb[2] += C; break;
+		case 4: X = C * (h6 - 4);   rgb[2] += C; rgb[0] += X; break;
+		case 5: X = C * (6 - h6);   rgb[2] += X; rgb[0] += C; break;
+	}
+	for(var i = 0; i != 3; ++i) rgb[i] = Math.round(rgb[i]*255);
+	return rgb;
+}
+
+/* 18.8.3 bgColor tint algorithm */
+function rgb_tint(hex, tint) {
+	if(tint === 0) return hex;
+	var hsl = rgb2HSL(hex2RGB(hex));
+	if (tint < 0) hsl[2] = hsl[2] * (1 + tint);
+	else hsl[2] = 1 - (1 - hsl[2]) * (1 - tint);
+	return rgb2Hex(hsl2RGB(hsl));
+}
+
+/* 18.3.1.13 width calculations */
+var DEF_MDW = 7, MAX_MDW = 15, MIN_MDW = 1, MDW = DEF_MDW;
+function width2px(width) { return (( width + ((128/MDW)|0)/256 )* MDW )|0; }
+function px2char(px) { return (((px - 5)/MDW * 100 + 0.5)|0)/100; }
+function char2width(chr) { return (((chr * MDW + 5)/MDW*256)|0)/256; }
+function cycle_width(collw) { return char2width(px2char(width2px(collw))); }
+function find_mdw(collw, coll) {
+	if(cycle_width(collw) != collw) {
+		for(MDW=DEF_MDW; MDW>MIN_MDW; --MDW) if(cycle_width(collw) === collw) break;
+		if(MDW === MIN_MDW) for(MDW=DEF_MDW+1; MDW<MAX_MDW; ++MDW) if(cycle_width(collw) === collw) break;
+		if(MDW === MAX_MDW) MDW = DEF_MDW;
+	}
+}
+var styles = {}; // shared styles
+
+var themes = {}; // shared themes
+
+/* 18.8.21 fills CT_Fills */
+function parse_fills(t, opts) {
+	styles.Fills = [];
+	var fill = {};
+	t[0].match(tagregex).forEach(function(x) {
+		var y = parsexmltag(x);
+		switch(y[0]) {
+			case '<fills': case '<fills>': case '</fills>': break;
+
+			/* 18.8.20 fill CT_Fill */
+			case '<fill>': break;
+			case '</fill>': styles.Fills.push(fill); fill = {}; break;
+
+			/* 18.8.32 patternFill CT_PatternFill */
+			case '<patternFill':
+				if(y.patternType) fill.patternType = y.patternType;
+				break;
+			case '<patternFill/>': case '</patternFill>': break;
+
+			/* 18.8.3 bgColor CT_Color */
+			case '<bgColor':
+				if(!fill.bgColor) fill.bgColor = {};
+				if(y.indexed) fill.bgColor.indexed = parseInt(y.indexed, 10);
+				if(y.theme) fill.bgColor.theme = parseInt(y.theme, 10);
+				if(y.tint) fill.bgColor.tint = parseFloat(y.tint);
+				/* Excel uses ARGB strings */
+				if(y.rgb) fill.bgColor.rgb = y.rgb.substring(y.rgb.length - 6);
+				break;
+			case '<bgColor/>': case '</bgColor>': break;
+
+			/* 18.8.19 fgColor CT_Color */
+			case '<fgColor':
+				if(!fill.fgColor) fill.fgColor = {};
+				if(y.theme) fill.fgColor.theme = parseInt(y.theme, 10);
+				if(y.tint) fill.fgColor.tint = parseFloat(y.tint);
+				/* Excel uses ARGB strings */
+				if(y.rgb) fill.fgColor.rgb = y.rgb.substring(y.rgb.length - 6);
+				break;
+			case '<bgColor/>': case '</fgColor>': break;
+
+			default: if(opts.WTF) throw 'unrecognized ' + y[0] + ' in fills';
+		}
+	});
+}
+
+/* 18.8.31 numFmts CT_NumFmts */
+function parse_numFmts(t, opts) {
+	styles.NumberFmt = [];
+	var k = keys(SSF._table);
+	for(var i=0; i < k.length; ++i) styles.NumberFmt[k[i]] = SSF._table[k[i]];
+	var m = t[0].match(tagregex);
+	for(i=0; i < m.length; ++i) {
+		var y = parsexmltag(m[i]);
+		switch(y[0]) {
+			case '<numFmts': case '</numFmts>': case '<numFmts/>': case '<numFmts>': break;
+			case '<numFmt': {
+				var f=unescapexml(utf8read(y.formatCode)), j=parseInt(y.numFmtId,10);
+				styles.NumberFmt[j] = f; if(j>0) SSF.load(f,j);
+			} break;
+			default: if(opts.WTF) throw 'unrecognized ' + y[0] + ' in numFmts';
+		}
+	}
+}
+
+function write_numFmts(NF, opts) {
+	var o = ["<numFmts>"];
+	[[5,8],[23,26],[41,44],[63,66],[164,392]].forEach(function(r) {
+		for(var i = r[0]; i <= r[1]; ++i) if(NF[i] !== undefined) o[o.length] = (writextag('numFmt',null,{numFmtId:i,formatCode:escapexml(NF[i])}));
+	});
+	if(o.length === 1) return "";
+	o[o.length] = ("</numFmts>");
+	o[0] = writextag('numFmts', null, { count:o.length-2 }).replace("/>", ">");
+	return o.join("");
+}
+
+/* 18.8.10 cellXfs CT_CellXfs */
+function parse_cellXfs(t, opts) {
+	styles.CellXf = [];
+	t[0].match(tagregex).forEach(function(x) {
+		var y = parsexmltag(x);
+		switch(y[0]) {
+			case '<cellXfs': case '<cellXfs>': case '<cellXfs/>': case '</cellXfs>': break;
+
+			/* 18.8.45 xf CT_Xf */
+			case '<xf': delete y[0];
+				if(y.numFmtId) y.numFmtId = parseInt(y.numFmtId, 10);
+				if(y.fillId) y.fillId = parseInt(y.fillId, 10);
+				styles.CellXf.push(y); break;
+			case '</xf>': break;
+
+			/* 18.8.1 alignment CT_CellAlignment */
+			case '<alignment': case '<alignment/>': break;
+
+			/* 18.8.33 protection CT_CellProtection */
+			case '<protection': case '</protection>': case '<protection/>': break;
+
+			case '<extLst': case '</extLst>': break;
+			case '<ext': break;
+			default: if(opts.WTF) throw 'unrecognized ' + y[0] + ' in cellXfs';
+		}
+	});
+}
+
+function write_cellXfs(cellXfs) {
+	var o = [];
+	o[o.length] = (writextag('cellXfs',null));
+	cellXfs.forEach(function(c) { o[o.length] = (writextag('xf', null, c)); });
+	o[o.length] = ("</cellXfs>");
+	if(o.length === 2) return "";
+	o[0] = writextag('cellXfs',null, {count:o.length-2}).replace("/>",">");
+	return o.join("");
+}
+
+/* 18.8 Styles CT_Stylesheet*/
+var parse_sty_xml= (function make_pstyx() {
+var numFmtRegex = /<numFmts([^>]*)>.*<\/numFmts>/;
+var cellXfRegex = /<cellXfs([^>]*)>.*<\/cellXfs>/;
+var fillsRegex = /<fills([^>]*)>.*<\/fills>/;
+
+return function parse_sty_xml(data, opts) {
+	/* 18.8.39 styleSheet CT_Stylesheet */
+	var t;
+
+	/* numFmts CT_NumFmts ? */
+	if((t=data.match(numFmtRegex))) parse_numFmts(t, opts);
+
+	/* fonts CT_Fonts ? */
+//	if((t=data.match(/<fonts([^>]*)>.*<\/fonts>/))) parse_fonts(t, opts);
+
+	/* fills CT_Fills */
+	if((t=data.match(fillsRegex))) parse_fills(t, opts);
+
+	/* borders CT_Borders ? */
+	/* cellStyleXfs CT_CellStyleXfs ? */
+
+	/* cellXfs CT_CellXfs ? */
+	if((t=data.match(cellXfRegex))) parse_cellXfs(t, opts);
+
+	/* dxfs CT_Dxfs ? */
+	/* tableStyles CT_TableStyles ? */
+	/* colors CT_Colors ? */
+	/* extLst CT_ExtensionList ? */
+
+	return styles;
+};
 })();
 
-// enforcing Stuk's coding style
-// vim: set shiftwidth=3 softtabstop=3:
-/*
- * Port of a script by Masanao Izumo.
- *
- * Only changes : wrap all the variables in a function and add the 
- * main function to JSZip (DEFLATE compression method).
- * Everything else was written by M. Izumo.
- *
- * Original code can be found here: http://www.onicos.com/staff/iz/amuse/javascript/expert/inflate.txt
- */
-
-if(!JSZip) {
-   throw "JSZip not defined";
-}
-
-/*
- * Original:
- *   http://www.onicos.com/staff/iz/amuse/javascript/expert/inflate.txt
- */
-
-(function(){
-  // the original implementation leaks a global variable.
-  // Defining the variable here doesn't break anything.
-  var zip_fixed_bd;
-
-/* Copyright (C) 1999 Masanao Izumo <iz@onicos.co.jp>
- * Version: 1.0.0.1
- * LastModified: Dec 25 1999
- */
-
-/* Interface:
- * data = zip_inflate(src);
- */
-
-/* constant parameters */
-var zip_WSIZE = 32768;		// Sliding Window size
-var zip_STORED_BLOCK = 0;
-var zip_STATIC_TREES = 1;
-var zip_DYN_TREES    = 2;
-
-/* for inflate */
-var zip_lbits = 9; 		// bits in base literal/length lookup table
-var zip_dbits = 6; 		// bits in base distance lookup table
-var zip_INBUFSIZ = 32768;	// Input buffer size
-var zip_INBUF_EXTRA = 64;	// Extra buffer
-
-/* variables (inflate) */
-var zip_slide;
-var zip_wp;			// current position in slide
-var zip_fixed_tl = null;	// inflate static
-var zip_fixed_td;		// inflate static
-var zip_fixed_bl, fixed_bd;	// inflate static
-var zip_bit_buf;		// bit buffer
-var zip_bit_len;		// bits in bit buffer
-var zip_method;
-var zip_eof;
-var zip_copy_leng;
-var zip_copy_dist;
-var zip_tl, zip_td;	// literal/length and distance decoder tables
-var zip_bl, zip_bd;	// number of bits decoded by tl and td
-
-var zip_inflate_data;
-var zip_inflate_pos;
-
-
-/* constant tables (inflate) */
-var zip_MASK_BITS = new Array(
-    0x0000,
-    0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
-    0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff);
-// Tables for deflate from PKZIP's appnote.txt.
-var zip_cplens = new Array( // Copy lengths for literal codes 257..285
-    3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
-    35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0);
-/* note: see note #13 above about the 258 in this list. */
-var zip_cplext = new Array( // Extra bits for literal codes 257..285
-    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, 99, 99); // 99==invalid
-var zip_cpdist = new Array( // Copy offsets for distance codes 0..29
-    1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
-    257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
-    8193, 12289, 16385, 24577);
-var zip_cpdext = new Array( // Extra bits for distance codes
-    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);
-var zip_border = new Array(  // Order of the bit length code lengths
-    16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15);
-/* objects (inflate) */
-
-function zip_HuftList() {
-    this.next = null;
-    this.list = null;
-}
-
-function zip_HuftNode() {
-    this.e = 0; // number of extra bits or operation
-    this.b = 0; // number of bits in this code or subcode
-
-    // union
-    this.n = 0; // literal, length base, or distance base
-    this.t = null; // (zip_HuftNode) pointer to next level of table
-}
-
-function zip_HuftBuild(b,	// code lengths in bits (all assumed <= BMAX)
-		       n,	// number of codes (assumed <= N_MAX)
-		       s,	// number of simple-valued codes (0..s-1)
-		       d,	// list of base values for non-simple codes
-		       e,	// list of extra bits for non-simple codes
-		       mm	// maximum lookup bits
-		   ) {
-    this.BMAX = 16;   // maximum bit length of any code
-    this.N_MAX = 288; // maximum number of codes in any set
-    this.status = 0;	// 0: success, 1: incomplete table, 2: bad input
-    this.root = null;	// (zip_HuftList) starting table
-    this.m = 0;		// maximum lookup bits, returns actual
-
-/* Given a list of code lengths and a maximum table size, make a set of
-   tables to decode that set of codes.	Return zero on success, one if
-   the given code set is incomplete (the tables are still built in this
-   case), two if the input is invalid (all zero length codes or an
-   oversubscribed set of lengths), and three if not enough memory.
-   The code with value 256 is special, and the tables are constructed
-   so that no bits beyond that code are fetched when that code is
-   decoded. */
-    {
-	var a;			// counter for codes of length k
-	var c = new Array(this.BMAX+1);	// bit length count table
-	var el;			// length of EOB code (value 256)
-	var f;			// i repeats in table every f entries
-	var g;			// maximum code length
-	var h;			// table level
-	var i;			// counter, current code
-	var j;			// counter
-	var k;			// number of bits in current code
-	var lx = new Array(this.BMAX+1);	// stack of bits per table
-	var p;			// pointer into c[], b[], or v[]
-	var pidx;		// index of p
-	var q;			// (zip_HuftNode) points to current table
-	var r = new zip_HuftNode(); // table entry for structure assignment
-	var u = new Array(this.BMAX); // zip_HuftNode[BMAX][]  table stack
-	var v = new Array(this.N_MAX); // values in order of bit length
-	var w;
-	var x = new Array(this.BMAX+1);// bit offsets, then code stack
-	var xp;			// pointer into x or c
-	var y;			// number of dummy codes added
-	var z;			// number of entries in current table
-	var o;
-	var tail;		// (zip_HuftList)
-
-	tail = this.root = null;
-	for(i = 0; i < c.length; i++)
-	    c[i] = 0;
-	for(i = 0; i < lx.length; i++)
-	    lx[i] = 0;
-	for(i = 0; i < u.length; i++)
-	    u[i] = null;
-	for(i = 0; i < v.length; i++)
-	    v[i] = 0;
-	for(i = 0; i < x.length; i++)
-	    x[i] = 0;
-
-	// Generate counts for each bit length
-	el = n > 256 ? b[256] : this.BMAX; // set length of EOB code, if any
-	p = b; pidx = 0;
-	i = n;
-	do {
-	    c[p[pidx]]++;	// assume all entries <= BMAX
-	    pidx++;
-	} while(--i > 0);
-	if(c[0] == n) {	// null input--all zero length codes
-	    this.root = null;
-	    this.m = 0;
-	    this.status = 0;
-	    return;
-	}
-
-	// Find minimum and maximum length, bound *m by those
-	for(j = 1; j <= this.BMAX; j++)
-	    if(c[j] != 0)
-		break;
-	k = j;			// minimum code length
-	if(mm < j)
-	    mm = j;
-	for(i = this.BMAX; i != 0; i--)
-	    if(c[i] != 0)
-		break;
-	g = i;			// maximum code length
-	if(mm > i)
-	    mm = i;
-
-	// Adjust last length count to fill out codes, if needed
-	for(y = 1 << j; j < i; j++, y <<= 1)
-	    if((y -= c[j]) < 0) {
-		this.status = 2;	// bad input: more codes than bits
-		this.m = mm;
-		return;
-	    }
-	if((y -= c[i]) < 0) {
-	    this.status = 2;
-	    this.m = mm;
-	    return;
-	}
-	c[i] += y;
-
-	// Generate starting offsets into the value table for each length
-	x[1] = j = 0;
-	p = c;
-	pidx = 1;
-	xp = 2;
-	while(--i > 0)		// note that i == g from above
-	    x[xp++] = (j += p[pidx++]);
-
-	// Make a table of values in order of bit lengths
-	p = b; pidx = 0;
-	i = 0;
-	do {
-	    if((j = p[pidx++]) != 0)
-		v[x[j]++] = i;
-	} while(++i < n);
-	n = x[g];			// set n to length of v
-
-	// Generate the Huffman codes and for each, make the table entries
-	x[0] = i = 0;		// first Huffman code is zero
-	p = v; pidx = 0;		// grab values in bit order
-	h = -1;			// no tables yet--level -1
-	w = lx[0] = 0;		// no bits decoded yet
-	q = null;			// ditto
-	z = 0;			// ditto
-
-	// go through the bit lengths (k already is bits in shortest code)
-	for(; k <= g; k++) {
-	    a = c[k];
-	    while(a-- > 0) {
-		// here i is the Huffman code of length k bits for value p[pidx]
-		// make tables up to required level
-		while(k > w + lx[1 + h]) {
-		    w += lx[1 + h]; // add bits already decoded
-		    h++;
-
-		    // compute minimum size table less than or equal to *m bits
-		    z = (z = g - w) > mm ? mm : z; // upper limit
-		    if((f = 1 << (j = k - w)) > a + 1) { // try a k-w bit table
-			// too few codes for k-w bit table
-			f -= a + 1;	// deduct codes from patterns left
-			xp = k;
-			while(++j < z) { // try smaller tables up to z bits
-			    if((f <<= 1) <= c[++xp])
-				break;	// enough codes to use up j bits
-			    f -= c[xp];	// else deduct codes from patterns
-			}
-		    }
-		    if(w + j > el && w < el)
-			j = el - w;	// make EOB code end at table
-		    z = 1 << j;	// table entries for j-bit table
-		    lx[1 + h] = j; // set table size in stack
-
-		    // allocate and link in new table
-		    q = new Array(z);
-		    for(o = 0; o < z; o++) {
-			q[o] = new zip_HuftNode();
-		    }
-
-		    if(tail == null)
-			tail = this.root = new zip_HuftList();
-		    else
-			tail = tail.next = new zip_HuftList();
-		    tail.next = null;
-		    tail.list = q;
-		    u[h] = q;	// table starts after link
-
-		    /* connect to last table, if there is one */
-		    if(h > 0) {
-			x[h] = i;		// save pattern for backing up
-			r.b = lx[h];	// bits to dump before this table
-			r.e = 16 + j;	// bits in this table
-			r.t = q;		// pointer to this table
-			j = (i & ((1 << w) - 1)) >> (w - lx[h]);
-			u[h-1][j].e = r.e;
-			u[h-1][j].b = r.b;
-			u[h-1][j].n = r.n;
-			u[h-1][j].t = r.t;
-		    }
-		}
-
-		// set up table entry in r
-		r.b = k - w;
-		if(pidx >= n)
-		    r.e = 99;		// out of values--invalid code
-		else if(p[pidx] < s) {
-		    r.e = (p[pidx] < 256 ? 16 : 15); // 256 is end-of-block code
-		    r.n = p[pidx++];	// simple code is just the value
-		} else {
-		    r.e = e[p[pidx] - s];	// non-simple--look up in lists
-		    r.n = d[p[pidx++] - s];
-		}
-
-		// fill code-like entries with r //
-		f = 1 << (k - w);
-		for(j = i >> w; j < z; j += f) {
-		    q[j].e = r.e;
-		    q[j].b = r.b;
-		    q[j].n = r.n;
-		    q[j].t = r.t;
-		}
-
-		// backwards increment the k-bit code i
-		for(j = 1 << (k - 1); (i & j) != 0; j >>= 1)
-		    i ^= j;
-		i ^= j;
-
-		// backup over finished tables
-		while((i & ((1 << w) - 1)) != x[h]) {
-		    w -= lx[h];		// don't need to update q
-		    h--;
-		}
-	    }
-	}
-
-	/* return actual size of base table */
-	this.m = lx[1];
-
-	/* Return true (1) if we were given an incomplete table */
-	this.status = ((y != 0 && g != 1) ? 1 : 0);
-    } /* end of constructor */
-}
-
-
-/* routines (inflate) */
-
-function zip_GET_BYTE() {
-    if(zip_inflate_data.length == zip_inflate_pos)
-	return -1;
-    return zip_inflate_data.charCodeAt(zip_inflate_pos++) & 0xff;
-}
-
-function zip_NEEDBITS(n) {
-    while(zip_bit_len < n) {
-	zip_bit_buf |= zip_GET_BYTE() << zip_bit_len;
-	zip_bit_len += 8;
-    }
-}
-
-function zip_GETBITS(n) {
-    return zip_bit_buf & zip_MASK_BITS[n];
-}
-
-function zip_DUMPBITS(n) {
-    zip_bit_buf >>= n;
-    zip_bit_len -= n;
-}
-
-function zip_inflate_codes(buff, off, size) {
-    /* inflate (decompress) the codes in a deflated (compressed) block.
-       Return an error code or zero if it all goes ok. */
-    var e;		// table entry flag/number of extra bits
-    var t;		// (zip_HuftNode) pointer to table entry
-    var n;
-
-    if(size == 0)
-      return 0;
-
-    // inflate the coded data
-    n = 0;
-    for(;;) {			// do until end of block
-	zip_NEEDBITS(zip_bl);
-	t = zip_tl.list[zip_GETBITS(zip_bl)];
-	e = t.e;
-	while(e > 16) {
-	    if(e == 99)
-		return -1;
-	    zip_DUMPBITS(t.b);
-	    e -= 16;
-	    zip_NEEDBITS(e);
-	    t = t.t[zip_GETBITS(e)];
-	    e = t.e;
-	}
-	zip_DUMPBITS(t.b);
-
-	if(e == 16) {		// then it's a literal
-	    zip_wp &= zip_WSIZE - 1;
-	    buff[off + n++] = zip_slide[zip_wp++] = t.n;
-	    if(n == size)
-		return size;
-	    continue;
-	}
-
-	// exit if end of block
-	if(e == 15)
-	    break;
-
-	// it's an EOB or a length
-
-	// get length of block to copy
-	zip_NEEDBITS(e);
-	zip_copy_leng = t.n + zip_GETBITS(e);
-	zip_DUMPBITS(e);
-
-	// decode distance of block to copy
-	zip_NEEDBITS(zip_bd);
-	t = zip_td.list[zip_GETBITS(zip_bd)];
-	e = t.e;
-
-	while(e > 16) {
-	    if(e == 99)
-		return -1;
-	    zip_DUMPBITS(t.b);
-	    e -= 16;
-	    zip_NEEDBITS(e);
-	    t = t.t[zip_GETBITS(e)];
-	    e = t.e;
-	}
-	zip_DUMPBITS(t.b);
-	zip_NEEDBITS(e);
-	zip_copy_dist = zip_wp - t.n - zip_GETBITS(e);
-	zip_DUMPBITS(e);
-
-	// do the copy
-	while(zip_copy_leng > 0 && n < size) {
-	    zip_copy_leng--;
-	    zip_copy_dist &= zip_WSIZE - 1;
-	    zip_wp &= zip_WSIZE - 1;
-	    buff[off + n++] = zip_slide[zip_wp++]
-		= zip_slide[zip_copy_dist++];
-	}
-
-	if(n == size)
-	    return size;
-    }
-
-    zip_method = -1; // done
-    return n;
-}
-
-function zip_inflate_stored(buff, off, size) {
-    /* "decompress" an inflated type 0 (stored) block. */
-    var n;
-
-    // go to byte boundary
-    n = zip_bit_len & 7;
-    zip_DUMPBITS(n);
-
-    // get the length and its complement
-    zip_NEEDBITS(16);
-    n = zip_GETBITS(16);
-    zip_DUMPBITS(16);
-    zip_NEEDBITS(16);
-    if(n != ((~zip_bit_buf) & 0xffff))
-	return -1;			// error in compressed data
-    zip_DUMPBITS(16);
-
-    // read and output the compressed data
-    zip_copy_leng = n;
-
-    n = 0;
-    while(zip_copy_leng > 0 && n < size) {
-	zip_copy_leng--;
-	zip_wp &= zip_WSIZE - 1;
-	zip_NEEDBITS(8);
-	buff[off + n++] = zip_slide[zip_wp++] =
-	    zip_GETBITS(8);
-	zip_DUMPBITS(8);
-    }
-
-    if(zip_copy_leng == 0)
-      zip_method = -1; // done
-    return n;
-}
-
-function zip_inflate_fixed(buff, off, size) {
-    /* decompress an inflated type 1 (fixed Huffman codes) block.  We should
-       either replace this with a custom decoder, or at least precompute the
-       Huffman tables. */
-
-    // if first time, set up tables for fixed blocks
-    if(zip_fixed_tl == null) {
-	var i;			// temporary variable
-	var l = new Array(288);	// length list for huft_build
-	var h;	// zip_HuftBuild
-
-	// literal table
-	for(i = 0; i < 144; i++)
-	    l[i] = 8;
-	for(; i < 256; i++)
-	    l[i] = 9;
-	for(; i < 280; i++)
-	    l[i] = 7;
-	for(; i < 288; i++)	// make a complete, but wrong code set
-	    l[i] = 8;
-	zip_fixed_bl = 7;
-
-	h = new zip_HuftBuild(l, 288, 257, zip_cplens, zip_cplext,
-			      zip_fixed_bl);
-	if(h.status != 0) {
-	    alert("HufBuild error: "+h.status);
-	    return -1;
-	}
-	zip_fixed_tl = h.root;
-	zip_fixed_bl = h.m;
-
-	// distance table
-	for(i = 0; i < 30; i++)	// make an incomplete code set
-	    l[i] = 5;
-	zip_fixed_bd = 5;
-
-	h = new zip_HuftBuild(l, 30, 0, zip_cpdist, zip_cpdext, zip_fixed_bd);
-	if(h.status > 1) {
-	    zip_fixed_tl = null;
-	    alert("HufBuild error: "+h.status);
-	    return -1;
-	}
-	zip_fixed_td = h.root;
-	zip_fixed_bd = h.m;
-    }
-
-    zip_tl = zip_fixed_tl;
-    zip_td = zip_fixed_td;
-    zip_bl = zip_fixed_bl;
-    zip_bd = zip_fixed_bd;
-    return zip_inflate_codes(buff, off, size);
-}
-
-function zip_inflate_dynamic(buff, off, size) {
-    // decompress an inflated type 2 (dynamic Huffman codes) block.
-    var i;		// temporary variables
-    var j;
-    var l;		// last length
-    var n;		// number of lengths to get
-    var t;		// (zip_HuftNode) literal/length code table
-    var nb;		// number of bit length codes
-    var nl;		// number of literal/length codes
-    var nd;		// number of distance codes
-    var ll = new Array(286+30); // literal/length and distance code lengths
-    var h;		// (zip_HuftBuild)
-
-    for(i = 0; i < ll.length; i++)
-	ll[i] = 0;
-
-    // read in table lengths
-    zip_NEEDBITS(5);
-    nl = 257 + zip_GETBITS(5);	// number of literal/length codes
-    zip_DUMPBITS(5);
-    zip_NEEDBITS(5);
-    nd = 1 + zip_GETBITS(5);	// number of distance codes
-    zip_DUMPBITS(5);
-    zip_NEEDBITS(4);
-    nb = 4 + zip_GETBITS(4);	// number of bit length codes
-    zip_DUMPBITS(4);
-    if(nl > 286 || nd > 30)
-      return -1;		// bad lengths
-
-    // read in bit-length-code lengths
-    for(j = 0; j < nb; j++)
-    {
-	zip_NEEDBITS(3);
-	ll[zip_border[j]] = zip_GETBITS(3);
-	zip_DUMPBITS(3);
-    }
-    for(; j < 19; j++)
-	ll[zip_border[j]] = 0;
-
-    // build decoding table for trees--single level, 7 bit lookup
-    zip_bl = 7;
-    h = new zip_HuftBuild(ll, 19, 19, null, null, zip_bl);
-    if(h.status != 0)
-	return -1;	// incomplete code set
-
-    zip_tl = h.root;
-    zip_bl = h.m;
-
-    // read in literal and distance code lengths
-    n = nl + nd;
-    i = l = 0;
-    while(i < n) {
-	zip_NEEDBITS(zip_bl);
-	t = zip_tl.list[zip_GETBITS(zip_bl)];
-	j = t.b;
-	zip_DUMPBITS(j);
-	j = t.n;
-	if(j < 16)		// length of code in bits (0..15)
-	    ll[i++] = l = j;	// save last length in l
-	else if(j == 16) {	// repeat last length 3 to 6 times
-	    zip_NEEDBITS(2);
-	    j = 3 + zip_GETBITS(2);
-	    zip_DUMPBITS(2);
-	    if(i + j > n)
-		return -1;
-	    while(j-- > 0)
-		ll[i++] = l;
-	} else if(j == 17) {	// 3 to 10 zero length codes
-	    zip_NEEDBITS(3);
-	    j = 3 + zip_GETBITS(3);
-	    zip_DUMPBITS(3);
-	    if(i + j > n)
-		return -1;
-	    while(j-- > 0)
-		ll[i++] = 0;
-	    l = 0;
-	} else {		// j == 18: 11 to 138 zero length codes
-	    zip_NEEDBITS(7);
-	    j = 11 + zip_GETBITS(7);
-	    zip_DUMPBITS(7);
-	    if(i + j > n)
-		return -1;
-	    while(j-- > 0)
-		ll[i++] = 0;
-	    l = 0;
-	}
-    }
-
-    // build the decoding tables for literal/length and distance codes
-    zip_bl = zip_lbits;
-    h = new zip_HuftBuild(ll, nl, 257, zip_cplens, zip_cplext, zip_bl);
-    if(zip_bl == 0)	// no literals or lengths
-	h.status = 1;
-    if(h.status != 0) {
-	if(h.status == 1)
-	    ;// **incomplete literal tree**
-	return -1;		// incomplete code set
-    }
-    zip_tl = h.root;
-    zip_bl = h.m;
-
-    for(i = 0; i < nd; i++)
-	ll[i] = ll[i + nl];
-    zip_bd = zip_dbits;
-    h = new zip_HuftBuild(ll, nd, 0, zip_cpdist, zip_cpdext, zip_bd);
-    zip_td = h.root;
-    zip_bd = h.m;
-
-    if(zip_bd == 0 && nl > 257) {   // lengths but no distances
-	// **incomplete distance tree**
-	return -1;
-    }
-
-    if(h.status == 1) {
-	;// **incomplete distance tree**
-    }
-    if(h.status != 0)
-	return -1;
-
-    // decompress until an end-of-block code
-    return zip_inflate_codes(buff, off, size);
-}
-
-function zip_inflate_start() {
-    var i;
-
-    if(zip_slide == null)
-	zip_slide = new Array(2 * zip_WSIZE);
-    zip_wp = 0;
-    zip_bit_buf = 0;
-    zip_bit_len = 0;
-    zip_method = -1;
-    zip_eof = false;
-    zip_copy_leng = zip_copy_dist = 0;
-    zip_tl = null;
-}
-
-function zip_inflate_internal(buff, off, size) {
-    // decompress an inflated entry
-    var n, i;
-
-    n = 0;
-    while(n < size) {
-	if(zip_eof && zip_method == -1)
-	    return n;
-
-	if(zip_copy_leng > 0) {
-	    if(zip_method != zip_STORED_BLOCK) {
-		// STATIC_TREES or DYN_TREES
-		while(zip_copy_leng > 0 && n < size) {
-		    zip_copy_leng--;
-		    zip_copy_dist &= zip_WSIZE - 1;
-		    zip_wp &= zip_WSIZE - 1;
-		    buff[off + n++] = zip_slide[zip_wp++] =
-			zip_slide[zip_copy_dist++];
-		}
-	    } else {
-		while(zip_copy_leng > 0 && n < size) {
-		    zip_copy_leng--;
-		    zip_wp &= zip_WSIZE - 1;
-		    zip_NEEDBITS(8);
-		    buff[off + n++] = zip_slide[zip_wp++] = zip_GETBITS(8);
-		    zip_DUMPBITS(8);
-		}
-		if(zip_copy_leng == 0)
-		    zip_method = -1; // done
-	    }
-	    if(n == size)
-		return n;
-	}
-
-	if(zip_method == -1) {
-	    if(zip_eof)
-		break;
-
-	    // read in last block bit
-	    zip_NEEDBITS(1);
-	    if(zip_GETBITS(1) != 0)
-		zip_eof = true;
-	    zip_DUMPBITS(1);
-
-	    // read in block type
-	    zip_NEEDBITS(2);
-	    zip_method = zip_GETBITS(2);
-	    zip_DUMPBITS(2);
-	    zip_tl = null;
-	    zip_copy_leng = 0;
-	}
-
-	switch(zip_method) {
-	  case 0: // zip_STORED_BLOCK
-	    i = zip_inflate_stored(buff, off + n, size - n);
-	    break;
-
-	  case 1: // zip_STATIC_TREES
-	    if(zip_tl != null)
-		i = zip_inflate_codes(buff, off + n, size - n);
-	    else
-		i = zip_inflate_fixed(buff, off + n, size - n);
-	    break;
-
-	  case 2: // zip_DYN_TREES
-	    if(zip_tl != null)
-		i = zip_inflate_codes(buff, off + n, size - n);
-	    else
-		i = zip_inflate_dynamic(buff, off + n, size - n);
-	    break;
-
-	  default: // error
-	    i = -1;
-	    break;
-	}
-
-	if(i == -1) {
-	    if(zip_eof)
-		return 0;
-	    return -1;
-	}
-	n += i;
-    }
-    return n;
-}
-
-function zip_inflate(str) {
-    var out, buff;
-    var i, j;
-
-    zip_inflate_start();
-    zip_inflate_data = str;
-    zip_inflate_pos = 0;
-
-    buff = new Array(1024);
-    out = "";
-    while((i = zip_inflate_internal(buff, 0, buff.length)) > 0) {
-	for(j = 0; j < i; j++)
-	    out += String.fromCharCode(buff[j]);
-    }
-    zip_inflate_data = null; // G.C.
-    return out;
-}
-
-//
-// end of the script of Masanao Izumo.
-//
-
-// we add the compression method for JSZip
-if(!JSZip.compressions["DEFLATE"]) {
-  JSZip.compressions["DEFLATE"] = {
-    magic : "\x08\x00",
-    uncompress : zip_inflate
-  }
-} else {
-  JSZip.compressions["DEFLATE"].uncompress = zip_inflate;
-}
-
-})();
-
-// enforcing Stuk's coding style
-// vim: set shiftwidth=3 softtabstop=3:
-/**
-
-JSZip - A Javascript class for generating and reading zip files
-<http://stuartk.com/jszip>
-
-(c) 2011 David Duponchel <d.duponchel@gmail.com>
-Dual licenced under the MIT license or GPLv3. See LICENSE.markdown.
-
-**/
-/*global JSZip,JSZipBase64 */
-(function () {
-
-   var MAX_VALUE_16BITS = 65535;
-   var MAX_VALUE_32BITS = -1; // well, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" is parsed as -1
-
-   /**
-    * Prettify a string read as binary.
-    * @param {string} str the string to prettify.
-    * @return {string} a pretty string.
-    */
-   var pretty = function (str) {
-      var res = '', code, i;
-      for (i = 0; i < (str||"").length; i++) {
-         code = str.charCodeAt(i);
-         res += '\\x' + (code < 16 ? "0" : "") + code.toString(16).toUpperCase();
-      }
-      return res;
-   };
-
-   /**
-    * Find a compression registered in JSZip.
-    * @param {string} compressionMethod the method magic to find.
-    * @return {Object|null} the JSZip compression object, null if none found.
-    */
-   var findCompression = function (compressionMethod) {
-      for (var method in JSZip.compressions) {
-         if( !JSZip.compressions.hasOwnProperty(method) ) { continue; }
-         if (JSZip.compressions[method].magic === compressionMethod) {
-            return JSZip.compressions[method];
-         }
-      }
-      return null;
-   };
-
-   // class StreamReader {{{
-   /**
-    * Read bytes from a stream.
-    * Developer tip : when debugging, a watch on pretty(this.reader.stream.slice(this.reader.index))
-    * is very useful :)
-    * @constructor
-    * @param {String|ArrayBuffer|Uint8Array} stream the stream to read.
-    */
-   function StreamReader(stream) {
-      this.stream = "";
-      if (JSZip.support.uint8array && stream instanceof Uint8Array) {
-         this.stream = JSZip.utils.uint8Array2String(stream);
-      } else if (JSZip.support.arraybuffer && stream instanceof ArrayBuffer) {
-         var bufferView = new Uint8Array(stream);
-         this.stream = JSZip.utils.uint8Array2String(bufferView);
-      } else {
-         this.stream = JSZip.utils.string2binary(stream);
-      }
-      this.index = 0;
-   }
-   StreamReader.prototype = {
-      /**
-       * Check that the offset will not go too far.
-       * @param {string} offset the additional offset to check.
-       * @throws {Error} an Error if the offset is out of bounds.
-       */
-      checkOffset : function (offset) {
-         this.checkIndex(this.index + offset);
-      },
-      /**
-       * Check that the specifed index will not be too far.
-       * @param {string} newIndex the index to check.
-       * @throws {Error} an Error if the index is out of bounds.
-       */
-      checkIndex : function (newIndex) {
-         if (this.stream.length < newIndex || newIndex < 0) {
-            throw new Error("End of stream reached (stream length = " +
-                            this.stream.length + ", asked index = " +
-                            (newIndex) + "). Corrupted zip ?");
-         }
-      },
-      /**
-       * Change the index.
-       * @param {number} newIndex The new index.
-       * @throws {Error} if the new index is out of the stream.
-       */
-      setIndex : function (newIndex) {
-         this.checkIndex(newIndex);
-         this.index = newIndex;
-      },
-      /**
-       * Skip the next n bytes.
-       * @param {number} n the number of bytes to skip.
-       * @throws {Error} if the new index is out of the stream.
-       */
-      skip : function (n) {
-         this.setIndex(this.index + n);
-      },
-      /**
-       * Get the byte at the specified index.
-       * @param {number} i the index to use.
-       * @return {number} a byte.
-       */
-      byteAt : function(i) {
-         return this.stream.charCodeAt(i);
-      },
-      /**
-       * Get the next number with a given byte size.
-       * @param {number} size the number of bytes to read.
-       * @return {number} the corresponding number.
-       */
-      readInt : function (size) {
-         var result = 0, i;
-         this.checkOffset(size);
-         for(i = this.index + size - 1; i >= this.index; i--) {
-            result = (result << 8) + this.byteAt(i);
-         }
-         this.index += size;
-         return result;
-      },
-      /**
-       * Get the next string with a given byte size.
-       * @param {number} size the number of bytes to read.
-       * @return {string} the corresponding string.
-       */
-      readString : function (size) {
-         this.checkOffset(size);
-         // this will work because the constructor applied the "& 0xff" mask.
-         var result = this.stream.slice(this.index, this.index + size);
-         this.index += size;
-         return result;
-      },
-      /**
-       * Get the next date.
-       * @return {Date} the date.
-       */
-      readDate : function () {
-         var dostime = this.readInt(4);
-         return new Date(
-            ((dostime >> 25) & 0x7f) + 1980, // year
-            ((dostime >> 21) & 0x0f) - 1, // month
-            (dostime >> 16) & 0x1f, // day
-            (dostime >> 11) & 0x1f, // hour
-            (dostime >> 5) & 0x3f, // minute
-            (dostime & 0x1f) << 1); // second
-      }
-   };
-   // }}} end of StreamReader
-
-   // class ZipEntry {{{
-   /**
-    * An entry in the zip file.
-    * @constructor
-    * @param {Object} options Options of the current file.
-    * @param {Object} loadOptions Options for loading the stream.
-    */
-   function ZipEntry(options, loadOptions) {
-      this.options = options;
-      this.loadOptions = loadOptions;
-   }
-   ZipEntry.prototype = {
-      /**
-       * say if the file is encrypted.
-       * @return {boolean} true if the file is encrypted, false otherwise.
-       */
-      isEncrypted : function () {
-         // bit 1 is set
-         return (this.bitFlag & 0x0001) === 0x0001;
-      },
-      /**
-       * say if the file has utf-8 filename/comment.
-       * @return {boolean} true if the filename/comment is in utf-8, false otherwise.
-       */
-      useUTF8 : function () {
-         // bit 11 is set
-         return (this.bitFlag & 0x0800) === 0x0800;
-      },
-      /**
-       * Read the local part of a zip file and add the info in this object.
-       * @param {StreamReader} reader the reader to use.
-       */
-      readLocalPart : function(reader) {
-         var compression, localExtraFieldsLength;
-
-         // we already know everything from the central dir !
-         // If the central dir data are false, we are doomed.
-         // On the bright side, the local part is scary  : zip64, data descriptors, both, etc.
-         // The less data we get here, the more reliable this should be.
-         // Let's skip the whole header and dash to the data !
-         reader.skip(22);
-         // in some zip created on windows, the filename stored in the central dir contains \ instead of /.
-         // Strangely, the filename here is OK.
-         // I would love to treat these zip files as corrupted (see http://www.info-zip.org/FAQ.html#backslashes
-         // or APPNOTE#4.4.17.1, "All slashes MUST be forward slashes '/'") but there are a lot of bad zip generators...
-         // Search "unzip mismatching "local" filename continuing with "central" filename version" on
-         // the internet.
-         //
-         // I think I see the logic here : the central directory is used to display
-         // content and the local directory is used to extract the files. Mixing / and \
-         // may be used to display \ to windows users and use / when extracting the files.
-         // Unfortunately, this lead also to some issues : http://seclists.org/fulldisclosure/2009/Sep/394
-         this.fileNameLength = reader.readInt(2);
-         localExtraFieldsLength = reader.readInt(2); // can't be sure this will be the same as the central dir
-         this.fileName = reader.readString(this.fileNameLength);
-         reader.skip(localExtraFieldsLength);
-
-         if (this.compressedSize == -1 || this.uncompressedSize == -1) {
-            throw new Error("Bug or corrupted zip : didn't get enough informations from the central directory " +
-                            "(compressedSize == -1 || uncompressedSize == -1)");
-         }
-         this.compressedFileData = reader.readString(this.compressedSize);
-
-         compression = findCompression(this.compressionMethod);
-         if (compression === null) { // no compression found
-            throw new Error("Corrupted zip : compression " + pretty(this.compressionMethod) +
-                            " unknown (inner file : " + this.fileName + ")");
-         }
-         this.uncompressedFileData = compression.uncompress(this.compressedFileData);
-
-         if (this.uncompressedFileData.length !== this.uncompressedSize) {
-            throw new Error("Bug : uncompressed data size mismatch");
-         }
-
-         if (this.loadOptions.checkCRC32 && JSZip.prototype.crc32(this.uncompressedFileData) !== this.crc32) {
-            throw new Error("Corrupted zip : CRC32 mismatch");
-         }
-      },
-
-      /**
-       * Read the central part of a zip file and add the info in this object.
-       * @param {StreamReader} reader the reader to use.
-       */
-      readCentralPart : function(reader) {
-         this.versionMadeBy          = reader.readString(2);
-         this.versionNeeded          = reader.readInt(2);
-         this.bitFlag                = reader.readInt(2);
-         this.compressionMethod      = reader.readString(2);
-         this.date                   = reader.readDate();
-         this.crc32                  = reader.readInt(4);
-         this.compressedSize         = reader.readInt(4);
-         this.uncompressedSize       = reader.readInt(4);
-         this.fileNameLength         = reader.readInt(2);
-         this.extraFieldsLength      = reader.readInt(2);
-         this.fileCommentLength      = reader.readInt(2);
-         this.diskNumberStart        = reader.readInt(2);
-         this.internalFileAttributes = reader.readInt(2);
-         this.externalFileAttributes = reader.readInt(4);
-         this.localHeaderOffset      = reader.readInt(4);
-
-         if (this.isEncrypted()) {
-            throw new Error("Encrypted zip are not supported");
-         }
-
-         this.fileName = reader.readString(this.fileNameLength);
-         this.readExtraFields(reader);
-         this.parseZIP64ExtraField(reader);
-         this.fileComment = reader.readString(this.fileCommentLength);
-
-         // warning, this is true only for zip with madeBy == DOS (plateform dependent feature)
-         this.dir = this.externalFileAttributes & 0x00000010 ? true : false;
-      },
-      /**
-       * Parse the ZIP64 extra field and merge the info in the current ZipEntry.
-       * @param {StreamReader} reader the reader to use.
-       */
-      parseZIP64ExtraField : function(reader) {
-
-         if(!this.extraFields[0x0001]) {
-            return;
-         }
-
-         // should be something, preparing the extra reader
-         var extraReader = new StreamReader(this.extraFields[0x0001].value);
-
-         // I really hope that these 64bits integer can fit in 32 bits integer, because js
-         // won't let us have more.
-         if(this.uncompressedSize === MAX_VALUE_32BITS) {
-            this.uncompressedSize = extraReader.readInt(8);
-         }
-         if(this.compressedSize === MAX_VALUE_32BITS) {
-            this.compressedSize = extraReader.readInt(8);
-         }
-         if(this.localHeaderOffset === MAX_VALUE_32BITS) {
-            this.localHeaderOffset = extraReader.readInt(8);
-         }
-         if(this.diskNumberStart === MAX_VALUE_32BITS) {
-            this.diskNumberStart = extraReader.readInt(4);
-         }
-      },
-      /**
-       * Read the central part of a zip file and add the info in this object.
-       * @param {StreamReader} reader the reader to use.
-       */
-      readExtraFields : function(reader) {
-         var start = reader.index,
-             extraFieldId,
-             extraFieldLength,
-             extraFieldValue;
-
-         this.extraFields = this.extraFields || {};
-
-         while (reader.index < start + this.extraFieldsLength) {
-            extraFieldId     = reader.readInt(2);
-            extraFieldLength = reader.readInt(2);
-            extraFieldValue  = reader.readString(extraFieldLength);
-
-            this.extraFields[extraFieldId] = {
-               id:     extraFieldId,
-               length: extraFieldLength,
-               value:  extraFieldValue
-            };
-         }
-      },
-      /**
-       * Apply an UTF8 transformation if needed.
-       */
-      handleUTF8 : function() {
-         if (this.useUTF8()) {
-            this.fileName    = JSZip.prototype.utf8decode(this.fileName);
-            this.fileComment = JSZip.prototype.utf8decode(this.fileComment);
-         }
-      }
-   };
-   // }}} end of ZipEntry
-
-   //  class ZipEntries {{{
-   /**
-    * All the entries in the zip file.
-    * @constructor
-    * @param {String|ArrayBuffer|Uint8Array} data the binary stream to load.
-    * @param {Object} loadOptions Options for loading the stream.
-    */
-   function ZipEntries(data, loadOptions) {
-      this.files = [];
-      this.loadOptions = loadOptions;
-      if (data) {
-         this.load(data);
-      }
-   }
-   ZipEntries.prototype = {
-      /**
-       * Check that the reader is on the speficied signature.
-       * @param {string} expectedSignature the expected signature.
-       * @throws {Error} if it is an other signature.
-       */
-      checkSignature : function(expectedSignature) {
-         var signature = this.reader.readString(4);
-         if (signature !== expectedSignature) {
-            throw new Error("Corrupted zip or bug : unexpected signature " +
-                            "(" + pretty(signature) + ", expected " + pretty(expectedSignature) + ")");
-         }
-      },
-      /**
-       * Read the end of the central directory.
-       */
-      readBlockEndOfCentral : function () {
-         this.diskNumber                  = this.reader.readInt(2);
-         this.diskWithCentralDirStart     = this.reader.readInt(2);
-         this.centralDirRecordsOnThisDisk = this.reader.readInt(2);
-         this.centralDirRecords           = this.reader.readInt(2);
-         this.centralDirSize              = this.reader.readInt(4);
-         this.centralDirOffset            = this.reader.readInt(4);
-
-         this.zipCommentLength            = this.reader.readInt(2);
-         this.zipComment                  = this.reader.readString(this.zipCommentLength);
-      },
-      /**
-       * Read the end of the Zip 64 central directory.
-       * Not merged with the method readEndOfCentral :
-       * The end of central can coexist with its Zip64 brother,
-       * I don't want to read the wrong number of bytes !
-       */
-      readBlockZip64EndOfCentral : function () {
-         this.zip64EndOfCentralSize       = this.reader.readInt(8);
-         this.versionMadeBy               = this.reader.readString(2);
-         this.versionNeeded               = this.reader.readInt(2);
-         this.diskNumber                  = this.reader.readInt(4);
-         this.diskWithCentralDirStart     = this.reader.readInt(4);
-         this.centralDirRecordsOnThisDisk = this.reader.readInt(8);
-         this.centralDirRecords           = this.reader.readInt(8);
-         this.centralDirSize              = this.reader.readInt(8);
-         this.centralDirOffset            = this.reader.readInt(8);
-
-         this.zip64ExtensibleData = {};
-         var extraDataSize = this.zip64EndOfCentralSize - 44,
-         index = 0,
-         extraFieldId,
-         extraFieldLength,
-         extraFieldValue;
-         while(index < extraDataSize) {
-            extraFieldId     = this.reader.readInt(2);
-            extraFieldLength = this.reader.readInt(4);
-            extraFieldValue  = this.reader.readString(extraFieldLength);
-            this.zip64ExtensibleData[extraFieldId] = {
-               id:     extraFieldId,
-               length: extraFieldLength,
-               value:  extraFieldValue
-            };
-         }
-      },
-      /**
-       * Read the end of the Zip 64 central directory locator.
-       */
-      readBlockZip64EndOfCentralLocator : function () {
-         this.diskWithZip64CentralDirStart       = this.reader.readInt(4);
-         this.relativeOffsetEndOfZip64CentralDir = this.reader.readInt(8);
-         this.disksCount                         = this.reader.readInt(4);
-         if (this.disksCount > 1) {
-            throw new Error("Multi-volumes zip are not supported");
-         }
-      },
-      /**
-       * Read the local files, based on the offset read in the central part.
-       */
-      readLocalFiles : function() {
-         var i, file;
-         for(i = 0; i < this.files.length; i++) {
-            file = this.files[i];
-            this.reader.setIndex(file.localHeaderOffset);
-            this.checkSignature(JSZip.signature.LOCAL_FILE_HEADER);
-            file.readLocalPart(this.reader);
-            file.handleUTF8();
-         }
-      },
-      /**
-       * Read the central directory.
-       */
-      readCentralDir : function() {
-         var file;
-
-         this.reader.setIndex(this.centralDirOffset);
-         while(this.reader.readString(4) === JSZip.signature.CENTRAL_FILE_HEADER) {
-            file = new ZipEntry({
-               zip64: this.zip64
-            }, this.loadOptions);
-            file.readCentralPart(this.reader);
-            this.files.push(file);
-         }
-      },
-      /**
-       * Read the end of central directory.
-       */
-      readEndOfCentral : function() {
-            var offset = this.reader.stream.lastIndexOf(JSZip.signature.CENTRAL_DIRECTORY_END);
-            if (offset === -1) {
-               throw new Error("Corrupted zip : can't find end of central directory");
-            }
-            this.reader.setIndex(offset);
-            this.checkSignature(JSZip.signature.CENTRAL_DIRECTORY_END);
-            this.readBlockEndOfCentral();
-
-         
-            /* extract from the zip spec :
-               4)  If one of the fields in the end of central directory
-                   record is too small to hold required data, the field
-                   should be set to -1 (0xFFFF or 0xFFFFFFFF) and the
-                   ZIP64 format record should be created.
-               5)  The end of central directory record and the
-                   Zip64 end of central directory locator record must
-                   reside on the same disk when splitting or spanning
-                   an archive.
-            */
-            if (  this.diskNumber                  === MAX_VALUE_16BITS
-               || this.diskWithCentralDirStart     === MAX_VALUE_16BITS
-               || this.centralDirRecordsOnThisDisk === MAX_VALUE_16BITS
-               || this.centralDirRecords           === MAX_VALUE_16BITS
-               || this.centralDirSize              === MAX_VALUE_32BITS
-               || this.centralDirOffset            === MAX_VALUE_32BITS
-            ) {
-               this.zip64 = true;
-
-               /*
-               Warning : the zip64 extension is supported, but ONLY if the 64bits integer read from
-               the zip file can fit into a 32bits integer. This cannot be solved : Javascript represents
-               all numbers as 64-bit double precision IEEE 754 floating point numbers.
-               So, we have 53bits for integers and bitwise operations treat everything as 32bits.
-               see https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/Bitwise_Operators
-               and http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf section 8.5
-               */
-
-               // should look for a zip64 EOCD locator
-               offset = this.reader.stream.lastIndexOf(JSZip.signature.ZIP64_CENTRAL_DIRECTORY_LOCATOR);
-               if (offset === -1) {
-                  throw new Error("Corrupted zip : can't find the ZIP64 end of central directory locator");
-               }
-               this.reader.setIndex(offset);
-               this.checkSignature(JSZip.signature.ZIP64_CENTRAL_DIRECTORY_LOCATOR);
-               this.readBlockZip64EndOfCentralLocator();
-
-               // now the zip64 EOCD record
-               this.reader.setIndex(this.relativeOffsetEndOfZip64CentralDir);
-               this.checkSignature(JSZip.signature.ZIP64_CENTRAL_DIRECTORY_END);
-               this.readBlockZip64EndOfCentral();
-            }
-      },
-      /**
-       * Read a zip file and create ZipEntries.
-       * @param {String|ArrayBuffer|Uint8Array} data the binary string representing a zip file.
-       */
-      load : function(data) {
-         this.reader = new StreamReader(data);
-
-         this.readEndOfCentral();
-         this.readCentralDir();
-         this.readLocalFiles();
-      }
-   };
-   // }}} end of ZipEntries
-
-   /**
-    * Implementation of the load method of JSZip.
-    * It uses the above classes to decode a zip file, and load every files.
-    * @param {String|ArrayBuffer|Uint8Array} data the data to load.
-    * @param {Object} options Options for loading the stream.
-    *  options.base64 : is the stream in base64 ? default : false
-    */
-   JSZip.prototype.load = function(data, options) {
-      var files, zipEntries, i, input;
-      options = options || {};
-      if(options.base64) {
-         data = JSZipBase64.decode(data);
-      }
-
-      zipEntries = new ZipEntries(data, options);
-      files = zipEntries.files;
-      for (i = 0; i < files.length; i++) {
-         input = files[i];
-         this.file(input.fileName, input.uncompressedFileData, {
-            binary:true,
-            optimizedBinaryString:true,
-            date:input.date,
-            dir:input.dir
-         });
-      }
-
-      return this;
-   };
-
-}());
-// enforcing Stuk's coding style
-// vim: set shiftwidth=3 softtabstop=3 foldmethod=marker:
+var STYLES_XML_ROOT = writextag('styleSheet', null, {
+	'xmlns': XMLNS.main[0],
+	'xmlns:vt': XMLNS.vt
+});
+
+RELS.STY = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles";
+
+function write_sty_xml(wb, opts) {
+	var o = [XML_HEADER, STYLES_XML_ROOT], w;
+	if((w = write_numFmts(wb.SSF)) != null) o[o.length] = w;
+	o[o.length] = ('<fonts count="1"><font><sz val="12"/><color theme="1"/><name val="Calibri"/><family val="2"/><scheme val="minor"/></font></fonts>');
+	o[o.length] = ('<fills count="2"><fill><patternFill patternType="none"/></fill><fill><patternFill patternType="gray125"/></fill></fills>');
+	o[o.length] = ('<borders count="1"><border><left/><right/><top/><bottom/><diagonal/></border></borders>');
+	o[o.length] = ('<cellStyleXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0"/></cellStyleXfs>');
+	if((w = write_cellXfs(opts.cellXfs))) o[o.length] = (w);
+	o[o.length] = ('<cellStyles count="1"><cellStyle name="Normal" xfId="0" builtinId="0"/></cellStyles>');
+	o[o.length] = ('<dxfs count="0"/>');
+	o[o.length] = ('<tableStyles count="0" defaultTableStyle="TableStyleMedium9" defaultPivotStyle="PivotStyleMedium4"/>');
+
+	if(o.length>2){ o[o.length] = ('</styleSheet>'); o[1]=o[1].replace("/>",">"); }
+	return o.join("");
+}
+/* [MS-XLSB] 2.4.651 BrtFmt */
+function parse_BrtFmt(data, length) {
+	var ifmt = data.read_shift(2);
+	var stFmtCode = parse_XLWideString(data,length-2);
+	return [ifmt, stFmtCode];
+}
+
+/* [MS-XLSB] 2.4.653 BrtFont TODO */
+function parse_BrtFont(data, length) {
+	var out = {flags:{}};
+	out.dyHeight = data.read_shift(2);
+	out.grbit = parse_FontFlags(data, 2);
+	out.bls = data.read_shift(2);
+	out.sss = data.read_shift(2);
+	out.uls = data.read_shift(1);
+	out.bFamily = data.read_shift(1);
+	out.bCharSet = data.read_shift(1);
+	data.l++;
+	out.brtColor = parse_BrtColor(data, 8);
+	out.bFontScheme = data.read_shift(1);
+	out.name = parse_XLWideString(data, length - 21);
+
+	out.flags.Bold = out.bls === 0x02BC;
+	out.flags.Italic = out.grbit.fItalic;
+	out.flags.Strikeout = out.grbit.fStrikeout;
+	out.flags.Outline = out.grbit.fOutline;
+	out.flags.Shadow = out.grbit.fShadow;
+	out.flags.Condense = out.grbit.fCondense;
+	out.flags.Extend = out.grbit.fExtend;
+	out.flags.Sub = out.sss & 0x2;
+	out.flags.Sup = out.sss & 0x1;
+	return out;
+}
+
+/* [MS-XLSB] 2.4.816 BrtXF */
+function parse_BrtXF(data, length) {
+	var ixfeParent = data.read_shift(2);
+	var ifmt = data.read_shift(2);
+	parsenoop(data, length-4);
+	return {ixfe:ixfeParent, ifmt:ifmt };
+}
+
+/* [MS-XLSB] 2.1.7.50 Styles */
+function parse_sty_bin(data, opts) {
+	styles.NumberFmt = [];
+	for(var y in SSF._table) styles.NumberFmt[y] = SSF._table[y];
+
+	styles.CellXf = [];
+	var state = ""; /* TODO: this should be a stack */
+	var pass = false;
+	recordhopper(data, function hopper_sty(val, R, RT) {
+		switch(R.n) {
+			case 'BrtFmt':
+				styles.NumberFmt[val[0]] = val[1]; SSF.load(val[1], val[0]);
+				break;
+			case 'BrtFont': break; /* TODO */
+			case 'BrtKnownFonts': break; /* TODO */
+			case 'BrtFill': break; /* TODO */
+			case 'BrtBorder': break; /* TODO */
+			case 'BrtXF':
+				if(state === "CELLXFS") {
+					styles.CellXf.push(val);
+				}
+				break; /* TODO */
+			case 'BrtStyle': break; /* TODO */
+			case 'BrtDXF': break; /* TODO */
+			case 'BrtMRUColor': break; /* TODO */
+			case 'BrtIndexedColor': break; /* TODO */
+			case 'BrtBeginStyleSheet': break;
+			case 'BrtEndStyleSheet': break;
+			case 'BrtBeginTableStyle': break;
+			case 'BrtTableStyleElement': break;
+			case 'BrtEndTableStyle': break;
+			case 'BrtBeginFmts': state = "FMTS"; break;
+			case 'BrtEndFmts': state = ""; break;
+			case 'BrtBeginFonts': state = "FONTS"; break;
+			case 'BrtEndFonts': state = ""; break;
+			case 'BrtACBegin': state = "ACFONTS"; break;
+			case 'BrtACEnd': state = ""; break;
+			case 'BrtBeginFills': state = "FILLS"; break;
+			case 'BrtEndFills': state = ""; break;
+			case 'BrtBeginBorders': state = "BORDERS"; break;
+			case 'BrtEndBorders': state = ""; break;
+			case 'BrtBeginCellStyleXFs': state = "CELLSTYLEXFS"; break;
+			case 'BrtEndCellStyleXFs': state = ""; break;
+			case 'BrtBeginCellXFs': state = "CELLXFS"; break;
+			case 'BrtEndCellXFs': state = ""; break;
+			case 'BrtBeginStyles': state = "STYLES"; break;
+			case 'BrtEndStyles': state = ""; break;
+			case 'BrtBeginDXFs': state = "DXFS"; break;
+			case 'BrtEndDXFs': state = ""; break;
+			case 'BrtBeginTableStyles': state = "TABLESTYLES"; break;
+			case 'BrtEndTableStyles': state = ""; break;
+			case 'BrtBeginColorPalette': state = "COLORPALETTE"; break;
+			case 'BrtEndColorPalette': state = ""; break;
+			case 'BrtBeginIndexedColors': state = "INDEXEDCOLORS"; break;
+			case 'BrtEndIndexedColors': state = ""; break;
+			case 'BrtBeginMRUColors': state = "MRUCOLORS"; break;
+			case 'BrtEndMRUColors': state = ""; break;
+			case 'BrtFRTBegin': pass = true; break;
+			case 'BrtFRTEnd': pass = false; break;
+			case 'BrtBeginStyleSheetExt14': break;
+			case 'BrtBeginSlicerStyles': break;
+			case 'BrtEndSlicerStyles': break;
+			case 'BrtBeginTimelineStylesheetExt15': break;
+			case 'BrtEndTimelineStylesheetExt15': break;
+			case 'BrtBeginTimelineStyles': break;
+			case 'BrtEndTimelineStyles': break;
+			case 'BrtEndStyleSheetExt14': break;
+			default: if(!pass || opts.WTF) throw new Error("Unexpected record " + RT + " " + R.n);
+		}
+	});
+	return styles;
+}
+
+/* [MS-XLSB] 2.1.7.50 Styles */
+function write_sty_bin(data, opts) {
+	var ba = buf_array();
+	write_record(ba, "BrtBeginStyleSheet");
+	/* [FMTS] */
+	/* [FONTS] */
+	/* [FILLS] */
+	/* [BORDERS] */
+	/* CELLSTYLEXFS */
+	/* CELLXFS*/
+	/* STYLES */
+	/* DXFS */
+	/* TABLESTYLES */
+	/* [COLORPALETTE] */
+	/* FRTSTYLESHEET*/
+	write_record(ba, "BrtEndStyleSheet");
+	return ba.end();
+}
+RELS.THEME = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme";
+
+/* 20.1.6.2 clrScheme CT_ColorScheme */
+function parse_clrScheme(t, opts) {
+	themes.themeElements.clrScheme = [];
+	var color = {};
+	t[0].match(tagregex).forEach(function(x) {
+		var y = parsexmltag(x);
+		switch(y[0]) {
+			case '<a:clrScheme': case '</a:clrScheme>': break;
+
+			/* 20.1.2.3.32 srgbClr CT_SRgbColor */
+			case '<a:srgbClr': color.rgb = y.val; break;
+
+			/* 20.1.2.3.33 sysClr CT_SystemColor */
+			case '<a:sysClr': color.rgb = y.lastClr; break;
+
+			/* 20.1.4.1.9 dk1 (Dark 1) */
+			case '<a:dk1>':
+			case '</a:dk1>':
+			/* 20.1.4.1.10 dk2 (Dark 2) */
+			case '<a:dk2>':
+			case '</a:dk2>':
+			/* 20.1.4.1.22 lt1 (Light 1) */
+			case '<a:lt1>':
+			case '</a:lt1>':
+			/* 20.1.4.1.23 lt2 (Light 2) */
+			case '<a:lt2>':
+			case '</a:lt2>':
+			/* 20.1.4.1.1 accent1 (Accent 1) */
+			case '<a:accent1>':
+			case '</a:accent1>':
+			/* 20.1.4.1.2 accent2 (Accent 2) */
+			case '<a:accent2>':
+			case '</a:accent2>':
+			/* 20.1.4.1.3 accent3 (Accent 3) */
+			case '<a:accent3>':
+			case '</a:accent3>':
+			/* 20.1.4.1.4 accent4 (Accent 4) */
+			case '<a:accent4>':
+			case '</a:accent4>':
+			/* 20.1.4.1.5 accent5 (Accent 5) */
+			case '<a:accent5>':
+			case '</a:accent5>':
+			/* 20.1.4.1.6 accent6 (Accent 6) */
+			case '<a:accent6>':
+			case '</a:accent6>':
+			/* 20.1.4.1.19 hlink (Hyperlink) */
+			case '<a:hlink>':
+			case '</a:hlink>':
+			/* 20.1.4.1.15 folHlink (Followed Hyperlink) */
+			case '<a:folHlink>':
+			case '</a:folHlink>':
+				if (y[0][1] === '/') {
+					themes.themeElements.clrScheme.push(color);
+					color = {};
+				} else {
+					color.name = y[0].substring(3, y[0].length - 1);
+				}
+				break;
+
+			default: if(opts.WTF) throw 'unrecognized ' + y[0] + ' in clrScheme';
+		}
+	});
+}
+
+var clrsregex = /<a:clrScheme([^>]*)>.*<\/a:clrScheme>/;
+/* 14.2.7 Theme Part */
+function parse_theme_xml(data, opts) {
+	if(!data || data.length === 0) return themes;
+	themes.themeElements = {};
+
+	var t;
+
+	/* clrScheme CT_ColorScheme */
+	if((t=data.match(clrsregex))) parse_clrScheme(t, opts);
+
+	return themes;
+}
+
+function write_theme() { return '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n<a:theme xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" name="Office Theme"><a:themeElements><a:clrScheme name="Office"><a:dk1><a:sysClr val="windowText" lastClr="000000"/></a:dk1><a:lt1><a:sysClr val="window" lastClr="FFFFFF"/></a:lt1><a:dk2><a:srgbClr val="1F497D"/></a:dk2><a:lt2><a:srgbClr val="EEECE1"/></a:lt2><a:accent1><a:srgbClr val="4F81BD"/></a:accent1><a:accent2><a:srgbClr val="C0504D"/></a:accent2><a:accent3><a:srgbClr val="9BBB59"/></a:accent3><a:accent4><a:srgbClr val="8064A2"/></a:accent4><a:accent5><a:srgbClr val="4BACC6"/></a:accent5><a:accent6><a:srgbClr val="F79646"/></a:accent6><a:hlink><a:srgbClr val="0000FF"/></a:hlink><a:folHlink><a:srgbClr val="800080"/></a:folHlink></a:clrScheme><a:fontScheme name="Office"><a:majorFont><a:latin typeface="Cambria"/><a:ea typeface=""/><a:cs typeface=""/><a:font script="Jpan" typeface="MS Pゴシック"/><a:font script="Hang" typeface="맑은 고딕"/><a:font script="Hans" typeface="宋体"/><a:font script="Hant" typeface="新細明體"/><a:font script="Arab" typeface="Times New Roman"/><a:font script="Hebr" typeface="Times New Roman"/><a:font script="Thai" typeface="Tahoma"/><a:font script="Ethi" typeface="Nyala"/><a:font script="Beng" typeface="Vrinda"/><a:font script="Gujr" typeface="Shruti"/><a:font script="Khmr" typeface="MoolBoran"/><a:font script="Knda" typeface="Tunga"/><a:font script="Guru" typeface="Raavi"/><a:font script="Cans" typeface="Euphemia"/><a:font script="Cher" typeface="Plantagenet Cherokee"/><a:font script="Yiii" typeface="Microsoft Yi Baiti"/><a:font script="Tibt" typeface="Microsoft Himalaya"/><a:font script="Thaa" typeface="MV Boli"/><a:font script="Deva" typeface="Mangal"/><a:font script="Telu" typeface="Gautami"/><a:font script="Taml" typeface="Latha"/><a:font script="Syrc" typeface="Estrangelo Edessa"/><a:font script="Orya" typeface="Kalinga"/><a:font script="Mlym" typeface="Kartika"/><a:font script="Laoo" typeface="DokChampa"/><a:font script="Sinh" typeface="Iskoola Pota"/><a:font script="Mong" typeface="Mongolian Baiti"/><a:font script="Viet" typeface="Times New Roman"/><a:font script="Uigh" typeface="Microsoft Uighur"/><a:font script="Geor" typeface="Sylfaen"/></a:majorFont><a:minorFont><a:latin typeface="Calibri"/><a:ea typeface=""/><a:cs typeface=""/><a:font script="Jpan" typeface="MS Pゴシック"/><a:font script="Hang" typeface="맑은 고딕"/><a:font script="Hans" typeface="宋体"/><a:font script="Hant" typeface="新細明體"/><a:font script="Arab" typeface="Arial"/><a:font script="Hebr" typeface="Arial"/><a:font script="Thai" typeface="Tahoma"/><a:font script="Ethi" typeface="Nyala"/><a:font script="Beng" typeface="Vrinda"/><a:font script="Gujr" typeface="Shruti"/><a:font script="Khmr" typeface="DaunPenh"/><a:font script="Knda" typeface="Tunga"/><a:font script="Guru" typeface="Raavi"/><a:font script="Cans" typeface="Euphemia"/><a:font script="Cher" typeface="Plantagenet Cherokee"/><a:font script="Yiii" typeface="Microsoft Yi Baiti"/><a:font script="Tibt" typeface="Microsoft Himalaya"/><a:font script="Thaa" typeface="MV Boli"/><a:font script="Deva" typeface="Mangal"/><a:font script="Telu" typeface="Gautami"/><a:font script="Taml" typeface="Latha"/><a:font script="Syrc" typeface="Estrangelo Edessa"/><a:font script="Orya" typeface="Kalinga"/><a:font script="Mlym" typeface="Kartika"/><a:font script="Laoo" typeface="DokChampa"/><a:font script="Sinh" typeface="Iskoola Pota"/><a:font script="Mong" typeface="Mongolian Baiti"/><a:font script="Viet" typeface="Arial"/><a:font script="Uigh" typeface="Microsoft Uighur"/><a:font script="Geor" typeface="Sylfaen"/></a:minorFont></a:fontScheme><a:fmtScheme name="Office"><a:fillStyleLst><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:gradFill rotWithShape="1"><a:gsLst><a:gs pos="0"><a:schemeClr val="phClr"><a:tint val="50000"/><a:satMod val="300000"/></a:schemeClr></a:gs><a:gs pos="35000"><a:schemeClr val="phClr"><a:tint val="37000"/><a:satMod val="300000"/></a:schemeClr></a:gs><a:gs pos="100000"><a:schemeClr val="phClr"><a:tint val="15000"/><a:satMod val="350000"/></a:schemeClr></a:gs></a:gsLst><a:lin ang="16200000" scaled="1"/></a:gradFill><a:gradFill rotWithShape="1"><a:gsLst><a:gs pos="0"><a:schemeClr val="phClr"><a:tint val="100000"/><a:shade val="100000"/><a:satMod val="130000"/></a:schemeClr></a:gs><a:gs pos="100000"><a:schemeClr val="phClr"><a:tint val="50000"/><a:shade val="100000"/><a:satMod val="350000"/></a:schemeClr></a:gs></a:gsLst><a:lin ang="16200000" scaled="0"/></a:gradFill></a:fillStyleLst><a:lnStyleLst><a:ln w="9525" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr"><a:shade val="95000"/><a:satMod val="105000"/></a:schemeClr></a:solidFill><a:prstDash val="solid"/></a:ln><a:ln w="25400" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:prstDash val="solid"/></a:ln><a:ln w="38100" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:prstDash val="solid"/></a:ln></a:lnStyleLst><a:effectStyleLst><a:effectStyle><a:effectLst><a:outerShdw blurRad="40000" dist="20000" dir="5400000" rotWithShape="0"><a:srgbClr val="000000"><a:alpha val="38000"/></a:srgbClr></a:outerShdw></a:effectLst></a:effectStyle><a:effectStyle><a:effectLst><a:outerShdw blurRad="40000" dist="23000" dir="5400000" rotWithShape="0"><a:srgbClr val="000000"><a:alpha val="35000"/></a:srgbClr></a:outerShdw></a:effectLst></a:effectStyle><a:effectStyle><a:effectLst><a:outerShdw blurRad="40000" dist="23000" dir="5400000" rotWithShape="0"><a:srgbClr val="000000"><a:alpha val="35000"/></a:srgbClr></a:outerShdw></a:effectLst><a:scene3d><a:camera prst="orthographicFront"><a:rot lat="0" lon="0" rev="0"/></a:camera><a:lightRig rig="threePt" dir="t"><a:rot lat="0" lon="0" rev="1200000"/></a:lightRig></a:scene3d><a:sp3d><a:bevelT w="63500" h="25400"/></a:sp3d></a:effectStyle></a:effectStyleLst><a:bgFillStyleLst><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:gradFill rotWithShape="1"><a:gsLst><a:gs pos="0"><a:schemeClr val="phClr"><a:tint val="40000"/><a:satMod val="350000"/></a:schemeClr></a:gs><a:gs pos="40000"><a:schemeClr val="phClr"><a:tint val="45000"/><a:shade val="99000"/><a:satMod val="350000"/></a:schemeClr></a:gs><a:gs pos="100000"><a:schemeClr val="phClr"><a:shade val="20000"/><a:satMod val="255000"/></a:schemeClr></a:gs></a:gsLst><a:path path="circle"><a:fillToRect l="50000" t="-80000" r="50000" b="180000"/></a:path></a:gradFill><a:gradFill rotWithShape="1"><a:gsLst><a:gs pos="0"><a:schemeClr val="phClr"><a:tint val="80000"/><a:satMod val="300000"/></a:schemeClr></a:gs><a:gs pos="100000"><a:schemeClr val="phClr"><a:shade val="30000"/><a:satMod val="200000"/></a:schemeClr></a:gs></a:gsLst><a:path path="circle"><a:fillToRect l="50000" t="50000" r="50000" b="50000"/></a:path></a:gradFill></a:bgFillStyleLst></a:fmtScheme></a:themeElements><a:objectDefaults><a:spDef><a:spPr/><a:bodyPr/><a:lstStyle/><a:style><a:lnRef idx="1"><a:schemeClr val="accent1"/></a:lnRef><a:fillRef idx="3"><a:schemeClr val="accent1"/></a:fillRef><a:effectRef idx="2"><a:schemeClr val="accent1"/></a:effectRef><a:fontRef idx="minor"><a:schemeClr val="lt1"/></a:fontRef></a:style></a:spDef><a:lnDef><a:spPr/><a:bodyPr/><a:lstStyle/><a:style><a:lnRef idx="2"><a:schemeClr val="accent1"/></a:lnRef><a:fillRef idx="0"><a:schemeClr val="accent1"/></a:fillRef><a:effectRef idx="1"><a:schemeClr val="accent1"/></a:effectRef><a:fontRef idx="minor"><a:schemeClr val="tx1"/></a:fontRef></a:style></a:lnDef></a:objectDefaults><a:extraClrSchemeLst/></a:theme>'; }
+/* 18.6 Calculation Chain */
+function parse_cc_xml(data, opts) {
+	var d = [];
+	var l = 0, i = 1;
+	(data.match(tagregex)||[]).forEach(function(x) {
+		var y = parsexmltag(x);
+		switch(y[0]) {
+			case '<?xml': break;
+			/* 18.6.2  calcChain CT_CalcChain 1 */
+			case '<calcChain': case '<calcChain>': case '</calcChain>': break;
+			/* 18.6.1  c CT_CalcCell 1 */
+			case '<c': delete y[0]; if(y.i) i = y.i; else y.i = i; d.push(y); break;
+		}
+	});
+	return d;
+}
+
+function write_cc_xml(data, opts) { }
+/* [MS-XLSB] 2.6.4.1 */
+function parse_BrtCalcChainItem$(data, length) {
+	var out = {};
+	out.i = data.read_shift(4);
+	var cell = {};
+	cell.r = data.read_shift(4);
+	cell.c = data.read_shift(4);
+	out.r = encode_cell(cell);
+	var flags = data.read_shift(1);
+	if(flags & 0x2) out.l = '1';
+	if(flags & 0x8) out.a = '1';
+	return out;
+}
+
+/* 18.6 Calculation Chain */
+function parse_cc_bin(data, opts) {
+	var out = [];
+	var pass = false;
+	recordhopper(data, function hopper_cc(val, R, RT) {
+		switch(R.n) {
+			case 'BrtCalcChainItem$': out.push(val); break;
+			case 'BrtBeginCalcChain$': break;
+			case 'BrtEndCalcChain$': break;
+			default: if(!pass || opts.WTF) throw new Error("Unexpected record " + RT + " " + R.n);
+		}
+	});
+	return out;
+}
+
+function write_cc_bin(data, opts) { }
+
+function parse_comments(zip, dirComments, sheets, sheetRels, opts) {
+	for(var i = 0; i != dirComments.length; ++i) {
+		var canonicalpath=dirComments[i];
+		var comments=parse_cmnt(getzipdata(zip, canonicalpath.replace(/^\//,''), true), canonicalpath, opts);
+		if(!comments || !comments.length) continue;
+		// find the sheets targeted by these comments
+		var sheetNames = keys(sheets);
+		for(var j = 0; j != sheetNames.length; ++j) {
+			var sheetName = sheetNames[j];
+			var rels = sheetRels[sheetName];
+			if(rels) {
+				var rel = rels[canonicalpath];
+				if(rel) insertCommentsIntoSheet(sheetName, sheets[sheetName], comments);
+			}
+		}
+	}
+}
+
+function insertCommentsIntoSheet(sheetName, sheet, comments) {
+	comments.forEach(function(comment) {
+		var cell = sheet[comment.ref];
+		if (!cell) {
+			cell = {};
+			sheet[comment.ref] = cell;
+			var range = safe_decode_range(sheet["!ref"]||"BDWGO1000001:A1");
+			var thisCell = decode_cell(comment.ref);
+			if(range.s.r > thisCell.r) range.s.r = thisCell.r;
+			if(range.e.r < thisCell.r) range.e.r = thisCell.r;
+			if(range.s.c > thisCell.c) range.s.c = thisCell.c;
+			if(range.e.c < thisCell.c) range.e.c = thisCell.c;
+			var encoded = encode_range(range);
+			if (encoded !== sheet["!ref"]) sheet["!ref"] = encoded;
+		}
+
+		if (!cell.c) cell.c = [];
+		var o = {a: comment.author, t: comment.t, r: comment.r};
+		if(comment.h) o.h = comment.h;
+		cell.c.push(o);
+	});
+}
+
+/* 18.7.3 CT_Comment */
+function parse_comments_xml(data, opts) {
+	if(data.match(/<(?:\w+:)?comments *\/>/)) return [];
+	var authors = [];
+	var commentList = [];
+	data.match(/<(?:\w+:)?authors>([^\u2603]*)<\/(?:\w+:)?authors>/)[1].split(/<\/\w*:?author>/).forEach(function(x) {
+		if(x === "" || x.trim() === "") return;
+		authors.push(x.match(/<(?:\w+:)?author[^>]*>(.*)/)[1]);
+	});
+	(data.match(/<(?:\w+:)?commentList>([^\u2603]*)<\/(?:\w+:)?commentList>/)||["",""])[1].split(/<\/\w*:?comment>/).forEach(function(x, index) {
+		if(x === "" || x.trim() === "") return;
+		var y = parsexmltag(x.match(/<(?:\w+:)?comment[^>]*>/)[0]);
+		var comment = { author: y.authorId && authors[y.authorId] ? authors[y.authorId] : undefined, ref: y.ref, guid: y.guid };
+		var cell = decode_cell(y.ref);
+		if(opts.sheetRows && opts.sheetRows <= cell.r) return;
+		var textMatch = x.match(/<text>([^\u2603]*)<\/text>/);
+		if (!textMatch || !textMatch[1]) return; // a comment may contain an empty text tag.
+		var rt = parse_si(textMatch[1]);
+		comment.r = rt.r;
+		comment.t = rt.t;
+		if(opts.cellHTML) comment.h = rt.h;
+		commentList.push(comment);
+	});
+	return commentList;
+}
+
+function write_comments_xml(data, opts) { }
+/* [MS-XLSB] 2.4.28 BrtBeginComment */
+function parse_BrtBeginComment(data, length) {
+	var out = {};
+	out.iauthor = data.read_shift(4);
+	var rfx = parse_UncheckedRfX(data, 16);
+	out.rfx = rfx.s;
+	out.ref = encode_cell(rfx.s);
+	data.l += 16; /*var guid = parse_GUID(data); */
+	return out;
+}
+
+/* [MS-XLSB] 2.4.324 BrtCommentAuthor */
+var parse_BrtCommentAuthor = parse_XLWideString;
+
+/* [MS-XLSB] 2.4.325 BrtCommentText */
+var parse_BrtCommentText = parse_RichStr;
+
+/* [MS-XLSB] 2.1.7.8 Comments */
+function parse_comments_bin(data, opts) {
+	var out = [];
+	var authors = [];
+	var c = {};
+	var pass = false;
+	recordhopper(data, function hopper_cmnt(val, R, RT) {
+		switch(R.n) {
+			case 'BrtCommentAuthor': authors.push(val); break;
+			case 'BrtBeginComment': c = val; break;
+			case 'BrtCommentText': c.t = val.t; c.h = val.h; c.r = val.r; break;
+			case 'BrtEndComment':
+				c.author = authors[c.iauthor];
+				delete c.iauthor;
+				if(opts.sheetRows && opts.sheetRows <= c.rfx.r) break;
+				delete c.rfx; out.push(c); break;
+			case 'BrtBeginComments': break;
+			case 'BrtEndComments': break;
+			case 'BrtBeginCommentAuthors': break;
+			case 'BrtEndCommentAuthors': break;
+			case 'BrtBeginCommentList': break;
+			case 'BrtEndCommentList': break;
+			default: if(!pass || opts.WTF) throw new Error("Unexpected record " + RT + " " + R.n);
+		}
+	});
+	return out;
+}
+
+function write_comments_bin(data, opts) { }
+/* [MS-XLSB] 2.5.97.4 CellParsedFormula TODO: use similar logic to js-xls */
+function parse_CellParsedFormula(data, length) {
+	var cce = data.read_shift(4);
+	return parsenoop(data, length-4);
+}
+var strs = {}; // shared strings
+var _ssfopts = {}; // spreadsheet formatting options
+
+RELS.WS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet";
+
+function get_sst_id(sst, str) {
+	for(var i = 0, len = sst.length; i < len; ++i) if(sst[i].t === str) { sst.Count ++; return i; }
+	sst[len] = {t:str}; sst.Count ++; sst.Unique ++; return len;
+}
+
+function get_cell_style(styles, cell, opts) {
+	var z = opts.revssf[cell.z != null ? cell.z : "General"];
+	for(var i = 0, len = styles.length; i != len; ++i) if(styles[i].numFmtId === z) return i;
+	styles[len] = {
+		numFmtId:z,
+		fontId:0,
+		fillId:0,
+		borderId:0,
+		xfId:0,
+		applyNumberFormat:1
+	};
+	return len;
+}
+
+function safe_format(p, fmtid, fillid, opts) {
+	try {
+		if(fmtid === 0) {
+			if(p.t === 'n') {
+				if((p.v|0) === p.v) p.w = SSF._general_int(p.v,_ssfopts);
+				else p.w = SSF._general_num(p.v,_ssfopts);
+			}
+			else if(p.v === undefined) return "";
+			else p.w = SSF._general(p.v,_ssfopts);
+		}
+		else p.w = SSF.format(fmtid,p.v,_ssfopts);
+		if(opts.cellNF) p.z = SSF._table[fmtid];
+	} catch(e) { if(opts.WTF) throw e; }
+	if(fillid) try {
+		p.s = styles.Fills[fillid];
+		if (p.s.fgColor && p.s.fgColor.theme) {
+			p.s.fgColor.rgb = rgb_tint(themes.themeElements.clrScheme[p.s.fgColor.theme].rgb, p.s.fgColor.tint || 0);
+			if(opts.WTF) p.s.fgColor.raw_rgb = themes.themeElements.clrScheme[p.s.fgColor.theme].rgb;
+		}
+		if (p.s.bgColor && p.s.bgColor.theme) {
+			p.s.bgColor.rgb = rgb_tint(themes.themeElements.clrScheme[p.s.bgColor.theme].rgb, p.s.bgColor.tint || 0);
+			if(opts.WTF) p.s.bgColor.raw_rgb = themes.themeElements.clrScheme[p.s.bgColor.theme].rgb;
+		}
+	} catch(e) { if(opts.WTF) throw e; }
+}
+function parse_ws_xml_dim(ws, s) {
+	var d = safe_decode_range(s);
+	if(d.s.r<=d.e.r && d.s.c<=d.e.c && d.s.r>=0 && d.s.c>=0) ws["!ref"] = encode_range(d);
+}
+var mergecregex = /<mergeCell ref="[A-Z0-9:]+"\s*\/>/g;
+var sheetdataregex = /<(?:\w+:)?sheetData>([^\u2603]*)<\/(?:\w+:)?sheetData>/;
+var hlinkregex = /<hyperlink[^>]*\/>/g;
+var dimregex = /"(\w*:\w*)"/;
+var colregex = /<col[^>]*\/>/g;
+/* 18.3 Worksheets */
+function parse_ws_xml(data, opts, rels) {
+	if(!data) return data;
+	/* 18.3.1.99 worksheet CT_Worksheet */
+	var s = {};
+
+	/* 18.3.1.35 dimension CT_SheetDimension ? */
+	var ridx = data.indexOf("<dimension");
+	if(ridx > 0) {
+		var ref = data.substr(ridx,50).match(dimregex);
+		if(ref != null) parse_ws_xml_dim(s, ref[1]);
+	}
+
+	/* 18.3.1.55 mergeCells CT_MergeCells */
+	var mergecells = [];
+	if(data.indexOf("</mergeCells>")!==-1) {
+		var merges = data.match(mergecregex);
+		for(ridx = 0; ridx != merges.length; ++ridx)
+			mergecells[ridx] = safe_decode_range(merges[ridx].substr(merges[ridx].indexOf("\"")+1));
+	}
+
+	/* 18.3.1.17 cols CT_Cols */
+	var columns = [];
+	if(opts.cellStyles && data.indexOf("</cols>")!==-1) {
+		/* 18.3.1.13 col CT_Col */
+		var cols = data.match(colregex);
+		parse_ws_xml_cols(columns, cols);
+	}
+
+	var refguess = {s: {r:1000000, c:1000000}, e: {r:0, c:0} };
+
+	/* 18.3.1.80 sheetData CT_SheetData ? */
+	var mtch=data.match(sheetdataregex);
+	if(mtch) parse_ws_xml_data(mtch[1], s, opts, refguess);
+
+	/* 18.3.1.48 hyperlinks CT_Hyperlinks */
+	if(data.indexOf("</hyperlinks>")!==-1) parse_ws_xml_hlinks(s, data.match(hlinkregex), rels);
+
+	if(!s["!ref"] && refguess.e.c >= refguess.s.c && refguess.e.r >= refguess.s.r) s["!ref"] = encode_range(refguess);
+	if(opts.sheetRows > 0 && s["!ref"]) {
+		var tmpref = safe_decode_range(s["!ref"]);
+		if(opts.sheetRows < +tmpref.e.r) {
+			tmpref.e.r = opts.sheetRows - 1;
+			if(tmpref.e.r > refguess.e.r) tmpref.e.r = refguess.e.r;
+			if(tmpref.e.r < tmpref.s.r) tmpref.s.r = tmpref.e.r;
+			if(tmpref.e.c > refguess.e.c) tmpref.e.c = refguess.e.c;
+			if(tmpref.e.c < tmpref.s.c) tmpref.s.c = tmpref.e.c;
+			s["!fullref"] = s["!ref"];
+			s["!ref"] = encode_range(tmpref);
+		}
+	}
+	if(mergecells.length > 0) s["!merges"] = mergecells;
+	if(columns.length > 0) s["!cols"] = columns;
+	return s;
+}
+
+
+function parse_ws_xml_hlinks(s, data, rels) {
+	for(var i = 0; i != data.length; ++i) {
+		var val = parsexmltag(data[i], true);
+		if(!val.ref) return;
+		var rel = rels['!id'][val.id];
+		if(rel) {
+			val.Target = rel.Target;
+			if(val.location) val.Target += "#"+val.location;
+			val.Rel = rel;
+		}
+		var rng = safe_decode_range(val.ref);
+		for(var R=rng.s.r;R<=rng.e.r;++R) for(var C=rng.s.c;C<=rng.e.c;++C) {
+			var addr = encode_cell({c:C,r:R});
+			if(!s[addr]) s[addr] = {t:"str",v:undefined};
+			s[addr].l = val;
+		}
+	}
+}
+
+function parse_ws_xml_cols(columns, cols) {
+	var seencol = false;
+	for(var coli = 0; coli != cols.length; ++coli) {
+		var coll = parsexmltag(cols[coli], true);
+		var colm=parseInt(coll.min, 10)-1, colM=parseInt(coll.max,10)-1;
+		delete coll.min; delete coll.max;
+		if(!seencol && coll.width) { seencol = true; find_mdw(+coll.width, coll); }
+		if(coll.width) {
+			coll.wpx = width2px(+coll.width);
+			coll.wch = px2char(coll.wpx);
+			coll.MDW = MDW;
+		}
+		while(colm <= colM) columns[colm++] = coll;
+	}
+}
+
+function write_ws_xml_cols(ws, cols) {
+	var o = ["<cols>"], col, width;
+	for(var i = 0; i != cols.length; ++i) {
+		if(!(col = cols[i])) continue;
+		var p = {min:i+1,max:i+1};
+		/* wch (chars), wpx (pixels) */
+		width = -1;
+		if(col.wpx) width = px2char(col.wpx);
+		else if(col.wch) width = col.wch;
+		if(width > -1) { p.width = char2width(width); p.customWidth= 1; }
+		o[o.length] = (writextag('col', null, p));
+	}
+	o[o.length] = "</cols>";
+	return o.join("");
+}
+
+function write_ws_xml_cell(cell, ref, ws, opts, idx, wb) {
+	if(cell.v === undefined) return "";
+	var vv = "";
+	switch(cell.t) {
+		case 'b': vv = cell.v ? "1" : "0"; break;
+		case 'n': case 'e': vv = ''+cell.v; break;
+		default: vv = cell.v; break;
+	}
+	var v = writetag('v', escapexml(vv)), o = {r:ref};
+	/* TODO: cell style */
+	var os = get_cell_style(opts.cellXfs, cell, opts);
+	if(os !== 0) o.s = os;
+	switch(cell.t) {
+		case 'n': break;
+		case 'b': o.t = "b"; break;
+		case 'e': o.t = "e"; break;
+		default:
+			if(opts.bookSST) {
+				v = writetag('v', ''+get_sst_id(opts.Strings, cell.v));
+				o.t = "s"; break;
+			}
+			o.t = "str"; break;
+	}
+	return writextag('c', v, o);
+}
+
+var parse_ws_xml_data = (function parse_ws_xml_data_factory() {
+	var cellregex = /<(?:\w+:)?c /, rowregex = /<\/(?:\w+:)?row>/;
+	var rregex = /r=["']([^"']*)["']/, isregex = /<is>([\S\s]*?)<\/is>/;
+	var match_v = matchtag("v"), match_f = matchtag("f");
+
+return function parse_ws_xml_data(sdata, s, opts, guess) {
+	var ri = 0, x = "", cells = [], cref = [], idx = 0, i=0, cc=0, d="", p;
+	var tag;
+	var sstr;
+	var fmtid = 0, fillid = 0, do_format = Array.isArray(styles.CellXf), cf;
+	for(var marr = sdata.split(rowregex), mt = 0, marrlen = marr.length; mt != marrlen; ++mt) {
+		x = marr[mt].trim();
+		var xlen = x.length;
+		if(xlen === 0) continue;
+
+		/* 18.3.1.73 row CT_Row */
+		for(ri = 0; ri < xlen; ++ri) if(x.charCodeAt(ri) === 62) break; ++ri;
+		tag = parsexmltag(x.substr(0,ri), true);
+		var tagr = parseInt(tag.r, 10);
+		if(opts.sheetRows && opts.sheetRows < tagr) continue;
+		if(guess.s.r > tagr - 1) guess.s.r = tagr - 1;
+		if(guess.e.r < tagr - 1) guess.e.r = tagr - 1;
+
+		/* 18.3.1.4 c CT_Cell */
+		cells = x.substr(ri).split(cellregex);
+		for(ri = 1, cellen = cells.length; ri != cellen; ++ri) {
+			x = cells[ri].trim();
+			if(x.length === 0) continue;
+			cref = x.match(rregex); idx = ri; i=0; cc=0;
+			x = "<c " + x;
+			if(cref !== null && cref.length === 2) {
+				idx = 0; d=cref[1];
+				for(i=0; i != d.length; ++i) {
+					if((cc=d.charCodeAt(i)-64) < 1 || cc > 26) break;
+					idx = 26*idx + cc;
+				}
+				--idx;
+			}
+
+			for(i = 0; i != x.length; ++i) if(x.charCodeAt(i) === 62) break; ++i;
+			tag = parsexmltag(x.substr(0,i), true);
+			d = x.substr(i);
+			p = {t:""};
+
+			if((cref=d.match(match_v))!== null) p.v=unescapexml(cref[1]);
+			if(opts.cellFormula && (cref=d.match(match_f))!== null) p.f=unescapexml(cref[1]);
+
+			/* SCHEMA IS ACTUALLY INCORRECT HERE.  IF A CELL HAS NO T, EMIT "" */
+			if(tag.t === undefined && p.v === undefined) {
+				if(!opts.sheetStubs) continue;
+				p.t = "str";
+			}
+			else p.t = tag.t || "n";
+			if(guess.s.c > idx) guess.s.c = idx;
+			if(guess.e.c < idx) guess.e.c = idx;
+			/* 18.18.11 t ST_CellType */
+			switch(p.t) {
+				case 'n': p.v = parseFloat(p.v); break;
+				case 's':
+					sstr = strs[parseInt(p.v, 10)];
+					p.v = sstr.t;
+					p.r = sstr.r;
+					if(opts.cellHTML) p.h = sstr.h;
+					break;
+				case 'str': if(p.v != null) p.v = utf8read(p.v); else p.v = ""; break;
+				case 'inlineStr':
+					cref = d.match(isregex);
+					p.t = 'str';
+					if(cref !== null) { sstr = parse_si(cref[1]); p.v = sstr.t; } else p.v = "";
+					break; // inline string
+				case 'b': p.v = parsexmlbool(p.v); break;
+				case 'd':
+					p.v = datenum(p.v);
+					p.t = 'n';
+					break;
+				/* in case of error, stick value in .raw */
+				case 'e': p.raw = RBErr[p.v]; break;
+			}
+			/* formatting */
+			fmtid = fillid = 0;
+			if(do_format && tag.s !== undefined) {
+				cf = styles.CellXf[tag.s];
+				if(cf != null) {
+					if(cf.numFmtId != null) fmtid = cf.numFmtId;
+					if(opts.cellStyles && cf.fillId != null) fillid = cf.fillId;
+				}
+			}
+			safe_format(p, fmtid, fillid, opts);
+			s[tag.r] = p;
+		}
+	}
+}; })();
+
+function write_ws_xml_data(ws, opts, idx, wb) {
+	var o = [], r = [], range = safe_decode_range(ws['!ref']), cell, ref, rr = "", cols = [], R, C;
+	for(C = range.s.c; C <= range.e.c; ++C) cols[C] = encode_col(C);
+	for(R = range.s.r; R <= range.e.r; ++R) {
+		r = [];
+		rr = encode_row(R);
+		for(C = range.s.c; C <= range.e.c; ++C) {
+			ref = cols[C] + rr;
+			if(ws[ref] === undefined) continue;
+			if((cell = write_ws_xml_cell(ws[ref], ref, ws, opts, idx, wb)) != null) r.push(cell);
+		}
+		if(r.length > 0) o[o.length] = (writextag('row', r.join(""), {r:rr}));
+	}
+	return o.join("");
+}
+
+var WS_XML_ROOT = writextag('worksheet', null, {
+	'xmlns': XMLNS.main[0],
+	'xmlns:r': XMLNS.r
+});
+
+function write_ws_xml(idx, opts, wb) {
+	var o = [XML_HEADER, WS_XML_ROOT];
+	var s = wb.SheetNames[idx], sidx = 0, rdata = "";
+	var ws = wb.Sheets[s];
+	if(ws === undefined) ws = {};
+	var ref = ws['!ref']; if(ref === undefined) ref = 'A1';
+	o[o.length] = (writextag('dimension', null, {'ref': ref}));
+
+	if(ws['!cols'] !== undefined && ws['!cols'].length > 0) o[o.length] = (write_ws_xml_cols(ws, ws['!cols']));
+	o[sidx = o.length] = '<sheetData/>';
+	if(ws['!ref'] !== undefined) {
+		rdata = write_ws_xml_data(ws, opts, idx, wb);
+		if(rdata.length > 0) o[o.length] = (rdata);
+	}
+	if(o.length>sidx+1) { o[o.length] = ('</sheetData>'); o[sidx]=o[sidx].replace("/>",">"); }
+
+	if(o.length>2) { o[o.length] = ('</worksheet>'); o[1]=o[1].replace("/>",">"); }
+	return o.join("");
+}
+
+/* [MS-XLSB] 2.4.718 BrtRowHdr */
+function parse_BrtRowHdr(data, length) {
+	var z = [];
+	z.r = data.read_shift(4);
+	data.l += length-4;
+	return z;
+}
+
+/* [MS-XLSB] 2.4.812 BrtWsDim */
+var parse_BrtWsDim = parse_UncheckedRfX;
+var write_BrtWsDim = write_UncheckedRfX;
+
+/* [MS-XLSB] 2.4.815 BrtWsProp */
+function parse_BrtWsProp(data, length) {
+	var z = {};
+	/* TODO: pull flags */
+	data.l += 19;
+	z.name = parse_CodeName(data, length - 19);
+	return z;
+}
+
+/* [MS-XLSB] 2.4.303 BrtCellBlank */
+function parse_BrtCellBlank(data, length) {
+	var cell = parse_Cell(data);
+	return [cell];
+}
+function write_BrtCellBlank(cell, val, o) {
+	if(o == null) o = new_buf(8);
+	return write_Cell(val, o);
+}
+
+
+/* [MS-XLSB] 2.4.304 BrtCellBool */
+function parse_BrtCellBool(data, length) {
+	var cell = parse_Cell(data);
+	var fBool = data.read_shift(1);
+	return [cell, fBool, 'b'];
+}
+
+/* [MS-XLSB] 2.4.305 BrtCellError */
+function parse_BrtCellError(data, length) {
+	var cell = parse_Cell(data);
+	var fBool = data.read_shift(1);
+	return [cell, fBool, 'e'];
+}
+
+/* [MS-XLSB] 2.4.308 BrtCellIsst */
+function parse_BrtCellIsst(data, length) {
+	var cell = parse_Cell(data);
+	var isst = data.read_shift(4);
+	return [cell, isst, 's'];
+}
+
+/* [MS-XLSB] 2.4.310 BrtCellReal */
+function parse_BrtCellReal(data, length) {
+	var cell = parse_Cell(data);
+	var value = parse_Xnum(data);
+	return [cell, value, 'n'];
+}
+
+/* [MS-XLSB] 2.4.311 BrtCellRk */
+function parse_BrtCellRk(data, length) {
+	var cell = parse_Cell(data);
+	var value = parse_RkNumber(data);
+	return [cell, value, 'n'];
+}
+
+/* [MS-XLSB] 2.4.314 BrtCellSt */
+function parse_BrtCellSt(data, length) {
+	var cell = parse_Cell(data);
+	var value = parse_XLWideString(data);
+	return [cell, value, 'str'];
+}
+
+/* [MS-XLSB] 2.4.647 BrtFmlaBool */
+function parse_BrtFmlaBool(data, length, opts) {
+	var cell = parse_Cell(data);
+	var value = data.read_shift(1);
+	var o = [cell, value, 'b'];
+	if(opts.cellFormula) {
+		var formula = parse_CellParsedFormula(data, length-9);
+		o[3] = ""; /* TODO */
+	}
+	else data.l += length-9;
+	return o;
+}
+
+/* [MS-XLSB] 2.4.648 BrtFmlaError */
+function parse_BrtFmlaError(data, length, opts) {
+	var cell = parse_Cell(data);
+	var value = data.read_shift(1);
+	var o = [cell, value, 'e'];
+	if(opts.cellFormula) {
+		var formula = parse_CellParsedFormula(data, length-9);
+		o[3] = ""; /* TODO */
+	}
+	else data.l += length-9;
+	return o;
+}
+
+/* [MS-XLSB] 2.4.649 BrtFmlaNum */
+function parse_BrtFmlaNum(data, length, opts) {
+	var cell = parse_Cell(data);
+	var value = parse_Xnum(data);
+	var o = [cell, value, 'n'];
+	if(opts.cellFormula) {
+		var formula = parse_CellParsedFormula(data, length - 16);
+		o[3] = ""; /* TODO */
+	}
+	else data.l += length-16;
+	return o;
+}
+
+/* [MS-XLSB] 2.4.650 BrtFmlaString */
+function parse_BrtFmlaString(data, length, opts) {
+	var start = data.l;
+	var cell = parse_Cell(data);
+	var value = parse_XLWideString(data);
+	var o = [cell, value, 'str'];
+	if(opts.cellFormula) {
+		var formula = parse_CellParsedFormula(data, start + length - data.l);
+	}
+	else data.l = start + length;
+	return o;
+}
+
+/* [MS-XLSB] 2.4.676 BrtMergeCell */
+var parse_BrtMergeCell = parse_UncheckedRfX;
+
+/* [MS-XLSB] 2.4.656 BrtHLink */
+function parse_BrtHLink(data, length, opts) {
+	var end = data.l + length;
+	var rfx = parse_UncheckedRfX(data, 16);
+	var relId = parse_XLNullableWideString(data);
+	var loc = parse_XLWideString(data);
+	var tooltip = parse_XLWideString(data);
+	var display = parse_XLWideString(data);
+	data.l = end;
+	return {rfx:rfx, relId:relId, loc:loc, tooltip:tooltip, display:display};
+}
+
+/* [MS-XLSB] 2.1.7.61 Worksheet */
+function parse_ws_bin(data, opts, rels) {
+	if(!data) return data;
+	if(!rels) rels = {'!id':{}};
+	var s = {};
+
+	var ref;
+	var refguess = {s: {r:1000000, c:1000000}, e: {r:0, c:0} };
+
+	var pass = false, end = false;
+	var row, p, cf, R, C, addr, sstr, rr;
+	var mergecells = [];
+	recordhopper(data, function ws_parse(val, R) {
+		if(end) return;
+		switch(R.n) {
+			case 'BrtWsDim': ref = val; break;
+			case 'BrtRowHdr':
+				row = val;
+				if(opts.sheetRows && opts.sheetRows <= row.r) end=true;
+				rr = encode_row(row.r);
+				break;
+
+			case 'BrtFmlaBool':
+			case 'BrtFmlaError':
+			case 'BrtFmlaNum':
+			case 'BrtFmlaString':
+			case 'BrtCellBool':
+			case 'BrtCellError':
+			case 'BrtCellIsst':
+			case 'BrtCellReal':
+			case 'BrtCellRk':
+			case 'BrtCellSt':
+				p = {t:val[2]};
+				switch(val[2]) {
+					case 'n': p.v = val[1]; break;
+					case 's': sstr = strs[val[1]]; p.v = sstr.t; p.r = sstr.r; break;
+					case 'b': p.v = val[1] ? true : false; break;
+					case 'e': p.raw = val[1]; p.v = BErr[p.raw]; break;
+					case 'str': p.v = utf8read(val[1]); break;
+				}
+				if(opts.cellFormula && val.length > 3) p.f = val[3];
+				if((cf = styles.CellXf[val[0].iStyleRef])) safe_format(p,cf.ifmt,null,opts);
+				s[encode_col(C=val[0].c) + rr] = p;
+				if(refguess.s.r > row.r) refguess.s.r = row.r;
+				if(refguess.s.c > C) refguess.s.c = C;
+				if(refguess.e.r < row.r) refguess.e.r = row.r;
+				if(refguess.e.c < C) refguess.e.c = C;
+				break;
+
+			case 'BrtCellBlank': if(!opts.sheetStubs) break;
+				p = {t:'str',v:undefined};
+				s[encode_col(C=val[0].c) + rr] = p;
+				if(refguess.s.r > row.r) refguess.s.r = row.r;
+				if(refguess.s.c > C) refguess.s.c = C;
+				if(refguess.e.r < row.r) refguess.e.r = row.r;
+				if(refguess.e.c < C) refguess.e.c = C;
+				break;
+
+			/* Merge Cells */
+			case 'BrtBeginMergeCells': break;
+			case 'BrtEndMergeCells': break;
+			case 'BrtMergeCell': mergecells.push(val); break;
+
+			case 'BrtHLink':
+				var rel = rels['!id'][val.relId];
+				if(rel) {
+					val.Target = rel.Target;
+					if(val.loc) val.Target += "#"+val.loc;
+					val.Rel = rel;
+				}
+				for(R=val.rfx.s.r;R<=val.rfx.e.r;++R) for(C=val.rfx.s.c;C<=val.rfx.e.c;++C) {
+					addr = encode_cell({c:C,r:R});
+					if(!s[addr]) s[addr] = {t:"str",v:undefined};
+					s[addr].l = val;
+				}
+				break;
+
+			case 'BrtArrFmla': break; // TODO
+			case 'BrtShrFmla': break; // TODO
+			case 'BrtBeginSheet': break;
+			case 'BrtWsProp': break; // TODO
+			case 'BrtSheetCalcProp': break; // TODO
+			case 'BrtBeginWsViews': break; // TODO
+			case 'BrtBeginWsView': break; // TODO
+			case 'BrtPane': break; // TODO
+			case 'BrtSel': break; // TODO
+			case 'BrtEndWsView': break; // TODO
+			case 'BrtEndWsViews': break; // TODO
+			case 'BrtACBegin': break; // TODO
+			case 'BrtRwDescent': break; // TODO
+			case 'BrtACEnd': break; // TODO
+			case 'BrtWsFmtInfoEx14': break; // TODO
+			case 'BrtWsFmtInfo': break; // TODO
+			case 'BrtBeginColInfos': break; // TODO
+			case 'BrtColInfo': break; // TODO
+			case 'BrtEndColInfos': break; // TODO
+			case 'BrtBeginSheetData': break; // TODO
+			case 'BrtEndSheetData': break; // TODO
+			case 'BrtSheetProtection': break; // TODO
+			case 'BrtPrintOptions': break; // TODO
+			case 'BrtMargins': break; // TODO
+			case 'BrtPageSetup': break; // TODO
+			case 'BrtFRTBegin': pass = true; break;
+			case 'BrtFRTEnd': pass = false; break;
+			case 'BrtEndSheet': break; // TODO
+			case 'BrtDrawing': break; // TODO
+			case 'BrtLegacyDrawing': break; // TODO
+			case 'BrtLegacyDrawingHF': break; // TODO
+			case 'BrtPhoneticInfo': break; // TODO
+			case 'BrtBeginHeaderFooter': break; // TODO
+			case 'BrtEndHeaderFooter': break; // TODO
+			case 'BrtBrk': break; // TODO
+			case 'BrtBeginRwBrk': break; // TODO
+			case 'BrtEndRwBrk': break; // TODO
+			case 'BrtBeginColBrk': break; // TODO
+			case 'BrtEndColBrk': break; // TODO
+			case 'BrtBeginUserShViews': break; // TODO
+			case 'BrtBeginUserShView': break; // TODO
+			case 'BrtEndUserShView': break; // TODO
+			case 'BrtEndUserShViews': break; // TODO
+			case 'BrtBkHim': break; // TODO
+			case 'BrtBeginOleObjects': break; // TODO
+			case 'BrtOleObject': break; // TODO
+			case 'BrtEndOleObjects': break; // TODO
+			case 'BrtBeginListParts': break; // TODO
+			case 'BrtListPart': break; // TODO
+			case 'BrtEndListParts': break; // TODO
+			case 'BrtBeginSortState': break; // TODO
+			case 'BrtBeginSortCond': break; // TODO
+			case 'BrtEndSortCond': break; // TODO
+			case 'BrtEndSortState': break; // TODO
+			case 'BrtBeginConditionalFormatting': break; // TODO
+			case 'BrtEndConditionalFormatting': break; // TODO
+			case 'BrtBeginCFRule': break; // TODO
+			case 'BrtEndCFRule': break; // TODO
+			case 'BrtBeginDVals': break; // TODO
+			case 'BrtDVal': break; // TODO
+			case 'BrtEndDVals': break; // TODO
+			case 'BrtRangeProtection': break; // TODO
+			case 'BrtBeginDCon': break; // TODO
+			case 'BrtEndDCon': break; // TODO
+			case 'BrtBeginDRefs': break;
+			case 'BrtDRef': break;
+			case 'BrtEndDRefs': break;
+
+			/* ActiveX */
+			case 'BrtBeginActiveXControls': break;
+			case 'BrtActiveX': break;
+			case 'BrtEndActiveXControls': break;
+
+			/* AutoFilter */
+			case 'BrtBeginAFilter': break;
+			case 'BrtEndAFilter': break;
+			case 'BrtBeginFilterColumn': break;
+			case 'BrtBeginFilters': break;
+			case 'BrtFilter': break;
+			case 'BrtEndFilters': break;
+			case 'BrtEndFilterColumn': break;
+			case 'BrtDynamicFilter': break;
+			case 'BrtTop10Filter': break;
+			case 'BrtBeginCustomFilters': break;
+			case 'BrtCustomFilter': break;
+			case 'BrtEndCustomFilters': break;
+
+			/* Smart Tags */
+			case 'BrtBeginSmartTags': break;
+			case 'BrtBeginCellSmartTags': break;
+			case 'BrtBeginCellSmartTag': break;
+			case 'BrtCellSmartTagProperty': break;
+			case 'BrtEndCellSmartTag': break;
+			case 'BrtEndCellSmartTags': break;
+			case 'BrtEndSmartTags': break;
+
+			/* Cell Watch */
+			case 'BrtBeginCellWatches': break;
+			case 'BrtCellWatch': break;
+			case 'BrtEndCellWatches': break;
+
+			/* Table */
+			case 'BrtTable': break;
+
+			/* Ignore Cell Errors */
+			case 'BrtBeginCellIgnoreECs': break;
+			case 'BrtCellIgnoreEC': break;
+			case 'BrtEndCellIgnoreECs': break;
+
+			default: if(!pass || opts.WTF) throw new Error("Unexpected record " + R.n);
+		}
+	}, opts);
+	if(!s["!ref"] && (refguess.s.r < 1000000 || ref.e.r > 0 || ref.e.c > 0 || ref.s.r > 0 || ref.s.c > 0)) s["!ref"] = encode_range(ref);
+	if(opts.sheetRows && s["!ref"]) {
+		var tmpref = safe_decode_range(s["!ref"]);
+		if(opts.sheetRows < +tmpref.e.r) {
+			tmpref.e.r = opts.sheetRows - 1;
+			if(tmpref.e.r > refguess.e.r) tmpref.e.r = refguess.e.r;
+			if(tmpref.e.r < tmpref.s.r) tmpref.s.r = tmpref.e.r;
+			if(tmpref.e.c > refguess.e.c) tmpref.e.c = refguess.e.c;
+			if(tmpref.e.c < tmpref.s.c) tmpref.s.c = tmpref.e.c;
+			s["!fullref"] = s["!ref"];
+			s["!ref"] = encode_range(tmpref);
+		}
+	}
+	if(mergecells.length > 0) s["!merges"] = mergecells;
+	return s;
+}
+
+/* TODO: something useful -- this is a stub */
+function write_ws_bin_cell(ba, cell, R, C, opts) {
+	if(cell.v === undefined) return "";
+	var vv = "";
+	switch(cell.t) {
+		case 'b': vv = cell.v ? "1" : "0"; break;
+		case 'n': case 'e': vv = ''+cell.v; break;
+		default: vv = cell.v; break;
+	}
+	var o = {r:R, c:C};
+	/* TODO: cell style */
+	o.s = get_cell_style(opts.cellXfs, cell, opts);
+	switch(cell.t) {
+		case 's': case 'str':
+			if(opts.bookSST) {
+				vv = get_sst_id(opts.Strings, cell.v);
+				o.t = "s"; break;
+			}
+			o.t = "str"; break;
+		case 'n': break;
+		case 'b': o.t = "b"; break;
+		case 'e': o.t = "e"; break;
+	}
+	write_record(ba, "BrtCellBlank", write_BrtCellBlank(cell, o));
+}
+
+function write_CELLTABLE(ba, ws, idx, opts, wb) {
+	var range = safe_decode_range(ws['!ref'] || "A1"), ref, rr = "", cols = [];
+	write_record(ba, 'BrtBeginSheetData');
+	for(var R = range.s.r; R <= range.e.r; ++R) {
+		rr = encode_row(R);
+		/* [ACCELLTABLE] */
+		/* BrtRowHdr */
+		for(var C = range.s.c; C <= range.e.c; ++C) {
+			/* *16384CELL */
+			if(R === range.s.r) cols[C] = encode_col(C);
+			ref = cols[C] + rr;
+			if(!ws[ref]) continue;
+			/* write cell */
+			write_ws_bin_cell(ba, ws[ref], R, C, opts);
+		}
+	}
+	write_record(ba, 'BrtEndSheetData');
+}
+
+function write_ws_bin(idx, opts, wb) {
+	var ba = buf_array();
+	var s = wb.SheetNames[idx], ws = wb.Sheets[s] || {};
+	var r = safe_decode_range(ws['!ref'] || "A1");
+	write_record(ba, "BrtBeginSheet");
+	/* [BrtWsProp] */
+	write_record(ba, "BrtWsDim", write_BrtWsDim(r));
+	/* [WSVIEWS2] */
+	/* [WSFMTINFO] */
+	/* *COLINFOS */
+	write_CELLTABLE(ba, ws, idx, opts, wb);
+	/* [BrtSheetCalcProp] */
+	/* [[BrtSheetProtectionIso] BrtSheetProtection] */
+	/* *([BrtRangeProtectionIso] BrtRangeProtection) */
+	/* [SCENMAN] */
+	/* [AUTOFILTER] */
+	/* [SORTSTATE] */
+	/* [DCON] */
+	/* [USERSHVIEWS] */
+	/* [MERGECELLS] */
+	/* [BrtPhoneticInfo] */
+	/* *CONDITIONALFORMATTING */
+	/* [DVALS] */
+	/* *BrtHLink */
+	/* [BrtPrintOptions] */
+	/* [BrtMargins] */
+	/* [BrtPageSetup] */
+	/* [HEADERFOOTER] */
+	/* [RWBRK] */
+	/* [COLBRK] */
+	/* *BrtBigName */
+	/* [CELLWATCHES] */
+	/* [IGNOREECS] */
+	/* [SMARTTAGS] */
+	/* [BrtDrawing] */
+	/* [BrtLegacyDrawing] */
+	/* [BrtLegacyDrawingHF] */
+	/* [BrtBkHim] */
+	/* [OLEOBJECTS] */
+	/* [ACTIVEXCONTROLS] */
+	/* [WEBPUBITEMS] */
+	/* [LISTPARTS] */
+	/* FRTWORKSHEET */
+	write_record(ba, "BrtEndSheet");
+	return ba.end();
+}
+/* 18.2.28 (CT_WorkbookProtection) Defaults */
+var WBPropsDef = [
+	['allowRefreshQuery', '0'],
+	['autoCompressPictures', '1'],
+	['backupFile', '0'],
+	['checkCompatibility', '0'],
+	['codeName', ''],
+	['date1904', '0'],
+	['dateCompatibility', '1'],
+	//['defaultThemeVersion', '0'],
+	['filterPrivacy', '0'],
+	['hidePivotFieldList', '0'],
+	['promptedSolutions', '0'],
+	['publishItems', '0'],
+	['refreshAllConnections', false],
+	['saveExternalLinkValues', '1'],
+	['showBorderUnselectedTables', '1'],
+	['showInkAnnotation', '1'],
+	['showObjects', 'all'],
+	['showPivotChartFilter', '0']
+	//['updateLinks', 'userSet']
+];
+
+/* 18.2.30 (CT_BookView) Defaults */
+var WBViewDef = [
+	['activeTab', '0'],
+	['autoFilterDateGrouping', '1'],
+	['firstSheet', '0'],
+	['minimized', '0'],
+	['showHorizontalScroll', '1'],
+	['showSheetTabs', '1'],
+	['showVerticalScroll', '1'],
+	['tabRatio', '600'],
+	['visibility', 'visible']
+	//window{Height,Width}, {x,y}Window
+];
+
+/* 18.2.19 (CT_Sheet) Defaults */
+var SheetDef = [
+	['state', 'visible']
+];
+
+/* 18.2.2  (CT_CalcPr) Defaults */
+var CalcPrDef = [
+	['calcCompleted', 'true'],
+	['calcMode', 'auto'],
+	['calcOnSave', 'true'],
+	['concurrentCalc', 'true'],
+	['fullCalcOnLoad', 'false'],
+	['fullPrecision', 'true'],
+	['iterate', 'false'],
+	['iterateCount', '100'],
+	['iterateDelta', '0.001'],
+	['refMode', 'A1']
+];
+
+/* 18.2.3 (CT_CustomWorkbookView) Defaults */
+var CustomWBViewDef = [
+	['autoUpdate', 'false'],
+	['changesSavedWin', 'false'],
+	['includeHiddenRowCol', 'true'],
+	['includePrintSettings', 'true'],
+	['maximized', 'false'],
+	['minimized', 'false'],
+	['onlySync', 'false'],
+	['personalView', 'false'],
+	['showComments', 'commIndicator'],
+	['showFormulaBar', 'true'],
+	['showHorizontalScroll', 'true'],
+	['showObjects', 'all'],
+	['showSheetTabs', 'true'],
+	['showStatusbar', 'true'],
+	['showVerticalScroll', 'true'],
+	['tabRatio', '600'],
+	['xWindow', '0'],
+	['yWindow', '0']
+];
+
+function push_defaults_array(target, defaults) {
+	for(var j = 0; j != target.length; ++j) { var w = target[j];
+		for(var i=0; i != defaults.length; ++i) { var z = defaults[i];
+			if(w[z[0]] == null) w[z[0]] = z[1];
+		}
+	}
+}
+function push_defaults(target, defaults) {
+	for(var i = 0; i != defaults.length; ++i) { var z = defaults[i];
+		if(target[z[0]] == null) target[z[0]] = z[1];
+	}
+}
+
+function parse_wb_defaults(wb) {
+	push_defaults(wb.WBProps, WBPropsDef);
+	push_defaults(wb.CalcPr, CalcPrDef);
+
+	push_defaults_array(wb.WBView, WBViewDef);
+	push_defaults_array(wb.Sheets, SheetDef);
+
+	_ssfopts.date1904 = parsexmlbool(wb.WBProps.date1904, 'date1904');
+}
+/* 18.2 Workbook */
+var wbnsregex = /<\w+:workbook/;
+function parse_wb_xml(data, opts) {
+	var wb = { AppVersion:{}, WBProps:{}, WBView:[], Sheets:[], CalcPr:{}, xmlns: "" };
+	var pass = false, xmlns = "xmlns";
+	data.match(tagregex).forEach(function xml_wb(x) {
+		var y = parsexmltag(x);
+		switch(strip_ns(y[0])) {
+			case '<?xml': break;
+
+			/* 18.2.27 workbook CT_Workbook 1 */
+			case '<workbook':
+				if(x.match(wbnsregex)) xmlns = "xmlns" + x.match(/<(\w+):/)[1];
+				wb.xmlns = y[xmlns];
+				break;
+			case '</workbook>': break;
+
+			/* 18.2.13 fileVersion CT_FileVersion ? */
+			case '<fileVersion': delete y[0]; wb.AppVersion = y; break;
+			case '<fileVersion/>': break;
+
+			/* 18.2.12 fileSharing CT_FileSharing ? */
+			case '<fileSharing': case '<fileSharing/>': break;
+
+			/* 18.2.28 workbookPr CT_WorkbookPr ? */
+			case '<workbookPr': delete y[0]; wb.WBProps = y; break;
+			case '<workbookPr/>': delete y[0]; wb.WBProps = y; break;
+
+			/* 18.2.29 workbookProtection CT_WorkbookProtection ? */
+			case '<workbookProtection': break;
+			case '<workbookProtection/>': break;
+
+			/* 18.2.1  bookViews CT_BookViews ? */
+			case '<bookViews>': case '</bookViews>': break;
+			/* 18.2.30   workbookView CT_BookView + */
+			case '<workbookView': delete y[0]; wb.WBView.push(y); break;
+
+			/* 18.2.20 sheets CT_Sheets 1 */
+			case '<sheets>': case '</sheets>': break; // aggregate sheet
+			/* 18.2.19   sheet CT_Sheet + */
+			case '<sheet': delete y[0]; y.name = utf8read(y.name); wb.Sheets.push(y); break;
+
+			/* 18.2.15 functionGroups CT_FunctionGroups ? */
+			case '<functionGroups': case '<functionGroups/>': break;
+			/* 18.2.14   functionGroup CT_FunctionGroup + */
+			case '<functionGroup': break;
+
+			/* 18.2.9  externalReferences CT_ExternalReferences ? */
+			case '<externalReferences': case '</externalReferences>': case '<externalReferences>': break;
+			/* 18.2.8    externalReference CT_ExternalReference + */
+			case '<externalReference': break;
+
+			/* 18.2.6  definedNames CT_DefinedNames ? */
+			case '<definedNames/>': break;
+			case '<definedNames>': case '<definedNames': pass=true; break;
+			case '</definedNames>': pass=false; break;
+			/* 18.2.5    definedName CT_DefinedName + */
+			case '<definedName': case '<definedName/>': case '</definedName>': break;
+
+			/* 18.2.2  calcPr CT_CalcPr ? */
+			case '<calcPr': delete y[0]; wb.CalcPr = y; break;
+			case '<calcPr/>': delete y[0]; wb.CalcPr = y; break;
+
+			/* 18.2.16 oleSize CT_OleSize ? (ref required) */
+			case '<oleSize': break;
+
+			/* 18.2.4  customWorkbookViews CT_CustomWorkbookViews ? */
+			case '<customWorkbookViews>': case '</customWorkbookViews>': case '<customWorkbookViews': break;
+			/* 18.2.3    customWorkbookView CT_CustomWorkbookView + */
+			case '<customWorkbookView': case '</customWorkbookView>': break;
+
+			/* 18.2.18 pivotCaches CT_PivotCaches ? */
+			case '<pivotCaches>': case '</pivotCaches>': case '<pivotCaches': break;
+			/* 18.2.17 pivotCache CT_PivotCache ? */
+			case '<pivotCache': break;
+
+			/* 18.2.21 smartTagPr CT_SmartTagPr ? */
+			case '<smartTagPr': case '<smartTagPr/>': break;
+
+			/* 18.2.23 smartTagTypes CT_SmartTagTypes ? */
+			case '<smartTagTypes': case '<smartTagTypes>': case '</smartTagTypes>': break;
+			/* 18.2.22   smartTagType CT_SmartTagType ? */
+			case '<smartTagType': break;
+
+			/* 18.2.24 webPublishing CT_WebPublishing ? */
+			case '<webPublishing': case '<webPublishing/>': break;
+
+			/* 18.2.11 fileRecoveryPr CT_FileRecoveryPr ? */
+			case '<fileRecoveryPr': case '<fileRecoveryPr/>': break;
+
+			/* 18.2.26 webPublishObjects CT_WebPublishObjects ? */
+			case '<webPublishObjects>': case '<webPublishObjects': case '</webPublishObjects>': break;
+			/* 18.2.25 webPublishObject CT_WebPublishObject ? */
+			case '<webPublishObject': break;
+
+			/* 18.2.10 extLst CT_ExtensionList ? */
+			case '<extLst>': case '</extLst>': case '<extLst/>': break;
+			/* 18.2.7    ext CT_Extension + */
+			case '<ext': pass=true; break; //TODO: check with versions of excel
+			case '</ext>': pass=false; break;
+
+			/* Others */
+			case '<ArchID': break;
+			case '<AlternateContent': pass=true; break;
+			case '</AlternateContent>': pass=false; break;
+
+			default: if(!pass && opts.WTF) throw 'unrecognized ' + y[0] + ' in workbook';
+		}
+	});
+	if(XMLNS.main.indexOf(wb.xmlns) === -1) throw new Error("Unknown Namespace: " + wb.xmlns);
+
+	parse_wb_defaults(wb);
+
+	return wb;
+}
+
+var WB_XML_ROOT = writextag('workbook', null, {
+	'xmlns': XMLNS.main[0],
+	//'xmlns:mx': XMLNS.mx,
+	//'xmlns:s': XMLNS.main[0],
+	'xmlns:r': XMLNS.r
+});
+
+function safe1904(wb) {
+	/* TODO: store date1904 somewhere else */
+	try { return parsexmlbool(wb.Workbook.WBProps.date1904) ? "true" : "false"; } catch(e) { return "false"; }
+}
+
+function write_wb_xml(wb, opts) {
+	var o = [XML_HEADER];
+	o[o.length] = WB_XML_ROOT;
+	o[o.length] = (writextag('workbookPr', null, {date1904:safe1904(wb)}));
+	o[o.length] = "<sheets>";
+	for(var i = 0; i != wb.SheetNames.length; ++i)
+		o[o.length] = (writextag('sheet',null,{name:wb.SheetNames[i].substr(0,31), sheetId:""+(i+1), "r:id":"rId"+(i+1)}));
+	o[o.length] = "</sheets>";
+	if(o.length>2){ o[o.length] = '</workbook>'; o[1]=o[1].replace("/>",">"); }
+	return o.join("");
+}
+/* [MS-XLSB] 2.4.301 BrtBundleSh */
+function parse_BrtBundleSh(data, length) {
+	var z = {};
+	z.hsState = data.read_shift(4); //ST_SheetState
+	z.iTabID = data.read_shift(4);
+	z.strRelID = parse_RelID(data,length-8);
+	z.name = parse_XLWideString(data);
+	return z;
+}
+function write_BrtBundleSh(data, o) {
+	if(!o) o = new_buf(127);
+	o.write_shift(4, data.hsState);
+	o.write_shift(4, data.iTabID);
+	write_RelID(data.strRelID, o);
+	write_XLWideString(data.name.substr(0,31), o);
+	return o;
+}
+
+/* [MS-XLSB] 2.4.807 BrtWbProp */
+function parse_BrtWbProp(data, length) {
+	data.read_shift(4);
+	var dwThemeVersion = data.read_shift(4);
+	var strName = (length > 8) ? parse_XLWideString(data) : "";
+	return [dwThemeVersion, strName];
+}
+function write_BrtWbProp(data, o) {
+	if(!o) o = new_buf(8);
+	o.write_shift(4, 0);
+	o.write_shift(4, 0);
+	return o;
+}
+
+function parse_BrtFRTArchID$(data, length) {
+	var o = {};
+	data.read_shift(4);
+	o.ArchID = data.read_shift(4);
+	data.l += length - 8;
+	return o;
+}
+
+/* [MS-XLSB] 2.1.7.60 Workbook */
+function parse_wb_bin(data, opts) {
+	var wb = { AppVersion:{}, WBProps:{}, WBView:[], Sheets:[], CalcPr:{}, xmlns: "" };
+	var pass = false, z;
+
+	recordhopper(data, function hopper_wb(val, R) {
+		switch(R.n) {
+			case 'BrtBundleSh': wb.Sheets.push(val); break;
+
+			case 'BrtBeginBook': break;
+			case 'BrtFileVersion': break;
+			case 'BrtWbProp': break;
+			case 'BrtACBegin': break;
+			case 'BrtAbsPath15': break;
+			case 'BrtACEnd': break;
+			case 'BrtWbFactoid': break;
+			/*case 'BrtBookProtectionIso': break;*/
+			case 'BrtBookProtection': break;
+			case 'BrtBeginBookViews': break;
+			case 'BrtBookView': break;
+			case 'BrtEndBookViews': break;
+			case 'BrtBeginBundleShs': break;
+			case 'BrtEndBundleShs': break;
+			case 'BrtBeginFnGroup': break;
+			case 'BrtEndFnGroup': break;
+			case 'BrtBeginExternals': break;
+			case 'BrtSupSelf': break;
+			case 'BrtSupBookSrc': break;
+			case 'BrtExternSheet': break;
+			case 'BrtEndExternals': break;
+			case 'BrtName': break;
+			case 'BrtCalcProp': break;
+			case 'BrtUserBookView': break;
+			case 'BrtBeginPivotCacheIDs': break;
+			case 'BrtBeginPivotCacheID': break;
+			case 'BrtEndPivotCacheID': break;
+			case 'BrtEndPivotCacheIDs': break;
+			case 'BrtWebOpt': break;
+			case 'BrtFileRecover': break;
+			case 'BrtFileSharing': break;
+			/*case 'BrtBeginWebPubItems': break;
+			case 'BrtBeginWebPubItem': break;
+			case 'BrtEndWebPubItem': break;
+			case 'BrtEndWebPubItems': break;*/
+
+			/* Smart Tags */
+			case 'BrtBeginSmartTagTypes': break;
+			case 'BrtSmartTagType': break;
+			case 'BrtEndSmartTagTypes': break;
+
+			case 'BrtFRTBegin': pass = true; break;
+			case 'BrtFRTArchID$': break;
+			case 'BrtWorkBookPr15': break;
+			case 'BrtFRTEnd': pass = false; break;
+			case 'BrtEndBook': break;
+			default: if(!pass || opts.WTF) throw new Error("Unexpected record " + R.n);
+		}
+	});
+
+	parse_wb_defaults(wb);
+
+	return wb;
+}
+
+/* [MS-XLSB] 2.1.7.60 Workbook */
+function write_BUNDLESHS(ba, wb, opts) {
+	write_record(ba, "BrtBeginBundleShs");
+	for(var idx = 0; idx != wb.SheetNames.length; ++idx) {
+		var d = { hsState: 0, iTabID: idx+1, strRelID: 'rId' + (idx+1), name: wb.SheetNames[idx] };
+		write_record(ba, "BrtBundleSh", write_BrtBundleSh(d));
+	}
+	write_record(ba, "BrtEndBundleShs");
+}
+
+/* [MS-XLSB] 2.4.643 BrtFileVersion */
+function write_BrtFileVersion(data, o) {
+	if(!o) o = new_buf(127);
+	for(var i = 0; i != 4; ++i) o.write_shift(4, 0);
+	write_XLWideString("SheetJS", o);
+	write_XLWideString(XLSX.version, o);
+	write_XLWideString(XLSX.version, o);
+	write_XLWideString("7262", o);
+	o.length = o.l;
+	return o;
+}
+
+/* [MS-XLSB] 2.1.7.60 Workbook */
+function write_BOOKVIEWS(ba, wb, opts) {
+	write_record(ba, "BrtBeginBookViews");
+	/* 1*(BrtBookView *FRT) */
+	write_record(ba, "BrtEndBookViews");
+}
+
+/* [MS-XLSB] 2.4.302 BrtCalcProp */
+function write_BrtCalcProp(data, o) {
+	if(!o) o = new_buf(26);
+	o.write_shift(4,0); /* force recalc */
+	o.write_shift(4,1);
+	o.write_shift(4,0);
+	write_Xnum(0, o);
+	o.write_shift(-4, 1023);
+	o.write_shift(1, 0x33);
+	o.write_shift(1, 0x00);
+	return o;
+}
+
+function write_BrtFileRecover(data, o) {
+	if(!o) o = new_buf(1);
+	o.write_shift(1,0);
+	return o;
+}
+
+/* [MS-XLSB] 2.1.7.60 Workbook */
+function write_wb_bin(wb, opts) {
+	var ba = buf_array();
+	write_record(ba, "BrtBeginBook");
+	write_record(ba, "BrtFileVersion", write_BrtFileVersion());
+	/* [[BrtFileSharingIso] BrtFileSharing] */
+	write_record(ba, "BrtWbProp", write_BrtWbProp());
+	/* [ACABSPATH] */
+	/* [[BrtBookProtectionIso] BrtBookProtection] */
+	write_BOOKVIEWS(ba, wb, opts);
+	write_BUNDLESHS(ba, wb, opts);
+	/* [FNGROUP] */
+	/* [EXTERNALS] */
+	/* *BrtName */
+	write_record(ba, "BrtCalcProp", write_BrtCalcProp());
+	/* [BrtOleSize] */
+	/* *(BrtUserBookView *FRT) */
+	/* [PIVOTCACHEIDS] */
+	/* [BrtWbFactoid] */
+	/* [SMARTTAGTYPES] */
+	/* [BrtWebOpt] */
+	write_record(ba, "BrtFileRecover", write_BrtFileRecover());
+	/* [WEBPUBITEMS] */
+	/* [CRERRS] */
+	/* FRTWORKBOOK */
+	write_record(ba, "BrtEndBook");
+
+	return ba.end();
+}
+function parse_wb(data, name, opts) {
+	return (name.substr(-4)===".bin" ? parse_wb_bin : parse_wb_xml)(data, opts);
+}
+
+function parse_ws(data, name, opts, rels) {
+	return (name.substr(-4)===".bin" ? parse_ws_bin : parse_ws_xml)(data, opts, rels);
+}
+
+function parse_sty(data, name, opts) {
+	return (name.substr(-4)===".bin" ? parse_sty_bin : parse_sty_xml)(data, opts);
+}
+
+function parse_theme(data, name, opts) {
+	return parse_theme_xml(data, opts);
+}
+
+function parse_sst(data, name, opts) {
+	return (name.substr(-4)===".bin" ? parse_sst_bin : parse_sst_xml)(data, opts);
+}
+
+function parse_cmnt(data, name, opts) {
+	return (name.substr(-4)===".bin" ? parse_comments_bin : parse_comments_xml)(data, opts);
+}
+
+function parse_cc(data, name, opts) {
+	return (name.substr(-4)===".bin" ? parse_cc_bin : parse_cc_xml)(data, opts);
+}
+
+function write_wb(wb, name, opts) {
+	return (name.substr(-4)===".bin" ? write_wb_bin : write_wb_xml)(wb, opts);
+}
+
+function write_ws(data, name, opts, wb) {
+	return (name.substr(-4)===".bin" ? write_ws_bin : write_ws_xml)(data, opts, wb);
+}
+
+function write_sty(data, name, opts) {
+	return (name.substr(-4)===".bin" ? write_sty_bin : write_sty_xml)(data, opts);
+}
+
+function write_sst(data, name, opts) {
+	return (name.substr(-4)===".bin" ? write_sst_bin : write_sst_xml)(data, opts);
+}
+/*
+function write_cmnt(data, name, opts) {
+	return (name.substr(-4)===".bin" ? write_comments_bin : write_comments_xml)(data, opts);
+}
+
+function write_cc(data, name, opts) {
+	return (name.substr(-4)===".bin" ? write_cc_bin : write_cc_xml)(data, opts);
+}
+*/
+/* [MS-XLSB] 2.3 Record Enumeration */
+var RecordEnum = {
+	0x0000: { n:"BrtRowHdr", f:parse_BrtRowHdr },
+	0x0001: { n:"BrtCellBlank", f:parse_BrtCellBlank },
+	0x0002: { n:"BrtCellRk", f:parse_BrtCellRk },
+	0x0003: { n:"BrtCellError", f:parse_BrtCellError },
+	0x0004: { n:"BrtCellBool", f:parse_BrtCellBool },
+	0x0005: { n:"BrtCellReal", f:parse_BrtCellReal },
+	0x0006: { n:"BrtCellSt", f:parse_BrtCellSt },
+	0x0007: { n:"BrtCellIsst", f:parse_BrtCellIsst },
+	0x0008: { n:"BrtFmlaString", f:parse_BrtFmlaString },
+	0x0009: { n:"BrtFmlaNum", f:parse_BrtFmlaNum },
+	0x000A: { n:"BrtFmlaBool", f:parse_BrtFmlaBool },
+	0x000B: { n:"BrtFmlaError", f:parse_BrtFmlaError },
+	0x0010: { n:"BrtFRTArchID$", f:parse_BrtFRTArchID$ },
+	0x0013: { n:"BrtSSTItem", f:parse_RichStr },
+	0x0014: { n:"BrtPCDIMissing", f:parsenoop },
+	0x0015: { n:"BrtPCDINumber", f:parsenoop },
+	0x0016: { n:"BrtPCDIBoolean", f:parsenoop },
+	0x0017: { n:"BrtPCDIError", f:parsenoop },
+	0x0018: { n:"BrtPCDIString", f:parsenoop },
+	0x0019: { n:"BrtPCDIDatetime", f:parsenoop },
+	0x001A: { n:"BrtPCDIIndex", f:parsenoop },
+	0x001B: { n:"BrtPCDIAMissing", f:parsenoop },
+	0x001C: { n:"BrtPCDIANumber", f:parsenoop },
+	0x001D: { n:"BrtPCDIABoolean", f:parsenoop },
+	0x001E: { n:"BrtPCDIAError", f:parsenoop },
+	0x001F: { n:"BrtPCDIAString", f:parsenoop },
+	0x0020: { n:"BrtPCDIADatetime", f:parsenoop },
+	0x0021: { n:"BrtPCRRecord", f:parsenoop },
+	0x0022: { n:"BrtPCRRecordDt", f:parsenoop },
+	0x0023: { n:"BrtFRTBegin", f:parsenoop },
+	0x0024: { n:"BrtFRTEnd", f:parsenoop },
+	0x0025: { n:"BrtACBegin", f:parsenoop },
+	0x0026: { n:"BrtACEnd", f:parsenoop },
+	0x0027: { n:"BrtName", f:parsenoop },
+	0x0028: { n:"BrtIndexRowBlock", f:parsenoop },
+	0x002A: { n:"BrtIndexBlock", f:parsenoop },
+	0x002B: { n:"BrtFont", f:parse_BrtFont },
+	0x002C: { n:"BrtFmt", f:parse_BrtFmt },
+	0x002D: { n:"BrtFill", f:parsenoop },
+	0x002E: { n:"BrtBorder", f:parsenoop },
+	0x002F: { n:"BrtXF", f:parse_BrtXF },
+	0x0030: { n:"BrtStyle", f:parsenoop },
+	0x0031: { n:"BrtCellMeta", f:parsenoop },
+	0x0032: { n:"BrtValueMeta", f:parsenoop },
+	0x0033: { n:"BrtMdb", f:parsenoop },
+	0x0034: { n:"BrtBeginFmd", f:parsenoop },
+	0x0035: { n:"BrtEndFmd", f:parsenoop },
+	0x0036: { n:"BrtBeginMdx", f:parsenoop },
+	0x0037: { n:"BrtEndMdx", f:parsenoop },
+	0x0038: { n:"BrtBeginMdxTuple", f:parsenoop },
+	0x0039: { n:"BrtEndMdxTuple", f:parsenoop },
+	0x003A: { n:"BrtMdxMbrIstr", f:parsenoop },
+	0x003B: { n:"BrtStr", f:parsenoop },
+	0x003C: { n:"BrtColInfo", f:parsenoop },
+	0x003E: { n:"BrtCellRString", f:parsenoop },
+	0x003F: { n:"BrtCalcChainItem$", f:parse_BrtCalcChainItem$ },
+	0x0040: { n:"BrtDVal", f:parsenoop },
+	0x0041: { n:"BrtSxvcellNum", f:parsenoop },
+	0x0042: { n:"BrtSxvcellStr", f:parsenoop },
+	0x0043: { n:"BrtSxvcellBool", f:parsenoop },
+	0x0044: { n:"BrtSxvcellErr", f:parsenoop },
+	0x0045: { n:"BrtSxvcellDate", f:parsenoop },
+	0x0046: { n:"BrtSxvcellNil", f:parsenoop },
+	0x0080: { n:"BrtFileVersion", f:parsenoop },
+	0x0081: { n:"BrtBeginSheet", f:parsenoop },
+	0x0082: { n:"BrtEndSheet", f:parsenoop },
+	0x0083: { n:"BrtBeginBook", f:parsenoop, p:0 },
+	0x0084: { n:"BrtEndBook", f:parsenoop },
+	0x0085: { n:"BrtBeginWsViews", f:parsenoop },
+	0x0086: { n:"BrtEndWsViews", f:parsenoop },
+	0x0087: { n:"BrtBeginBookViews", f:parsenoop },
+	0x0088: { n:"BrtEndBookViews", f:parsenoop },
+	0x0089: { n:"BrtBeginWsView", f:parsenoop },
+	0x008A: { n:"BrtEndWsView", f:parsenoop },
+	0x008B: { n:"BrtBeginCsViews", f:parsenoop },
+	0x008C: { n:"BrtEndCsViews", f:parsenoop },
+	0x008D: { n:"BrtBeginCsView", f:parsenoop },
+	0x008E: { n:"BrtEndCsView", f:parsenoop },
+	0x008F: { n:"BrtBeginBundleShs", f:parsenoop },
+	0x0090: { n:"BrtEndBundleShs", f:parsenoop },
+	0x0091: { n:"BrtBeginSheetData", f:parsenoop },
+	0x0092: { n:"BrtEndSheetData", f:parsenoop },
+	0x0093: { n:"BrtWsProp", f:parse_BrtWsProp },
+	0x0094: { n:"BrtWsDim", f:parse_BrtWsDim, p:16 },
+	0x0097: { n:"BrtPane", f:parsenoop },
+	0x0098: { n:"BrtSel", f:parsenoop },
+	0x0099: { n:"BrtWbProp", f:parse_BrtWbProp },
+	0x009A: { n:"BrtWbFactoid", f:parsenoop },
+	0x009B: { n:"BrtFileRecover", f:parsenoop },
+	0x009C: { n:"BrtBundleSh", f:parse_BrtBundleSh },
+	0x009D: { n:"BrtCalcProp", f:parsenoop },
+	0x009E: { n:"BrtBookView", f:parsenoop },
+	0x009F: { n:"BrtBeginSst", f:parse_BrtBeginSst },
+	0x00A0: { n:"BrtEndSst", f:parsenoop },
+	0x00A1: { n:"BrtBeginAFilter", f:parsenoop },
+	0x00A2: { n:"BrtEndAFilter", f:parsenoop },
+	0x00A3: { n:"BrtBeginFilterColumn", f:parsenoop },
+	0x00A4: { n:"BrtEndFilterColumn", f:parsenoop },
+	0x00A5: { n:"BrtBeginFilters", f:parsenoop },
+	0x00A6: { n:"BrtEndFilters", f:parsenoop },
+	0x00A7: { n:"BrtFilter", f:parsenoop },
+	0x00A8: { n:"BrtColorFilter", f:parsenoop },
+	0x00A9: { n:"BrtIconFilter", f:parsenoop },
+	0x00AA: { n:"BrtTop10Filter", f:parsenoop },
+	0x00AB: { n:"BrtDynamicFilter", f:parsenoop },
+	0x00AC: { n:"BrtBeginCustomFilters", f:parsenoop },
+	0x00AD: { n:"BrtEndCustomFilters", f:parsenoop },
+	0x00AE: { n:"BrtCustomFilter", f:parsenoop },
+	0x00AF: { n:"BrtAFilterDateGroupItem", f:parsenoop },
+	0x00B0: { n:"BrtMergeCell", f:parse_BrtMergeCell },
+	0x00B1: { n:"BrtBeginMergeCells", f:parsenoop },
+	0x00B2: { n:"BrtEndMergeCells", f:parsenoop },
+	0x00B3: { n:"BrtBeginPivotCacheDef", f:parsenoop },
+	0x00B4: { n:"BrtEndPivotCacheDef", f:parsenoop },
+	0x00B5: { n:"BrtBeginPCDFields", f:parsenoop },
+	0x00B6: { n:"BrtEndPCDFields", f:parsenoop },
+	0x00B7: { n:"BrtBeginPCDField", f:parsenoop },
+	0x00B8: { n:"BrtEndPCDField", f:parsenoop },
+	0x00B9: { n:"BrtBeginPCDSource", f:parsenoop },
+	0x00BA: { n:"BrtEndPCDSource", f:parsenoop },
+	0x00BB: { n:"BrtBeginPCDSRange", f:parsenoop },
+	0x00BC: { n:"BrtEndPCDSRange", f:parsenoop },
+	0x00BD: { n:"BrtBeginPCDFAtbl", f:parsenoop },
+	0x00BE: { n:"BrtEndPCDFAtbl", f:parsenoop },
+	0x00BF: { n:"BrtBeginPCDIRun", f:parsenoop },
+	0x00C0: { n:"BrtEndPCDIRun", f:parsenoop },
+	0x00C1: { n:"BrtBeginPivotCacheRecords", f:parsenoop },
+	0x00C2: { n:"BrtEndPivotCacheRecords", f:parsenoop },
+	0x00C3: { n:"BrtBeginPCDHierarchies", f:parsenoop },
+	0x00C4: { n:"BrtEndPCDHierarchies", f:parsenoop },
+	0x00C5: { n:"BrtBeginPCDHierarchy", f:parsenoop },
+	0x00C6: { n:"BrtEndPCDHierarchy", f:parsenoop },
+	0x00C7: { n:"BrtBeginPCDHFieldsUsage", f:parsenoop },
+	0x00C8: { n:"BrtEndPCDHFieldsUsage", f:parsenoop },
+	0x00C9: { n:"BrtBeginExtConnection", f:parsenoop },
+	0x00CA: { n:"BrtEndExtConnection", f:parsenoop },
+	0x00CB: { n:"BrtBeginECDbProps", f:parsenoop },
+	0x00CC: { n:"BrtEndECDbProps", f:parsenoop },
+	0x00CD: { n:"BrtBeginECOlapProps", f:parsenoop },
+	0x00CE: { n:"BrtEndECOlapProps", f:parsenoop },
+	0x00CF: { n:"BrtBeginPCDSConsol", f:parsenoop },
+	0x00D0: { n:"BrtEndPCDSConsol", f:parsenoop },
+	0x00D1: { n:"BrtBeginPCDSCPages", f:parsenoop },
+	0x00D2: { n:"BrtEndPCDSCPages", f:parsenoop },
+	0x00D3: { n:"BrtBeginPCDSCPage", f:parsenoop },
+	0x00D4: { n:"BrtEndPCDSCPage", f:parsenoop },
+	0x00D5: { n:"BrtBeginPCDSCPItem", f:parsenoop },
+	0x00D6: { n:"BrtEndPCDSCPItem", f:parsenoop },
+	0x00D7: { n:"BrtBeginPCDSCSets", f:parsenoop },
+	0x00D8: { n:"BrtEndPCDSCSets", f:parsenoop },
+	0x00D9: { n:"BrtBeginPCDSCSet", f:parsenoop },
+	0x00DA: { n:"BrtEndPCDSCSet", f:parsenoop },
+	0x00DB: { n:"BrtBeginPCDFGroup", f:parsenoop },
+	0x00DC: { n:"BrtEndPCDFGroup", f:parsenoop },
+	0x00DD: { n:"BrtBeginPCDFGItems", f:parsenoop },
+	0x00DE: { n:"BrtEndPCDFGItems", f:parsenoop },
+	0x00DF: { n:"BrtBeginPCDFGRange", f:parsenoop },
+	0x00E0: { n:"BrtEndPCDFGRange", f:parsenoop },
+	0x00E1: { n:"BrtBeginPCDFGDiscrete", f:parsenoop },
+	0x00E2: { n:"BrtEndPCDFGDiscrete", f:parsenoop },
+	0x00E3: { n:"BrtBeginPCDSDTupleCache", f:parsenoop },
+	0x00E4: { n:"BrtEndPCDSDTupleCache", f:parsenoop },
+	0x00E5: { n:"BrtBeginPCDSDTCEntries", f:parsenoop },
+	0x00E6: { n:"BrtEndPCDSDTCEntries", f:parsenoop },
+	0x00E7: { n:"BrtBeginPCDSDTCEMembers", f:parsenoop },
+	0x00E8: { n:"BrtEndPCDSDTCEMembers", f:parsenoop },
+	0x00E9: { n:"BrtBeginPCDSDTCEMember", f:parsenoop },
+	0x00EA: { n:"BrtEndPCDSDTCEMember", f:parsenoop },
+	0x00EB: { n:"BrtBeginPCDSDTCQueries", f:parsenoop },
+	0x00EC: { n:"BrtEndPCDSDTCQueries", f:parsenoop },
+	0x00ED: { n:"BrtBeginPCDSDTCQuery", f:parsenoop },
+	0x00EE: { n:"BrtEndPCDSDTCQuery", f:parsenoop },
+	0x00EF: { n:"BrtBeginPCDSDTCSets", f:parsenoop },
+	0x00F0: { n:"BrtEndPCDSDTCSets", f:parsenoop },
+	0x00F1: { n:"BrtBeginPCDSDTCSet", f:parsenoop },
+	0x00F2: { n:"BrtEndPCDSDTCSet", f:parsenoop },
+	0x00F3: { n:"BrtBeginPCDCalcItems", f:parsenoop },
+	0x00F4: { n:"BrtEndPCDCalcItems", f:parsenoop },
+	0x00F5: { n:"BrtBeginPCDCalcItem", f:parsenoop },
+	0x00F6: { n:"BrtEndPCDCalcItem", f:parsenoop },
+	0x00F7: { n:"BrtBeginPRule", f:parsenoop },
+	0x00F8: { n:"BrtEndPRule", f:parsenoop },
+	0x00F9: { n:"BrtBeginPRFilters", f:parsenoop },
+	0x00FA: { n:"BrtEndPRFilters", f:parsenoop },
+	0x00FB: { n:"BrtBeginPRFilter", f:parsenoop },
+	0x00FC: { n:"BrtEndPRFilter", f:parsenoop },
+	0x00FD: { n:"BrtBeginPNames", f:parsenoop },
+	0x00FE: { n:"BrtEndPNames", f:parsenoop },
+	0x00FF: { n:"BrtBeginPName", f:parsenoop },
+	0x0100: { n:"BrtEndPName", f:parsenoop },
+	0x0101: { n:"BrtBeginPNPairs", f:parsenoop },
+	0x0102: { n:"BrtEndPNPairs", f:parsenoop },
+	0x0103: { n:"BrtBeginPNPair", f:parsenoop },
+	0x0104: { n:"BrtEndPNPair", f:parsenoop },
+	0x0105: { n:"BrtBeginECWebProps", f:parsenoop },
+	0x0106: { n:"BrtEndECWebProps", f:parsenoop },
+	0x0107: { n:"BrtBeginEcWpTables", f:parsenoop },
+	0x0108: { n:"BrtEndECWPTables", f:parsenoop },
+	0x0109: { n:"BrtBeginECParams", f:parsenoop },
+	0x010A: { n:"BrtEndECParams", f:parsenoop },
+	0x010B: { n:"BrtBeginECParam", f:parsenoop },
+	0x010C: { n:"BrtEndECParam", f:parsenoop },
+	0x010D: { n:"BrtBeginPCDKPIs", f:parsenoop },
+	0x010E: { n:"BrtEndPCDKPIs", f:parsenoop },
+	0x010F: { n:"BrtBeginPCDKPI", f:parsenoop },
+	0x0110: { n:"BrtEndPCDKPI", f:parsenoop },
+	0x0111: { n:"BrtBeginDims", f:parsenoop },
+	0x0112: { n:"BrtEndDims", f:parsenoop },
+	0x0113: { n:"BrtBeginDim", f:parsenoop },
+	0x0114: { n:"BrtEndDim", f:parsenoop },
+	0x0115: { n:"BrtIndexPartEnd", f:parsenoop },
+	0x0116: { n:"BrtBeginStyleSheet", f:parsenoop },
+	0x0117: { n:"BrtEndStyleSheet", f:parsenoop },
+	0x0118: { n:"BrtBeginSXView", f:parsenoop },
+	0x0119: { n:"BrtEndSXVI", f:parsenoop },
+	0x011A: { n:"BrtBeginSXVI", f:parsenoop },
+	0x011B: { n:"BrtBeginSXVIs", f:parsenoop },
+	0x011C: { n:"BrtEndSXVIs", f:parsenoop },
+	0x011D: { n:"BrtBeginSXVD", f:parsenoop },
+	0x011E: { n:"BrtEndSXVD", f:parsenoop },
+	0x011F: { n:"BrtBeginSXVDs", f:parsenoop },
+	0x0120: { n:"BrtEndSXVDs", f:parsenoop },
+	0x0121: { n:"BrtBeginSXPI", f:parsenoop },
+	0x0122: { n:"BrtEndSXPI", f:parsenoop },
+	0x0123: { n:"BrtBeginSXPIs", f:parsenoop },
+	0x0124: { n:"BrtEndSXPIs", f:parsenoop },
+	0x0125: { n:"BrtBeginSXDI", f:parsenoop },
+	0x0126: { n:"BrtEndSXDI", f:parsenoop },
+	0x0127: { n:"BrtBeginSXDIs", f:parsenoop },
+	0x0128: { n:"BrtEndSXDIs", f:parsenoop },
+	0x0129: { n:"BrtBeginSXLI", f:parsenoop },
+	0x012A: { n:"BrtEndSXLI", f:parsenoop },
+	0x012B: { n:"BrtBeginSXLIRws", f:parsenoop },
+	0x012C: { n:"BrtEndSXLIRws", f:parsenoop },
+	0x012D: { n:"BrtBeginSXLICols", f:parsenoop },
+	0x012E: { n:"BrtEndSXLICols", f:parsenoop },
+	0x012F: { n:"BrtBeginSXFormat", f:parsenoop },
+	0x0130: { n:"BrtEndSXFormat", f:parsenoop },
+	0x0131: { n:"BrtBeginSXFormats", f:parsenoop },
+	0x0132: { n:"BrtEndSxFormats", f:parsenoop },
+	0x0133: { n:"BrtBeginSxSelect", f:parsenoop },
+	0x0134: { n:"BrtEndSxSelect", f:parsenoop },
+	0x0135: { n:"BrtBeginISXVDRws", f:parsenoop },
+	0x0136: { n:"BrtEndISXVDRws", f:parsenoop },
+	0x0137: { n:"BrtBeginISXVDCols", f:parsenoop },
+	0x0138: { n:"BrtEndISXVDCols", f:parsenoop },
+	0x0139: { n:"BrtEndSXLocation", f:parsenoop },
+	0x013A: { n:"BrtBeginSXLocation", f:parsenoop },
+	0x013B: { n:"BrtEndSXView", f:parsenoop },
+	0x013C: { n:"BrtBeginSXTHs", f:parsenoop },
+	0x013D: { n:"BrtEndSXTHs", f:parsenoop },
+	0x013E: { n:"BrtBeginSXTH", f:parsenoop },
+	0x013F: { n:"BrtEndSXTH", f:parsenoop },
+	0x0140: { n:"BrtBeginISXTHRws", f:parsenoop },
+	0x0141: { n:"BrtEndISXTHRws", f:parsenoop },
+	0x0142: { n:"BrtBeginISXTHCols", f:parsenoop },
+	0x0143: { n:"BrtEndISXTHCols", f:parsenoop },
+	0x0144: { n:"BrtBeginSXTDMPS", f:parsenoop },
+	0x0145: { n:"BrtEndSXTDMPs", f:parsenoop },
+	0x0146: { n:"BrtBeginSXTDMP", f:parsenoop },
+	0x0147: { n:"BrtEndSXTDMP", f:parsenoop },
+	0x0148: { n:"BrtBeginSXTHItems", f:parsenoop },
+	0x0149: { n:"BrtEndSXTHItems", f:parsenoop },
+	0x014A: { n:"BrtBeginSXTHItem", f:parsenoop },
+	0x014B: { n:"BrtEndSXTHItem", f:parsenoop },
+	0x014C: { n:"BrtBeginMetadata", f:parsenoop },
+	0x014D: { n:"BrtEndMetadata", f:parsenoop },
+	0x014E: { n:"BrtBeginEsmdtinfo", f:parsenoop },
+	0x014F: { n:"BrtMdtinfo", f:parsenoop },
+	0x0150: { n:"BrtEndEsmdtinfo", f:parsenoop },
+	0x0151: { n:"BrtBeginEsmdb", f:parsenoop },
+	0x0152: { n:"BrtEndEsmdb", f:parsenoop },
+	0x0153: { n:"BrtBeginEsfmd", f:parsenoop },
+	0x0154: { n:"BrtEndEsfmd", f:parsenoop },
+	0x0155: { n:"BrtBeginSingleCells", f:parsenoop },
+	0x0156: { n:"BrtEndSingleCells", f:parsenoop },
+	0x0157: { n:"BrtBeginList", f:parsenoop },
+	0x0158: { n:"BrtEndList", f:parsenoop },
+	0x0159: { n:"BrtBeginListCols", f:parsenoop },
+	0x015A: { n:"BrtEndListCols", f:parsenoop },
+	0x015B: { n:"BrtBeginListCol", f:parsenoop },
+	0x015C: { n:"BrtEndListCol", f:parsenoop },
+	0x015D: { n:"BrtBeginListXmlCPr", f:parsenoop },
+	0x015E: { n:"BrtEndListXmlCPr", f:parsenoop },
+	0x015F: { n:"BrtListCCFmla", f:parsenoop },
+	0x0160: { n:"BrtListTrFmla", f:parsenoop },
+	0x0161: { n:"BrtBeginExternals", f:parsenoop },
+	0x0162: { n:"BrtEndExternals", f:parsenoop },
+	0x0163: { n:"BrtSupBookSrc", f:parsenoop },
+	0x0165: { n:"BrtSupSelf", f:parsenoop },
+	0x0166: { n:"BrtSupSame", f:parsenoop },
+	0x0167: { n:"BrtSupTabs", f:parsenoop },
+	0x0168: { n:"BrtBeginSupBook", f:parsenoop },
+	0x0169: { n:"BrtPlaceholderName", f:parsenoop },
+	0x016A: { n:"BrtExternSheet", f:parsenoop },
+	0x016B: { n:"BrtExternTableStart", f:parsenoop },
+	0x016C: { n:"BrtExternTableEnd", f:parsenoop },
+	0x016E: { n:"BrtExternRowHdr", f:parsenoop },
+	0x016F: { n:"BrtExternCellBlank", f:parsenoop },
+	0x0170: { n:"BrtExternCellReal", f:parsenoop },
+	0x0171: { n:"BrtExternCellBool", f:parsenoop },
+	0x0172: { n:"BrtExternCellError", f:parsenoop },
+	0x0173: { n:"BrtExternCellString", f:parsenoop },
+	0x0174: { n:"BrtBeginEsmdx", f:parsenoop },
+	0x0175: { n:"BrtEndEsmdx", f:parsenoop },
+	0x0176: { n:"BrtBeginMdxSet", f:parsenoop },
+	0x0177: { n:"BrtEndMdxSet", f:parsenoop },
+	0x0178: { n:"BrtBeginMdxMbrProp", f:parsenoop },
+	0x0179: { n:"BrtEndMdxMbrProp", f:parsenoop },
+	0x017A: { n:"BrtBeginMdxKPI", f:parsenoop },
+	0x017B: { n:"BrtEndMdxKPI", f:parsenoop },
+	0x017C: { n:"BrtBeginEsstr", f:parsenoop },
+	0x017D: { n:"BrtEndEsstr", f:parsenoop },
+	0x017E: { n:"BrtBeginPRFItem", f:parsenoop },
+	0x017F: { n:"BrtEndPRFItem", f:parsenoop },
+	0x0180: { n:"BrtBeginPivotCacheIDs", f:parsenoop },
+	0x0181: { n:"BrtEndPivotCacheIDs", f:parsenoop },
+	0x0182: { n:"BrtBeginPivotCacheID", f:parsenoop },
+	0x0183: { n:"BrtEndPivotCacheID", f:parsenoop },
+	0x0184: { n:"BrtBeginISXVIs", f:parsenoop },
+	0x0185: { n:"BrtEndISXVIs", f:parsenoop },
+	0x0186: { n:"BrtBeginColInfos", f:parsenoop },
+	0x0187: { n:"BrtEndColInfos", f:parsenoop },
+	0x0188: { n:"BrtBeginRwBrk", f:parsenoop },
+	0x0189: { n:"BrtEndRwBrk", f:parsenoop },
+	0x018A: { n:"BrtBeginColBrk", f:parsenoop },
+	0x018B: { n:"BrtEndColBrk", f:parsenoop },
+	0x018C: { n:"BrtBrk", f:parsenoop },
+	0x018D: { n:"BrtUserBookView", f:parsenoop },
+	0x018E: { n:"BrtInfo", f:parsenoop },
+	0x018F: { n:"BrtCUsr", f:parsenoop },
+	0x0190: { n:"BrtUsr", f:parsenoop },
+	0x0191: { n:"BrtBeginUsers", f:parsenoop },
+	0x0193: { n:"BrtEOF", f:parsenoop },
+	0x0194: { n:"BrtUCR", f:parsenoop },
+	0x0195: { n:"BrtRRInsDel", f:parsenoop },
+	0x0196: { n:"BrtRREndInsDel", f:parsenoop },
+	0x0197: { n:"BrtRRMove", f:parsenoop },
+	0x0198: { n:"BrtRREndMove", f:parsenoop },
+	0x0199: { n:"BrtRRChgCell", f:parsenoop },
+	0x019A: { n:"BrtRREndChgCell", f:parsenoop },
+	0x019B: { n:"BrtRRHeader", f:parsenoop },
+	0x019C: { n:"BrtRRUserView", f:parsenoop },
+	0x019D: { n:"BrtRRRenSheet", f:parsenoop },
+	0x019E: { n:"BrtRRInsertSh", f:parsenoop },
+	0x019F: { n:"BrtRRDefName", f:parsenoop },
+	0x01A0: { n:"BrtRRNote", f:parsenoop },
+	0x01A1: { n:"BrtRRConflict", f:parsenoop },
+	0x01A2: { n:"BrtRRTQSIF", f:parsenoop },
+	0x01A3: { n:"BrtRRFormat", f:parsenoop },
+	0x01A4: { n:"BrtRREndFormat", f:parsenoop },
+	0x01A5: { n:"BrtRRAutoFmt", f:parsenoop },
+	0x01A6: { n:"BrtBeginUserShViews", f:parsenoop },
+	0x01A7: { n:"BrtBeginUserShView", f:parsenoop },
+	0x01A8: { n:"BrtEndUserShView", f:parsenoop },
+	0x01A9: { n:"BrtEndUserShViews", f:parsenoop },
+	0x01AA: { n:"BrtArrFmla", f:parsenoop },
+	0x01AB: { n:"BrtShrFmla", f:parsenoop },
+	0x01AC: { n:"BrtTable", f:parsenoop },
+	0x01AD: { n:"BrtBeginExtConnections", f:parsenoop },
+	0x01AE: { n:"BrtEndExtConnections", f:parsenoop },
+	0x01AF: { n:"BrtBeginPCDCalcMems", f:parsenoop },
+	0x01B0: { n:"BrtEndPCDCalcMems", f:parsenoop },
+	0x01B1: { n:"BrtBeginPCDCalcMem", f:parsenoop },
+	0x01B2: { n:"BrtEndPCDCalcMem", f:parsenoop },
+	0x01B3: { n:"BrtBeginPCDHGLevels", f:parsenoop },
+	0x01B4: { n:"BrtEndPCDHGLevels", f:parsenoop },
+	0x01B5: { n:"BrtBeginPCDHGLevel", f:parsenoop },
+	0x01B6: { n:"BrtEndPCDHGLevel", f:parsenoop },
+	0x01B7: { n:"BrtBeginPCDHGLGroups", f:parsenoop },
+	0x01B8: { n:"BrtEndPCDHGLGroups", f:parsenoop },
+	0x01B9: { n:"BrtBeginPCDHGLGroup", f:parsenoop },
+	0x01BA: { n:"BrtEndPCDHGLGroup", f:parsenoop },
+	0x01BB: { n:"BrtBeginPCDHGLGMembers", f:parsenoop },
+	0x01BC: { n:"BrtEndPCDHGLGMembers", f:parsenoop },
+	0x01BD: { n:"BrtBeginPCDHGLGMember", f:parsenoop },
+	0x01BE: { n:"BrtEndPCDHGLGMember", f:parsenoop },
+	0x01BF: { n:"BrtBeginQSI", f:parsenoop },
+	0x01C0: { n:"BrtEndQSI", f:parsenoop },
+	0x01C1: { n:"BrtBeginQSIR", f:parsenoop },
+	0x01C2: { n:"BrtEndQSIR", f:parsenoop },
+	0x01C3: { n:"BrtBeginDeletedNames", f:parsenoop },
+	0x01C4: { n:"BrtEndDeletedNames", f:parsenoop },
+	0x01C5: { n:"BrtBeginDeletedName", f:parsenoop },
+	0x01C6: { n:"BrtEndDeletedName", f:parsenoop },
+	0x01C7: { n:"BrtBeginQSIFs", f:parsenoop },
+	0x01C8: { n:"BrtEndQSIFs", f:parsenoop },
+	0x01C9: { n:"BrtBeginQSIF", f:parsenoop },
+	0x01CA: { n:"BrtEndQSIF", f:parsenoop },
+	0x01CB: { n:"BrtBeginAutoSortScope", f:parsenoop },
+	0x01CC: { n:"BrtEndAutoSortScope", f:parsenoop },
+	0x01CD: { n:"BrtBeginConditionalFormatting", f:parsenoop },
+	0x01CE: { n:"BrtEndConditionalFormatting", f:parsenoop },
+	0x01CF: { n:"BrtBeginCFRule", f:parsenoop },
+	0x01D0: { n:"BrtEndCFRule", f:parsenoop },
+	0x01D1: { n:"BrtBeginIconSet", f:parsenoop },
+	0x01D2: { n:"BrtEndIconSet", f:parsenoop },
+	0x01D3: { n:"BrtBeginDatabar", f:parsenoop },
+	0x01D4: { n:"BrtEndDatabar", f:parsenoop },
+	0x01D5: { n:"BrtBeginColorScale", f:parsenoop },
+	0x01D6: { n:"BrtEndColorScale", f:parsenoop },
+	0x01D7: { n:"BrtCFVO", f:parsenoop },
+	0x01D8: { n:"BrtExternValueMeta", f:parsenoop },
+	0x01D9: { n:"BrtBeginColorPalette", f:parsenoop },
+	0x01DA: { n:"BrtEndColorPalette", f:parsenoop },
+	0x01DB: { n:"BrtIndexedColor", f:parsenoop },
+	0x01DC: { n:"BrtMargins", f:parsenoop },
+	0x01DD: { n:"BrtPrintOptions", f:parsenoop },
+	0x01DE: { n:"BrtPageSetup", f:parsenoop },
+	0x01DF: { n:"BrtBeginHeaderFooter", f:parsenoop },
+	0x01E0: { n:"BrtEndHeaderFooter", f:parsenoop },
+	0x01E1: { n:"BrtBeginSXCrtFormat", f:parsenoop },
+	0x01E2: { n:"BrtEndSXCrtFormat", f:parsenoop },
+	0x01E3: { n:"BrtBeginSXCrtFormats", f:parsenoop },
+	0x01E4: { n:"BrtEndSXCrtFormats", f:parsenoop },
+	0x01E5: { n:"BrtWsFmtInfo", f:parsenoop },
+	0x01E6: { n:"BrtBeginMgs", f:parsenoop },
+	0x01E7: { n:"BrtEndMGs", f:parsenoop },
+	0x01E8: { n:"BrtBeginMGMaps", f:parsenoop },
+	0x01E9: { n:"BrtEndMGMaps", f:parsenoop },
+	0x01EA: { n:"BrtBeginMG", f:parsenoop },
+	0x01EB: { n:"BrtEndMG", f:parsenoop },
+	0x01EC: { n:"BrtBeginMap", f:parsenoop },
+	0x01ED: { n:"BrtEndMap", f:parsenoop },
+	0x01EE: { n:"BrtHLink", f:parse_BrtHLink },
+	0x01EF: { n:"BrtBeginDCon", f:parsenoop },
+	0x01F0: { n:"BrtEndDCon", f:parsenoop },
+	0x01F1: { n:"BrtBeginDRefs", f:parsenoop },
+	0x01F2: { n:"BrtEndDRefs", f:parsenoop },
+	0x01F3: { n:"BrtDRef", f:parsenoop },
+	0x01F4: { n:"BrtBeginScenMan", f:parsenoop },
+	0x01F5: { n:"BrtEndScenMan", f:parsenoop },
+	0x01F6: { n:"BrtBeginSct", f:parsenoop },
+	0x01F7: { n:"BrtEndSct", f:parsenoop },
+	0x01F8: { n:"BrtSlc", f:parsenoop },
+	0x01F9: { n:"BrtBeginDXFs", f:parsenoop },
+	0x01FA: { n:"BrtEndDXFs", f:parsenoop },
+	0x01FB: { n:"BrtDXF", f:parsenoop },
+	0x01FC: { n:"BrtBeginTableStyles", f:parsenoop },
+	0x01FD: { n:"BrtEndTableStyles", f:parsenoop },
+	0x01FE: { n:"BrtBeginTableStyle", f:parsenoop },
+	0x01FF: { n:"BrtEndTableStyle", f:parsenoop },
+	0x0200: { n:"BrtTableStyleElement", f:parsenoop },
+	0x0201: { n:"BrtTableStyleClient", f:parsenoop },
+	0x0202: { n:"BrtBeginVolDeps", f:parsenoop },
+	0x0203: { n:"BrtEndVolDeps", f:parsenoop },
+	0x0204: { n:"BrtBeginVolType", f:parsenoop },
+	0x0205: { n:"BrtEndVolType", f:parsenoop },
+	0x0206: { n:"BrtBeginVolMain", f:parsenoop },
+	0x0207: { n:"BrtEndVolMain", f:parsenoop },
+	0x0208: { n:"BrtBeginVolTopic", f:parsenoop },
+	0x0209: { n:"BrtEndVolTopic", f:parsenoop },
+	0x020A: { n:"BrtVolSubtopic", f:parsenoop },
+	0x020B: { n:"BrtVolRef", f:parsenoop },
+	0x020C: { n:"BrtVolNum", f:parsenoop },
+	0x020D: { n:"BrtVolErr", f:parsenoop },
+	0x020E: { n:"BrtVolStr", f:parsenoop },
+	0x020F: { n:"BrtVolBool", f:parsenoop },
+	0x0210: { n:"BrtBeginCalcChain$", f:parsenoop },
+	0x0211: { n:"BrtEndCalcChain$", f:parsenoop },
+	0x0212: { n:"BrtBeginSortState", f:parsenoop },
+	0x0213: { n:"BrtEndSortState", f:parsenoop },
+	0x0214: { n:"BrtBeginSortCond", f:parsenoop },
+	0x0215: { n:"BrtEndSortCond", f:parsenoop },
+	0x0216: { n:"BrtBookProtection", f:parsenoop },
+	0x0217: { n:"BrtSheetProtection", f:parsenoop },
+	0x0218: { n:"BrtRangeProtection", f:parsenoop },
+	0x0219: { n:"BrtPhoneticInfo", f:parsenoop },
+	0x021A: { n:"BrtBeginECTxtWiz", f:parsenoop },
+	0x021B: { n:"BrtEndECTxtWiz", f:parsenoop },
+	0x021C: { n:"BrtBeginECTWFldInfoLst", f:parsenoop },
+	0x021D: { n:"BrtEndECTWFldInfoLst", f:parsenoop },
+	0x021E: { n:"BrtBeginECTwFldInfo", f:parsenoop },
+	0x0224: { n:"BrtFileSharing", f:parsenoop },
+	0x0225: { n:"BrtOleSize", f:parsenoop },
+	0x0226: { n:"BrtDrawing", f:parsenoop },
+	0x0227: { n:"BrtLegacyDrawing", f:parsenoop },
+	0x0228: { n:"BrtLegacyDrawingHF", f:parsenoop },
+	0x0229: { n:"BrtWebOpt", f:parsenoop },
+	0x022A: { n:"BrtBeginWebPubItems", f:parsenoop },
+	0x022B: { n:"BrtEndWebPubItems", f:parsenoop },
+	0x022C: { n:"BrtBeginWebPubItem", f:parsenoop },
+	0x022D: { n:"BrtEndWebPubItem", f:parsenoop },
+	0x022E: { n:"BrtBeginSXCondFmt", f:parsenoop },
+	0x022F: { n:"BrtEndSXCondFmt", f:parsenoop },
+	0x0230: { n:"BrtBeginSXCondFmts", f:parsenoop },
+	0x0231: { n:"BrtEndSXCondFmts", f:parsenoop },
+	0x0232: { n:"BrtBkHim", f:parsenoop },
+	0x0234: { n:"BrtColor", f:parsenoop },
+	0x0235: { n:"BrtBeginIndexedColors", f:parsenoop },
+	0x0236: { n:"BrtEndIndexedColors", f:parsenoop },
+	0x0239: { n:"BrtBeginMRUColors", f:parsenoop },
+	0x023A: { n:"BrtEndMRUColors", f:parsenoop },
+	0x023C: { n:"BrtMRUColor", f:parsenoop },
+	0x023D: { n:"BrtBeginDVals", f:parsenoop },
+	0x023E: { n:"BrtEndDVals", f:parsenoop },
+	0x0241: { n:"BrtSupNameStart", f:parsenoop },
+	0x0242: { n:"BrtSupNameValueStart", f:parsenoop },
+	0x0243: { n:"BrtSupNameValueEnd", f:parsenoop },
+	0x0244: { n:"BrtSupNameNum", f:parsenoop },
+	0x0245: { n:"BrtSupNameErr", f:parsenoop },
+	0x0246: { n:"BrtSupNameSt", f:parsenoop },
+	0x0247: { n:"BrtSupNameNil", f:parsenoop },
+	0x0248: { n:"BrtSupNameBool", f:parsenoop },
+	0x0249: { n:"BrtSupNameFmla", f:parsenoop },
+	0x024A: { n:"BrtSupNameBits", f:parsenoop },
+	0x024B: { n:"BrtSupNameEnd", f:parsenoop },
+	0x024C: { n:"BrtEndSupBook", f:parsenoop },
+	0x024D: { n:"BrtCellSmartTagProperty", f:parsenoop },
+	0x024E: { n:"BrtBeginCellSmartTag", f:parsenoop },
+	0x024F: { n:"BrtEndCellSmartTag", f:parsenoop },
+	0x0250: { n:"BrtBeginCellSmartTags", f:parsenoop },
+	0x0251: { n:"BrtEndCellSmartTags", f:parsenoop },
+	0x0252: { n:"BrtBeginSmartTags", f:parsenoop },
+	0x0253: { n:"BrtEndSmartTags", f:parsenoop },
+	0x0254: { n:"BrtSmartTagType", f:parsenoop },
+	0x0255: { n:"BrtBeginSmartTagTypes", f:parsenoop },
+	0x0256: { n:"BrtEndSmartTagTypes", f:parsenoop },
+	0x0257: { n:"BrtBeginSXFilters", f:parsenoop },
+	0x0258: { n:"BrtEndSXFilters", f:parsenoop },
+	0x0259: { n:"BrtBeginSXFILTER", f:parsenoop },
+	0x025A: { n:"BrtEndSXFilter", f:parsenoop },
+	0x025B: { n:"BrtBeginFills", f:parsenoop },
+	0x025C: { n:"BrtEndFills", f:parsenoop },
+	0x025D: { n:"BrtBeginCellWatches", f:parsenoop },
+	0x025E: { n:"BrtEndCellWatches", f:parsenoop },
+	0x025F: { n:"BrtCellWatch", f:parsenoop },
+	0x0260: { n:"BrtBeginCRErrs", f:parsenoop },
+	0x0261: { n:"BrtEndCRErrs", f:parsenoop },
+	0x0262: { n:"BrtCrashRecErr", f:parsenoop },
+	0x0263: { n:"BrtBeginFonts", f:parsenoop },
+	0x0264: { n:"BrtEndFonts", f:parsenoop },
+	0x0265: { n:"BrtBeginBorders", f:parsenoop },
+	0x0266: { n:"BrtEndBorders", f:parsenoop },
+	0x0267: { n:"BrtBeginFmts", f:parsenoop },
+	0x0268: { n:"BrtEndFmts", f:parsenoop },
+	0x0269: { n:"BrtBeginCellXFs", f:parsenoop },
+	0x026A: { n:"BrtEndCellXFs", f:parsenoop },
+	0x026B: { n:"BrtBeginStyles", f:parsenoop },
+	0x026C: { n:"BrtEndStyles", f:parsenoop },
+	0x0271: { n:"BrtBigName", f:parsenoop },
+	0x0272: { n:"BrtBeginCellStyleXFs", f:parsenoop },
+	0x0273: { n:"BrtEndCellStyleXFs", f:parsenoop },
+	0x0274: { n:"BrtBeginComments", f:parsenoop },
+	0x0275: { n:"BrtEndComments", f:parsenoop },
+	0x0276: { n:"BrtBeginCommentAuthors", f:parsenoop },
+	0x0277: { n:"BrtEndCommentAuthors", f:parsenoop },
+	0x0278: { n:"BrtCommentAuthor", f:parse_BrtCommentAuthor },
+	0x0279: { n:"BrtBeginCommentList", f:parsenoop },
+	0x027A: { n:"BrtEndCommentList", f:parsenoop },
+	0x027B: { n:"BrtBeginComment", f:parse_BrtBeginComment},
+	0x027C: { n:"BrtEndComment", f:parsenoop },
+	0x027D: { n:"BrtCommentText", f:parse_BrtCommentText },
+	0x027E: { n:"BrtBeginOleObjects", f:parsenoop },
+	0x027F: { n:"BrtOleObject", f:parsenoop },
+	0x0280: { n:"BrtEndOleObjects", f:parsenoop },
+	0x0281: { n:"BrtBeginSxrules", f:parsenoop },
+	0x0282: { n:"BrtEndSxRules", f:parsenoop },
+	0x0283: { n:"BrtBeginActiveXControls", f:parsenoop },
+	0x0284: { n:"BrtActiveX", f:parsenoop },
+	0x0285: { n:"BrtEndActiveXControls", f:parsenoop },
+	0x0286: { n:"BrtBeginPCDSDTCEMembersSortBy", f:parsenoop },
+	0x0288: { n:"BrtBeginCellIgnoreECs", f:parsenoop },
+	0x0289: { n:"BrtCellIgnoreEC", f:parsenoop },
+	0x028A: { n:"BrtEndCellIgnoreECs", f:parsenoop },
+	0x028B: { n:"BrtCsProp", f:parsenoop },
+	0x028C: { n:"BrtCsPageSetup", f:parsenoop },
+	0x028D: { n:"BrtBeginUserCsViews", f:parsenoop },
+	0x028E: { n:"BrtEndUserCsViews", f:parsenoop },
+	0x028F: { n:"BrtBeginUserCsView", f:parsenoop },
+	0x0290: { n:"BrtEndUserCsView", f:parsenoop },
+	0x0291: { n:"BrtBeginPcdSFCIEntries", f:parsenoop },
+	0x0292: { n:"BrtEndPCDSFCIEntries", f:parsenoop },
+	0x0293: { n:"BrtPCDSFCIEntry", f:parsenoop },
+	0x0294: { n:"BrtBeginListParts", f:parsenoop },
+	0x0295: { n:"BrtListPart", f:parsenoop },
+	0x0296: { n:"BrtEndListParts", f:parsenoop },
+	0x0297: { n:"BrtSheetCalcProp", f:parsenoop },
+	0x0298: { n:"BrtBeginFnGroup", f:parsenoop },
+	0x0299: { n:"BrtFnGroup", f:parsenoop },
+	0x029A: { n:"BrtEndFnGroup", f:parsenoop },
+	0x029B: { n:"BrtSupAddin", f:parsenoop },
+	0x029C: { n:"BrtSXTDMPOrder", f:parsenoop },
+	0x029D: { n:"BrtCsProtection", f:parsenoop },
+	0x029F: { n:"BrtBeginWsSortMap", f:parsenoop },
+	0x02A0: { n:"BrtEndWsSortMap", f:parsenoop },
+	0x02A1: { n:"BrtBeginRRSort", f:parsenoop },
+	0x02A2: { n:"BrtEndRRSort", f:parsenoop },
+	0x02A3: { n:"BrtRRSortItem", f:parsenoop },
+	0x02A4: { n:"BrtFileSharingIso", f:parsenoop },
+	0x02A5: { n:"BrtBookProtectionIso", f:parsenoop },
+	0x02A6: { n:"BrtSheetProtectionIso", f:parsenoop },
+	0x02A7: { n:"BrtCsProtectionIso", f:parsenoop },
+	0x02A8: { n:"BrtRangeProtectionIso", f:parsenoop },
+	0x0400: { n:"BrtRwDescent", f:parsenoop },
+	0x0401: { n:"BrtKnownFonts", f:parsenoop },
+	0x0402: { n:"BrtBeginSXTupleSet", f:parsenoop },
+	0x0403: { n:"BrtEndSXTupleSet", f:parsenoop },
+	0x0404: { n:"BrtBeginSXTupleSetHeader", f:parsenoop },
+	0x0405: { n:"BrtEndSXTupleSetHeader", f:parsenoop },
+	0x0406: { n:"BrtSXTupleSetHeaderItem", f:parsenoop },
+	0x0407: { n:"BrtBeginSXTupleSetData", f:parsenoop },
+	0x0408: { n:"BrtEndSXTupleSetData", f:parsenoop },
+	0x0409: { n:"BrtBeginSXTupleSetRow", f:parsenoop },
+	0x040A: { n:"BrtEndSXTupleSetRow", f:parsenoop },
+	0x040B: { n:"BrtSXTupleSetRowItem", f:parsenoop },
+	0x040C: { n:"BrtNameExt", f:parsenoop },
+	0x040D: { n:"BrtPCDH14", f:parsenoop },
+	0x040E: { n:"BrtBeginPCDCalcMem14", f:parsenoop },
+	0x040F: { n:"BrtEndPCDCalcMem14", f:parsenoop },
+	0x0410: { n:"BrtSXTH14", f:parsenoop },
+	0x0411: { n:"BrtBeginSparklineGroup", f:parsenoop },
+	0x0412: { n:"BrtEndSparklineGroup", f:parsenoop },
+	0x0413: { n:"BrtSparkline", f:parsenoop },
+	0x0414: { n:"BrtSXDI14", f:parsenoop },
+	0x0415: { n:"BrtWsFmtInfoEx14", f:parsenoop },
+	0x0416: { n:"BrtBeginConditionalFormatting14", f:parsenoop },
+	0x0417: { n:"BrtEndConditionalFormatting14", f:parsenoop },
+	0x0418: { n:"BrtBeginCFRule14", f:parsenoop },
+	0x0419: { n:"BrtEndCFRule14", f:parsenoop },
+	0x041A: { n:"BrtCFVO14", f:parsenoop },
+	0x041B: { n:"BrtBeginDatabar14", f:parsenoop },
+	0x041C: { n:"BrtBeginIconSet14", f:parsenoop },
+	0x041D: { n:"BrtDVal14", f:parsenoop },
+	0x041E: { n:"BrtBeginDVals14", f:parsenoop },
+	0x041F: { n:"BrtColor14", f:parsenoop },
+	0x0420: { n:"BrtBeginSparklines", f:parsenoop },
+	0x0421: { n:"BrtEndSparklines", f:parsenoop },
+	0x0422: { n:"BrtBeginSparklineGroups", f:parsenoop },
+	0x0423: { n:"BrtEndSparklineGroups", f:parsenoop },
+	0x0425: { n:"BrtSXVD14", f:parsenoop },
+	0x0426: { n:"BrtBeginSxview14", f:parsenoop },
+	0x0427: { n:"BrtEndSxview14", f:parsenoop },
+	0x042A: { n:"BrtBeginPCD14", f:parsenoop },
+	0x042B: { n:"BrtEndPCD14", f:parsenoop },
+	0x042C: { n:"BrtBeginExtConn14", f:parsenoop },
+	0x042D: { n:"BrtEndExtConn14", f:parsenoop },
+	0x042E: { n:"BrtBeginSlicerCacheIDs", f:parsenoop },
+	0x042F: { n:"BrtEndSlicerCacheIDs", f:parsenoop },
+	0x0430: { n:"BrtBeginSlicerCacheID", f:parsenoop },
+	0x0431: { n:"BrtEndSlicerCacheID", f:parsenoop },
+	0x0433: { n:"BrtBeginSlicerCache", f:parsenoop },
+	0x0434: { n:"BrtEndSlicerCache", f:parsenoop },
+	0x0435: { n:"BrtBeginSlicerCacheDef", f:parsenoop },
+	0x0436: { n:"BrtEndSlicerCacheDef", f:parsenoop },
+	0x0437: { n:"BrtBeginSlicersEx", f:parsenoop },
+	0x0438: { n:"BrtEndSlicersEx", f:parsenoop },
+	0x0439: { n:"BrtBeginSlicerEx", f:parsenoop },
+	0x043A: { n:"BrtEndSlicerEx", f:parsenoop },
+	0x043B: { n:"BrtBeginSlicer", f:parsenoop },
+	0x043C: { n:"BrtEndSlicer", f:parsenoop },
+	0x043D: { n:"BrtSlicerCachePivotTables", f:parsenoop },
+	0x043E: { n:"BrtBeginSlicerCacheOlapImpl", f:parsenoop },
+	0x043F: { n:"BrtEndSlicerCacheOlapImpl", f:parsenoop },
+	0x0440: { n:"BrtBeginSlicerCacheLevelsData", f:parsenoop },
+	0x0441: { n:"BrtEndSlicerCacheLevelsData", f:parsenoop },
+	0x0442: { n:"BrtBeginSlicerCacheLevelData", f:parsenoop },
+	0x0443: { n:"BrtEndSlicerCacheLevelData", f:parsenoop },
+	0x0444: { n:"BrtBeginSlicerCacheSiRanges", f:parsenoop },
+	0x0445: { n:"BrtEndSlicerCacheSiRanges", f:parsenoop },
+	0x0446: { n:"BrtBeginSlicerCacheSiRange", f:parsenoop },
+	0x0447: { n:"BrtEndSlicerCacheSiRange", f:parsenoop },
+	0x0448: { n:"BrtSlicerCacheOlapItem", f:parsenoop },
+	0x0449: { n:"BrtBeginSlicerCacheSelections", f:parsenoop },
+	0x044A: { n:"BrtSlicerCacheSelection", f:parsenoop },
+	0x044B: { n:"BrtEndSlicerCacheSelections", f:parsenoop },
+	0x044C: { n:"BrtBeginSlicerCacheNative", f:parsenoop },
+	0x044D: { n:"BrtEndSlicerCacheNative", f:parsenoop },
+	0x044E: { n:"BrtSlicerCacheNativeItem", f:parsenoop },
+	0x044F: { n:"BrtRangeProtection14", f:parsenoop },
+	0x0450: { n:"BrtRangeProtectionIso14", f:parsenoop },
+	0x0451: { n:"BrtCellIgnoreEC14", f:parsenoop },
+	0x0457: { n:"BrtList14", f:parsenoop },
+	0x0458: { n:"BrtCFIcon", f:parsenoop },
+	0x0459: { n:"BrtBeginSlicerCachesPivotCacheIDs", f:parsenoop },
+	0x045A: { n:"BrtEndSlicerCachesPivotCacheIDs", f:parsenoop },
+	0x045B: { n:"BrtBeginSlicers", f:parsenoop },
+	0x045C: { n:"BrtEndSlicers", f:parsenoop },
+	0x045D: { n:"BrtWbProp14", f:parsenoop },
+	0x045E: { n:"BrtBeginSXEdit", f:parsenoop },
+	0x045F: { n:"BrtEndSXEdit", f:parsenoop },
+	0x0460: { n:"BrtBeginSXEdits", f:parsenoop },
+	0x0461: { n:"BrtEndSXEdits", f:parsenoop },
+	0x0462: { n:"BrtBeginSXChange", f:parsenoop },
+	0x0463: { n:"BrtEndSXChange", f:parsenoop },
+	0x0464: { n:"BrtBeginSXChanges", f:parsenoop },
+	0x0465: { n:"BrtEndSXChanges", f:parsenoop },
+	0x0466: { n:"BrtSXTupleItems", f:parsenoop },
+	0x0468: { n:"BrtBeginSlicerStyle", f:parsenoop },
+	0x0469: { n:"BrtEndSlicerStyle", f:parsenoop },
+	0x046A: { n:"BrtSlicerStyleElement", f:parsenoop },
+	0x046B: { n:"BrtBeginStyleSheetExt14", f:parsenoop },
+	0x046C: { n:"BrtEndStyleSheetExt14", f:parsenoop },
+	0x046D: { n:"BrtBeginSlicerCachesPivotCacheID", f:parsenoop },
+	0x046E: { n:"BrtEndSlicerCachesPivotCacheID", f:parsenoop },
+	0x046F: { n:"BrtBeginConditionalFormattings", f:parsenoop },
+	0x0470: { n:"BrtEndConditionalFormattings", f:parsenoop },
+	0x0471: { n:"BrtBeginPCDCalcMemExt", f:parsenoop },
+	0x0472: { n:"BrtEndPCDCalcMemExt", f:parsenoop },
+	0x0473: { n:"BrtBeginPCDCalcMemsExt", f:parsenoop },
+	0x0474: { n:"BrtEndPCDCalcMemsExt", f:parsenoop },
+	0x0475: { n:"BrtPCDField14", f:parsenoop },
+	0x0476: { n:"BrtBeginSlicerStyles", f:parsenoop },
+	0x0477: { n:"BrtEndSlicerStyles", f:parsenoop },
+	0x0478: { n:"BrtBeginSlicerStyleElements", f:parsenoop },
+	0x0479: { n:"BrtEndSlicerStyleElements", f:parsenoop },
+	0x047A: { n:"BrtCFRuleExt", f:parsenoop },
+	0x047B: { n:"BrtBeginSXCondFmt14", f:parsenoop },
+	0x047C: { n:"BrtEndSXCondFmt14", f:parsenoop },
+	0x047D: { n:"BrtBeginSXCondFmts14", f:parsenoop },
+	0x047E: { n:"BrtEndSXCondFmts14", f:parsenoop },
+	0x0480: { n:"BrtBeginSortCond14", f:parsenoop },
+	0x0481: { n:"BrtEndSortCond14", f:parsenoop },
+	0x0482: { n:"BrtEndDVals14", f:parsenoop },
+	0x0483: { n:"BrtEndIconSet14", f:parsenoop },
+	0x0484: { n:"BrtEndDatabar14", f:parsenoop },
+	0x0485: { n:"BrtBeginColorScale14", f:parsenoop },
+	0x0486: { n:"BrtEndColorScale14", f:parsenoop },
+	0x0487: { n:"BrtBeginSxrules14", f:parsenoop },
+	0x0488: { n:"BrtEndSxrules14", f:parsenoop },
+	0x0489: { n:"BrtBeginPRule14", f:parsenoop },
+	0x048A: { n:"BrtEndPRule14", f:parsenoop },
+	0x048B: { n:"BrtBeginPRFilters14", f:parsenoop },
+	0x048C: { n:"BrtEndPRFilters14", f:parsenoop },
+	0x048D: { n:"BrtBeginPRFilter14", f:parsenoop },
+	0x048E: { n:"BrtEndPRFilter14", f:parsenoop },
+	0x048F: { n:"BrtBeginPRFItem14", f:parsenoop },
+	0x0490: { n:"BrtEndPRFItem14", f:parsenoop },
+	0x0491: { n:"BrtBeginCellIgnoreECs14", f:parsenoop },
+	0x0492: { n:"BrtEndCellIgnoreECs14", f:parsenoop },
+	0x0493: { n:"BrtDxf14", f:parsenoop },
+	0x0494: { n:"BrtBeginDxF14s", f:parsenoop },
+	0x0495: { n:"BrtEndDxf14s", f:parsenoop },
+	0x0499: { n:"BrtFilter14", f:parsenoop },
+	0x049A: { n:"BrtBeginCustomFilters14", f:parsenoop },
+	0x049C: { n:"BrtCustomFilter14", f:parsenoop },
+	0x049D: { n:"BrtIconFilter14", f:parsenoop },
+	0x049E: { n:"BrtPivotCacheConnectionName", f:parsenoop },
+	0x0800: { n:"BrtBeginDecoupledPivotCacheIDs", f:parsenoop },
+	0x0801: { n:"BrtEndDecoupledPivotCacheIDs", f:parsenoop },
+	0x0802: { n:"BrtDecoupledPivotCacheID", f:parsenoop },
+	0x0803: { n:"BrtBeginPivotTableRefs", f:parsenoop },
+	0x0804: { n:"BrtEndPivotTableRefs", f:parsenoop },
+	0x0805: { n:"BrtPivotTableRef", f:parsenoop },
+	0x0806: { n:"BrtSlicerCacheBookPivotTables", f:parsenoop },
+	0x0807: { n:"BrtBeginSxvcells", f:parsenoop },
+	0x0808: { n:"BrtEndSxvcells", f:parsenoop },
+	0x0809: { n:"BrtBeginSxRow", f:parsenoop },
+	0x080A: { n:"BrtEndSxRow", f:parsenoop },
+	0x080C: { n:"BrtPcdCalcMem15", f:parsenoop },
+	0x0813: { n:"BrtQsi15", f:parsenoop },
+	0x0814: { n:"BrtBeginWebExtensions", f:parsenoop },
+	0x0815: { n:"BrtEndWebExtensions", f:parsenoop },
+	0x0816: { n:"BrtWebExtension", f:parsenoop },
+	0x0817: { n:"BrtAbsPath15", f:parsenoop },
+	0x0818: { n:"BrtBeginPivotTableUISettings", f:parsenoop },
+	0x0819: { n:"BrtEndPivotTableUISettings", f:parsenoop },
+	0x081B: { n:"BrtTableSlicerCacheIDs", f:parsenoop },
+	0x081C: { n:"BrtTableSlicerCacheID", f:parsenoop },
+	0x081D: { n:"BrtBeginTableSlicerCache", f:parsenoop },
+	0x081E: { n:"BrtEndTableSlicerCache", f:parsenoop },
+	0x081F: { n:"BrtSxFilter15", f:parsenoop },
+	0x0820: { n:"BrtBeginTimelineCachePivotCacheIDs", f:parsenoop },
+	0x0821: { n:"BrtEndTimelineCachePivotCacheIDs", f:parsenoop },
+	0x0822: { n:"BrtTimelineCachePivotCacheID", f:parsenoop },
+	0x0823: { n:"BrtBeginTimelineCacheIDs", f:parsenoop },
+	0x0824: { n:"BrtEndTimelineCacheIDs", f:parsenoop },
+	0x0825: { n:"BrtBeginTimelineCacheID", f:parsenoop },
+	0x0826: { n:"BrtEndTimelineCacheID", f:parsenoop },
+	0x0827: { n:"BrtBeginTimelinesEx", f:parsenoop },
+	0x0828: { n:"BrtEndTimelinesEx", f:parsenoop },
+	0x0829: { n:"BrtBeginTimelineEx", f:parsenoop },
+	0x082A: { n:"BrtEndTimelineEx", f:parsenoop },
+	0x082B: { n:"BrtWorkBookPr15", f:parsenoop },
+	0x082C: { n:"BrtPCDH15", f:parsenoop },
+	0x082D: { n:"BrtBeginTimelineStyle", f:parsenoop },
+	0x082E: { n:"BrtEndTimelineStyle", f:parsenoop },
+	0x082F: { n:"BrtTimelineStyleElement", f:parsenoop },
+	0x0830: { n:"BrtBeginTimelineStylesheetExt15", f:parsenoop },
+	0x0831: { n:"BrtEndTimelineStylesheetExt15", f:parsenoop },
+	0x0832: { n:"BrtBeginTimelineStyles", f:parsenoop },
+	0x0833: { n:"BrtEndTimelineStyles", f:parsenoop },
+	0x0834: { n:"BrtBeginTimelineStyleElements", f:parsenoop },
+	0x0835: { n:"BrtEndTimelineStyleElements", f:parsenoop },
+	0x0836: { n:"BrtDxf15", f:parsenoop },
+	0x0837: { n:"BrtBeginDxfs15", f:parsenoop },
+	0x0838: { n:"brtEndDxfs15", f:parsenoop },
+	0x0839: { n:"BrtSlicerCacheHideItemsWithNoData", f:parsenoop },
+	0x083A: { n:"BrtBeginItemUniqueNames", f:parsenoop },
+	0x083B: { n:"BrtEndItemUniqueNames", f:parsenoop },
+	0x083C: { n:"BrtItemUniqueName", f:parsenoop },
+	0x083D: { n:"BrtBeginExtConn15", f:parsenoop },
+	0x083E: { n:"BrtEndExtConn15", f:parsenoop },
+	0x083F: { n:"BrtBeginOledbPr15", f:parsenoop },
+	0x0840: { n:"BrtEndOledbPr15", f:parsenoop },
+	0x0841: { n:"BrtBeginDataFeedPr15", f:parsenoop },
+	0x0842: { n:"BrtEndDataFeedPr15", f:parsenoop },
+	0x0843: { n:"BrtTextPr15", f:parsenoop },
+	0x0844: { n:"BrtRangePr15", f:parsenoop },
+	0x0845: { n:"BrtDbCommand15", f:parsenoop },
+	0x0846: { n:"BrtBeginDbTables15", f:parsenoop },
+	0x0847: { n:"BrtEndDbTables15", f:parsenoop },
+	0x0848: { n:"BrtDbTable15", f:parsenoop },
+	0x0849: { n:"BrtBeginDataModel", f:parsenoop },
+	0x084A: { n:"BrtEndDataModel", f:parsenoop },
+	0x084B: { n:"BrtBeginModelTables", f:parsenoop },
+	0x084C: { n:"BrtEndModelTables", f:parsenoop },
+	0x084D: { n:"BrtModelTable", f:parsenoop },
+	0x084E: { n:"BrtBeginModelRelationships", f:parsenoop },
+	0x084F: { n:"BrtEndModelRelationships", f:parsenoop },
+	0x0850: { n:"BrtModelRelationship", f:parsenoop },
+	0x0851: { n:"BrtBeginECTxtWiz15", f:parsenoop },
+	0x0852: { n:"BrtEndECTxtWiz15", f:parsenoop },
+	0x0853: { n:"BrtBeginECTWFldInfoLst15", f:parsenoop },
+	0x0854: { n:"BrtEndECTWFldInfoLst15", f:parsenoop },
+	0x0855: { n:"BrtBeginECTWFldInfo15", f:parsenoop },
+	0x0856: { n:"BrtFieldListActiveItem", f:parsenoop },
+	0x0857: { n:"BrtPivotCacheIdVersion", f:parsenoop },
+	0x0858: { n:"BrtSXDI15", f:parsenoop },
+	0xFFFF: { n:"", f:parsenoop }
+};
+
+var evert_RE = evert_key(RecordEnum, 'n');
+function fix_opts_func(defaults) {
+	return function fix_opts(opts) {
+		for(var i = 0; i != defaults.length; ++i) {
+			var d = defaults[i];
+			if(opts[d[0]] === undefined) opts[d[0]] = d[1];
+			if(d[2] === 'n') opts[d[0]] = Number(opts[d[0]]);
+		}
+	};
+}
+
+var fix_read_opts = fix_opts_func([
+	['cellNF', false], /* emit cell number format string as .z */
+	['cellHTML', true], /* emit html string as .h */
+	['cellFormula', true], /* emit formulae as .f */
+	['cellStyles', false], /* emits style/theme as .s */
+
+	['sheetStubs', false], /* emit empty cells */
+	['sheetRows', 0, 'n'], /* read n rows (0 = read all rows) */
+
+	['bookDeps', false], /* parse calculation chains */
+	['bookSheets', false], /* only try to get sheet names (no Sheets) */
+	['bookProps', false], /* only try to get properties (no Sheets) */
+	['bookFiles', false], /* include raw file structure (keys, files) */
+	['bookVBA', false], /* include vba raw data (vbaraw) */
+
+	['WTF', false] /* WTF mode (throws errors) */
+]);
+
+
+var fix_write_opts = fix_opts_func([
+	['bookSST', false], /* Generate Shared String Table */
+
+	['bookType', 'xlsx'], /* Type of workbook (xlsx/m/b) */
+
+	['WTF', false] /* WTF mode (throws errors) */
+]);
+function safe_parse_wbrels(wbrels, sheets) {
+	if(!wbrels) return 0;
+	try {
+		wbrels = sheets.map(function pwbr(w) { return [w.name, wbrels['!id'][w.id].Target]; });
+	} catch(e) { return null; }
+	return !wbrels || wbrels.length === 0 ? null : wbrels;
+}
+
+function safe_parse_ws(zip, path, relsPath, sheet, sheetRels, sheets, opts) {
+	try {
+		sheetRels[sheet]=parse_rels(getzipdata(zip, relsPath, true), path);
+		sheets[sheet]=parse_ws(getzipdata(zip, path),path,opts,sheetRels[sheet]);
+	} catch(e) { if(opts.WTF) throw e; }
+}
+
+var nodirs = function nodirs(x){return x.substr(-1) != '/';};
+function parse_zip(zip, opts) {
+	make_ssf(SSF);
+	opts = opts || {};
+	fix_read_opts(opts);
+	reset_cp();
+	var entries = keys(zip.files).filter(nodirs).sort();
+	var dir = parse_ct(getzipdata(zip, '[Content_Types].xml'), opts);
+	var xlsb = false;
+	var sheets, binname;
+	if(dir.workbooks.length === 0) {
+		binname = "xl/workbook.xml";
+		if(getzipdata(zip,binname, true)) dir.workbooks.push(binname);
+	}
+	if(dir.workbooks.length === 0) {
+		binname = "xl/workbook.bin";
+		if(!getzipfile(zip,binname,true)) throw new Error("Could not find workbook");
+		dir.workbooks.push(binname);
+		xlsb = true;
+	}
+	if(dir.workbooks[0].substr(-3) == "bin") xlsb = true;
+	if(xlsb) set_cp(1200);
+
+	if(!opts.bookSheets && !opts.bookProps) {
+		strs = [];
+		if(dir.sst) strs=parse_sst(getzipdata(zip, dir.sst.replace(/^\//,'')), dir.sst, opts);
+
+		styles = {};
+		if(dir.style) styles = parse_sty(getzipdata(zip, dir.style.replace(/^\//,'')),dir.style, opts);
+
+		themes = {};
+		if(opts.cellStyles && dir.themes.length) themes = parse_theme(getzipdata(zip, dir.themes[0].replace(/^\//,''), true),dir.themes[0], opts);
+	}
+
+	var wb = parse_wb(getzipdata(zip, dir.workbooks[0].replace(/^\//,'')), dir.workbooks[0], opts);
+
+	var props = {}, propdata = "";
+
+	if(dir.coreprops.length !== 0) {
+		propdata = getzipdata(zip, dir.coreprops[0].replace(/^\//,''), true);
+		if(propdata) props = parse_core_props(propdata);
+		if(dir.extprops.length !== 0) {
+			propdata = getzipdata(zip, dir.extprops[0].replace(/^\//,''), true);
+			if(propdata) parse_ext_props(propdata, props);
+		}
+	}
+
+	var custprops = {};
+	if(!opts.bookSheets || opts.bookProps) {
+		if (dir.custprops.length !== 0) {
+			propdata = getzipdata(zip, dir.custprops[0].replace(/^\//,''), true);
+			if(propdata) custprops = parse_cust_props(propdata, opts);
+		}
+	}
+
+	var out = {};
+	if(opts.bookSheets || opts.bookProps) {
+		if(props.Worksheets && props.SheetNames.length > 0) sheets=props.SheetNames;
+		else if(wb.Sheets) sheets = wb.Sheets.map(function pluck(x){ return x.name; });
+		if(opts.bookProps) { out.Props = props; out.Custprops = custprops; }
+		if(typeof sheets !== 'undefined') out.SheetNames = sheets;
+		if(opts.bookSheets ? out.SheetNames : opts.bookProps) return out;
+	}
+	sheets = {};
+
+	var deps = {};
+	if(opts.bookDeps && dir.calcchain) deps=parse_cc(getzipdata(zip, dir.calcchain.replace(/^\//,'')),dir.calcchain,opts);
+
+	var i=0;
+	var sheetRels = {};
+	var path, relsPath;
+	if(!props.Worksheets) {
+		var wbsheets = wb.Sheets;
+		props.Worksheets = wbsheets.length;
+		props.SheetNames = [];
+		for(var j = 0; j != wbsheets.length; ++j) {
+			props.SheetNames[j] = wbsheets[j].name;
+		}
+	}
+
+	var wbext = xlsb ? "bin" : "xml";
+	var wbrelsfile = 'xl/_rels/workbook.' + wbext + '.rels';
+	var wbrels = parse_rels(getzipdata(zip, wbrelsfile, true), wbrelsfile);
+	if(wbrels) wbrels = safe_parse_wbrels(wbrels, wb.Sheets);
+	/* Numbers iOS hack */
+	var nmode = (getzipdata(zip,"xl/worksheets/sheet.xml",true))?1:0;
+	for(i = 0; i != props.Worksheets; ++i) {
+		if(wbrels) path = 'xl/' + (wbrels[i][1]).replace(/[\/]?xl\//, "");
+		else {
+			path = 'xl/worksheets/sheet'+(i+1-nmode)+"." + wbext;
+			path = path.replace(/sheet0\./,"sheet.");
+		}
+		relsPath = path.replace(/^(.*)(\/)([^\/]*)$/, "$1/_rels/$3.rels");
+		safe_parse_ws(zip, path, relsPath, props.SheetNames[i], sheetRels, sheets, opts);
+	}
+
+	if(dir.comments) parse_comments(zip, dir.comments, sheets, sheetRels, opts);
+
+	out = {
+		Directory: dir,
+		Workbook: wb,
+		Props: props,
+		Custprops: custprops,
+		Deps: deps,
+		Sheets: sheets,
+		SheetNames: props.SheetNames,
+		Strings: strs,
+		Styles: styles,
+		Themes: themes,
+		SSF: SSF.get_table()
+	};
+	if(opts.bookFiles) {
+		out.keys = entries;
+		out.files = zip.files;
+	}
+	if(opts.bookVBA) {
+		if(dir.vba.length > 0) out.vbaraw = getzipdata(zip,dir.vba[0],true);
+		else if(dir.defaults.bin === 'application/vnd.ms-office.vbaProject') out.vbaraw = getzipdata(zip,'xl/vbaProject.bin',true);
+	}
+	return out;
+}
+function add_rels(rels, rId, f, type, relobj) {
+	if(!relobj) relobj = {};
+	if(!rels['!id']) rels['!id'] = {};
+	relobj.Id = 'rId' + rId;
+	relobj.Type = type;
+	relobj.Target = f;
+	if(rels['!id'][relobj.Id]) throw new Error("Cannot rewrite rId " + rId);
+	rels['!id'][relobj.Id] = relobj;
+	rels[('/' + relobj.Target).replace("//","/")] = relobj;
+}
+
+function write_zip(wb, opts) {
+	if(wb && !wb.SSF) {
+		wb.SSF = SSF.get_table();
+	}
+	if(wb && wb.SSF) {
+		make_ssf(SSF); SSF.load_table(wb.SSF);
+		opts.revssf = evert_num(wb.SSF); opts.revssf[wb.SSF[65535]] = 0;
+	}
+	opts.rels = {}; opts.wbrels = {};
+	opts.Strings = []; opts.Strings.Count = 0; opts.Strings.Unique = 0;
+	var wbext = opts.bookType == "xlsb" ? "bin" : "xml";
+	var ct = { workbooks: [], sheets: [], calcchains: [], themes: [], styles: [],
+		coreprops: [], extprops: [], custprops: [], strs:[], comments: [], vba: [],
+		TODO:[], rels:[], xmlns: "" };
+	fix_write_opts(opts = opts || {});
+	var zip = new jszip();
+	var f = "", rId = 0;
+
+	opts.cellXfs = [];
+	get_cell_style(opts.cellXfs, {}, {revssf:{"General":0}});
+
+	f = "docProps/core.xml";
+	zip.file(f, write_core_props(wb.Props, opts));
+	ct.coreprops.push(f);
+	add_rels(opts.rels, 2, f, RELS.CORE_PROPS);
+
+	f = "docProps/app.xml";
+	if(!wb.Props) wb.Props = {};
+	wb.Props.SheetNames = wb.SheetNames;
+	wb.Props.Worksheets = wb.SheetNames.length;
+	zip.file(f, write_ext_props(wb.Props, opts));
+	ct.extprops.push(f);
+	add_rels(opts.rels, 3, f, RELS.EXT_PROPS);
+
+	if(wb.Custprops !== wb.Props && keys(wb.Custprops||{}).length > 0) {
+		f = "docProps/custom.xml";
+		zip.file(f, write_cust_props(wb.Custprops, opts));
+		ct.custprops.push(f);
+		add_rels(opts.rels, 4, f, RELS.CUST_PROPS);
+	}
+
+	f = "xl/workbook." + wbext;
+	zip.file(f, write_wb(wb, f, opts));
+	ct.workbooks.push(f);
+	add_rels(opts.rels, 1, f, RELS.WB);
+
+	for(rId=1;rId <= wb.SheetNames.length; ++rId) {
+		f = "xl/worksheets/sheet" + rId + "." + wbext;
+		zip.file(f, write_ws(rId-1, f, opts, wb));
+		ct.sheets.push(f);
+		add_rels(opts.wbrels, rId, "worksheets/sheet" + rId + "." + wbext, RELS.WS);
+	}
+
+	if(opts.Strings != null && opts.Strings.length > 0) {
+		f = "xl/sharedStrings." + wbext;
+		zip.file(f, write_sst(opts.Strings, f, opts));
+		ct.strs.push(f);
+		add_rels(opts.wbrels, ++rId, "sharedStrings." + wbext, RELS.SST);
+	}
+
+	/* TODO: something more intelligent with themes */
+
+	f = "xl/theme/theme1.xml";
+	zip.file(f, write_theme());
+	ct.themes.push(f);
+	add_rels(opts.wbrels, ++rId, "theme/theme1.xml", RELS.THEME);
+
+	/* TODO: something more intelligent with styles */
+
+	f = "xl/styles." + wbext;
+	zip.file(f, write_sty(wb, f, opts));
+	ct.styles.push(f);
+	add_rels(opts.wbrels, ++rId, "styles." + wbext, RELS.STY);
+
+	zip.file("[Content_Types].xml", write_ct(ct, opts));
+	zip.file('_rels/.rels', write_rels(opts.rels));
+	zip.file('xl/_rels/workbook.' + wbext + '.rels', write_rels(opts.wbrels));
+	return zip;
+}
+function readSync(data, opts) {
+	var zip, d = data;
+	var o = opts||{};
+	if(!o.type) o.type = (has_buf && Buffer.isBuffer(data)) ? "buffer" : "base64";
+	switch(o.type) {
+		case "base64": zip = new jszip(d, { base64:true }); break;
+		case "binary": zip = new jszip(d, { base64:false }); break;
+		case "buffer": zip = new jszip(d); break;
+		case "file": zip=new jszip(d=_fs.readFileSync(data)); break;
+		default: throw new Error("Unrecognized type " + o.type);
+	}
+	return parse_zip(zip, o);
+}
+
+function readFileSync(data, opts) {
+	var o = opts||{}; o.type = 'file';
+	return readSync(data, o);
+}
+
+function writeSync(wb, opts) {
+	var o = opts||{};
+	var z = write_zip(wb, o);
+	switch(o.type) {
+		case "base64": return z.generate({type:"base64"});
+		case "binary": return z.generate({type:"string"});
+		case "buffer": return z.generate({type:"nodebuffer"});
+		case "file": return _fs.writeFileSync(o.file, z.generate({type:"nodebuffer"}));
+		default: throw new Error("Unrecognized type " + o.type);
+	}
+}
+
+function writeFileSync(wb, filename, opts) {
+	var o = opts||{}; o.type = 'file';
+	o.file = filename;
+	switch(o.file.substr(-5).toLowerCase()) {
+		case '.xlsm': o.bookType = 'xlsm'; break;
+		case '.xlsb': o.bookType = 'xlsb'; break;
+	}
+	return writeSync(wb, o);
+}
+
+function decode_row(rowstr) { return parseInt(unfix_row(rowstr),10) - 1; }
+function encode_row(row) { return "" + (row + 1); }
+function fix_row(cstr) { return cstr.replace(/([A-Z]|^)(\d+)$/,"$1$$$2"); }
+function unfix_row(cstr) { return cstr.replace(/\$(\d+)$/,"$1"); }
+
+function decode_col(colstr) { var c = unfix_col(colstr), d = 0, i = 0; for(; i !== c.length; ++i) d = 26*d + c.charCodeAt(i) - 64; return d - 1; }
+function encode_col(col) { var s=""; for(++col; col; col=Math.floor((col-1)/26)) s = String.fromCharCode(((col-1)%26) + 65) + s; return s; }
+function fix_col(cstr) { return cstr.replace(/^([A-Z])/,"$$$1"); }
+function unfix_col(cstr) { return cstr.replace(/^\$([A-Z])/,"$1"); }
+
+function split_cell(cstr) { return cstr.replace(/(\$?[A-Z]*)(\$?\d*)/,"$1,$2").split(","); }
+function decode_cell(cstr) { var splt = split_cell(cstr); return { c:decode_col(splt[0]), r:decode_row(splt[1]) }; }
+function encode_cell(cell) { return encode_col(cell.c) + encode_row(cell.r); }
+function fix_cell(cstr) { return fix_col(fix_row(cstr)); }
+function unfix_cell(cstr) { return unfix_col(unfix_row(cstr)); }
+function decode_range(range) { var x =range.split(":").map(decode_cell); return {s:x[0],e:x[x.length-1]}; }
+function encode_range(cs,ce) {
+	if(ce === undefined || typeof ce === 'number') return encode_range(cs.s, cs.e);
+	if(typeof cs !== 'string') cs = encode_cell(cs); if(typeof ce !== 'string') ce = encode_cell(ce);
+	return cs == ce ? cs : cs + ":" + ce;
+}
+
+function safe_decode_range(range) {
+	var o = {s:{c:0,r:0},e:{c:0,r:0}};
+	var idx = 0, i = 0, cc = 0;
+	var len = range.length;
+	for(idx = 0; i < len; ++i) {
+		if((cc=range.charCodeAt(i)-64) < 1 || cc > 26) break;
+		idx = 26*idx + cc;
+	}
+	o.s.c = --idx;
+
+	for(idx = 0; i < len; ++i) {
+		if((cc=range.charCodeAt(i)-48) < 0 || cc > 9) break;
+		idx = 10*idx + cc;
+	}
+	o.s.r = --idx;
+
+	if(i === len || range.charCodeAt(++i) === 58) { o.e.c=o.s.c; o.e.r=o.s.r; return o; }
+
+	for(idx = 0; i != len; ++i) {
+		if((cc=range.charCodeAt(i)-64) < 1 || cc > 26) break;
+		idx = 26*idx + cc;
+	}
+	o.e.c = --idx;
+
+	for(idx = 0; i != len; ++i) {
+		if((cc=range.charCodeAt(i)-48) < 0 || cc > 9) break;
+		idx = 10*idx + cc;
+	}
+	o.e.r = --idx;
+	return o;
+}
+
+function safe_format_cell(cell, v) {
+	if(cell.z !== undefined) try { return (cell.w = SSF.format(cell.z, v)); } catch(e) { }
+	if(!cell.XF) return v;
+	try { return (cell.w = SSF.format(cell.XF.ifmt||0, v)); } catch(e) { return ''+v; }
+}
+
+function format_cell(cell, v) {
+	if(cell == null || cell.t == null) return "";
+	if(cell.w !== undefined) return cell.w;
+	if(v === undefined) return safe_format_cell(cell, cell.v);
+	return safe_format_cell(cell, v);
+}
+
+function sheet_to_json(sheet, opts){
+	var val, row, range, header = 0, offset = 1, r, hdr = [], isempty, R, C, v;
+	var o = opts != null ? opts : {};
+	var raw = o.raw;
+	if(sheet == null || sheet["!ref"] == null) return [];
+	range = o.range !== undefined ? o.range : sheet["!ref"];
+	if(o.header === 1) header = 1;
+	else if(o.header === "A") header = 2;
+	else if(Array.isArray(o.header)) header = 3;
+	switch(typeof range) {
+		case 'string': r = safe_decode_range(range); break;
+		case 'number': r = safe_decode_range(sheet["!ref"]); r.s.r = range; break;
+		default: r = range;
+	}
+	if(header > 0) offset = 0;
+	var rr = encode_row(r.s.r);
+	var cols = new Array(r.e.c-r.s.c+1);
+	var out = new Array(r.e.r-r.s.r-offset+1);
+	var outi = 0;
+	for(C = r.s.c; C <= r.e.c; ++C) {
+		cols[C] = encode_col(C);
+		val = sheet[cols[C] + rr];
+		switch(header) {
+			case 1: hdr[C] = C; break;
+			case 2: hdr[C] = cols[C]; break;
+			case 3: hdr[C] = o.header[C - r.s.c]; break;
+			default:
+				if(val === undefined) continue;
+				hdr[C] = format_cell(val);
+		}
+	}
+
+	for (R = r.s.r + offset; R <= r.e.r; ++R) {
+		rr = encode_row(R);
+		isempty = true;
+		row = header === 1 ? [] : Object.create({ __rowNum__ : R });
+		for (C = r.s.c; C <= r.e.c; ++C) {
+			val = sheet[cols[C] + rr];
+			if(val === undefined || val.t === undefined) continue;
+			v = val.v;
+			switch(val.t){
+				case 'e': continue;
+				case 's': case 'str': break;
+				case 'b': case 'n': break;
+				default: throw 'unrecognized type ' + val.t;
+			}
+			if(v !== undefined) {
+				row[hdr[C]] = raw ? v : format_cell(val,v);
+				isempty = false;
+			}
+		}
+		if(isempty === false) out[outi++] = row;
+	}
+	out.length = outi;
+	return out;
+}
+
+function sheet_to_row_object_array(sheet, opts) { return sheet_to_json(sheet, opts != null ? opts : {}); }
+
+function sheet_to_csv(sheet, opts) {
+	var out = "", txt = "", qreg = /"/g;
+	var o = opts == null ? {} : opts;
+	if(sheet == null || sheet["!ref"] == null) return "";
+	var r = safe_decode_range(sheet["!ref"]);
+	var FS = o.FS !== undefined ? o.FS : ",", fs = FS.charCodeAt(0);
+	var RS = o.RS !== undefined ? o.RS : "\n", rs = RS.charCodeAt(0);
+	var row = "", rr = "", cols = [];
+	var i = 0, cc = 0, val;
+	var R = 0, C = 0;
+	for(C = r.s.c; C <= r.e.c; ++C) cols[C] = encode_col(C);
+	for(R = r.s.r; R <= r.e.r; ++R) {
+		row = "";
+		rr = encode_row(R);
+		for(C = r.s.c; C <= r.e.c; ++C) {
+			val = sheet[cols[C] + rr];
+			txt = val !== undefined ? ''+format_cell(val) : "";
+			for(i = 0, cc = 0; i !== txt.length; ++i) if((cc = txt.charCodeAt(i)) === fs || cc === rs || cc === 34) {
+				txt = "\"" + txt.replace(qreg, '""') + "\""; break; }
+			row += (C === r.s.c ? "" : FS) + txt;
+		}
+		out += row + RS;
+	}
+	return out;
+}
+var make_csv = sheet_to_csv;
+
+function sheet_to_formulae(sheet) {
+	var cmds, y = "", x, val="";
+	if(sheet == null || sheet["!ref"] == null) return "";
+	var r = safe_decode_range(sheet['!ref']), rr = "", cols = [], C;
+	cmds = new Array((r.e.r-r.s.r+1)*(r.e.c-r.s.c+1));
+	var i = 0;
+	for(C = r.s.c; C <= r.e.c; ++C) cols[C] = encode_col(C);
+	for(var R = r.s.r; R <= r.e.r; ++R) {
+		rr = encode_row(R);
+		for(C = r.s.c; C <= r.e.c; ++C) {
+			y = cols[C] + rr;
+			x = sheet[y];
+			val = "";
+			if(x === undefined) continue;
+			if(x.f != null) val = x.f;
+			else if(x.w !== undefined) val = "'" + x.w;
+			else if(x.v === undefined) continue;
+			else val = ""+x.v;
+			cmds[i++] = y + "=" + val;
+		}
+	}
+	cmds.length = i;
+	return cmds;
+}
+
+var utils = {
+	encode_col: encode_col,
+	encode_row: encode_row,
+	encode_cell: encode_cell,
+	encode_range: encode_range,
+	decode_col: decode_col,
+	decode_row: decode_row,
+	split_cell: split_cell,
+	decode_cell: decode_cell,
+	decode_range: decode_range,
+	format_cell: format_cell,
+	get_formulae: sheet_to_formulae,
+	make_csv: sheet_to_csv,
+	make_json: sheet_to_json,
+	make_formulae: sheet_to_formulae,
+	sheet_to_csv: sheet_to_csv,
+	sheet_to_json: sheet_to_json,
+	sheet_to_formulae: sheet_to_formulae,
+	sheet_to_row_object_array: sheet_to_row_object_array
+};
+XLSX.parseZip = parse_zip;
+XLSX.read = readSync;
+XLSX.readFile = readFileSync;
+XLSX.write = writeSync;
+XLSX.writeFile = writeFileSync;
+XLSX.utils = utils;
+XLSX.SSF = SSF;
+})(typeof exports !== 'undefined' ? exports : XLSX);
 //! moment.js
 //! version : 2.5.1
 //! authors : Tim Wood, Iskren Chernev, Moment.js contributors
@@ -29666,7 +31262,7 @@
             if(this.tooltipOptions.defaultTheme) {
                 $tip.css({
                     'background': '#fff',
-                    'z-index': '21040',//***lchang
+                    'z-index': '1040',
                     'padding': '0.4em 0.6em',
                     'border-radius': '0.5em',
                     'font-size': '0.8em',
@@ -30216,41 +31812,34 @@
 	allowColumnRenaming : true,
 	//proxy : 'php/proxy.php?address=', //set this if a HTTP proxy shall be used (e.g. to bypass X-Domain problems)
 	//colors for several datasets; rgb1 will be used for selected objects, rgb0 for unselected
-	colors : [{//blue /***lchang changed the color set***/
-                r1 : 0,
-                g1 : 128,
-                b1 : 255,
-                r0 : 205,
-                g0 : 230,
-                b0 : 255
-        },{//red
+	colors : [{
 		r1 : 255,
-		g1 : 84,
-		b1 : 22,
-		r0 : 255,
-		g0 : 218,
+		g1 : 101,
+		b1 : 0,
+		r0 : 253,
+		g0 : 229,
 		b0 : 205
-	}, {//purple
-		r1 : 132,
-		g1 : 0,
+	}, {
+		r1 : 144,
+		g1 : 26,
 		b1 : 255,
-		r0 : 231,
-		g0 : 205,
-		b0 : 255
-	}, {//green
-		r1 : 0,
-		g1 : 225,
-		b1 : 116,
-		r0 : 180,
+		r0 : 230,
 		g0 : 225,
-		b0 : 204
-	}, {//yellow
-		r1 : 255,
-		g1 : 191,
+		b0 : 255
+	}, {
+		r1 : 0,
+		g1 : 217,
 		b1 : 0,
-		r0 : 255,
-		g0 : 243,
-		b0 : 205
+		r0 : 213,
+		g0 : 255,
+		b0 : 213
+	}, {
+		r1 : 240,
+		g1 : 220,
+		b1 : 0,
+		r0 : 247,
+		g0 : 244,
+		b0 : 197
 	}]
 
 }
@@ -30314,30 +31903,7 @@
 GeoTemConfig.applySettings = function(settings) {
 	$.extend(this, settings);
 };
-function HSVtoRGB(h, s, v) {
-    var r, g, b, i, f, p, q, t;
-    if (h && s === undefined && v === undefined) {
-        s = h.s, v = h.v, h = h.h;
-    }
-    i = Math.floor(h * 6);
-    f = h * 6 - i;
-    p = v * (1 - s);
-    q = v * (1 - f * s);
-    t = v * (1 - (1 - f) * s);
-    switch (i % 6) {
-        case 0: r = v, g = t, b = p; break;
-        case 1: r = q, g = v, b = p; break;
-        case 2: r = p, g = v, b = t; break;
-        case 3: r = p, g = q, b = v; break;
-        case 4: r = t, g = p, b = v; break;
-        case 5: r = v, g = p, b = q; break;
-    }
-    return {
-        r: Math.floor(r * 255),
-        g: Math.floor(g * 255),
-        b: Math.floor(b * 255)
-    };
-}
+
 //Keeps track of how many colors where assigned yet.
 GeoTemConfig.assignedColorCount = 0;
 GeoTemConfig.getColor = function(id){
@@ -30346,18 +31912,6 @@
 		
 		while (true){
 			if( GeoTemConfig.colors.length <= GeoTemConfig.assignedColorCount ){
-				var h=Math.random();
-				var rgb=HSVtoRGB(h,1.0,1.0);
-				var rgb0=HSVtoRGB(h,0.2,1.0);
-				color = {
-					r1 : rgb.r,
-					g1 : rgb.g,
-					b1 : rgb.b,
-					r0 : rgb0.r,
-					g0 : rgb0.g,
-					b0 : rgb0.b
-				};
-				/*
 				color = {
 					r1 : Math.floor((Math.random()*255)+1),
 					g1 : Math.floor((Math.random()*255)+1),
@@ -30365,7 +31919,7 @@
 					r0 : 230,
 					g0 : 230,
 					b0 : 230
-				};*/
+				};
 			} else
 				color = GeoTemConfig.colors[GeoTemConfig.assignedColorCount];
 			
@@ -30386,11 +31940,9 @@
 				}
 			}
 			if (found === true){
-				//***lchang: GeoTemConfig.colors.length is never <= GeoTemConfig.assignedColorCount, so the GeoTemConfig.assignedColorCount never changes, and infinite loops occurs
-				GeoTemConfig.assignedColorCount++;
 				if( GeoTemConfig.colors.length <= GeoTemConfig.assignedColorCount ){
 					//next time skip over this color
-					//GeoTemConfig.assignedColorCount++;//***lchang
+					GeoTemConfig.assignedColorCount++;
 				}
 				continue;
 			} else {
@@ -30480,17 +32032,28 @@
  * @param {String} url the url of the file to load
  * @return json object of given file
  */
-GeoTemConfig.getJson = function(url) {
+GeoTemConfig.getJson = function(url,asyncFunc) {
+	var async = false;
+	if( asyncFunc ){
+		async = true;
+	}
+	
 	var data;
 	$.ajax({
 		url : url,
-		async : false,
+		async : async,
 		dataType : 'json',
 		success : function(json) {
 			data = json;
-		}
-	});
-	return data;
+			if (async){
+				asyncFunc(data);
+			}
+		}
+	});
+	
+	if (async){
+		return data;
+	}
 }
 
 GeoTemConfig.mergeObjects = function(set1, set2) {
@@ -30622,11 +32185,10 @@
 		}
 		
 		dataObject["tableContent"] = tableContent;
-
+		
 		json.push(dataObject);
 	}
-
-
+	
 	return json;
 };
 
@@ -30721,17 +32283,34 @@
     var json;
     req.onload = function() {
     	json = GeoTemConfig.convertCsv(req.response);
-    	
     	if( asyncFunc )
     		asyncFunc(json);
     };
-
 	req.send();
 	
-	if( !async ){ 
+	if( !async ){
 		return json;
 	}
-
+};
+
+/**
+ * loads a binary file  
+ * @param {String} url of the file to load
+ * @return binary data
+ */
+GeoTemConfig.getBinary = function(url,asyncFunc) {
+	var async = true;
+
+	var req = new XMLHttpRequest();
+    req.open("GET",url,async);
+    req.responseType = "arraybuffer";
+    
+    var binaryData;
+	req.onload = function() {
+		var arrayBuffer = req.response;
+		asyncFunc(arrayBuffer);
+    };
+	req.send();
 };
 
 /**
@@ -31645,7 +33224,28 @@
  * @param {Object} parent parent to call filter functions
  * @param {HTML object} parentDiv div to append filter buttons
  */
+FilterBarFactory = {
+	filterBarArray :[],
+	push : function(newFilterBar){
+		FilterBarFactory.filterBarArray.push(newFilterBar);
+	},
+	resetAll : function(show) {
+		$(FilterBarFactory.filterBarArray).each(function(){
+			if (show) {
+				this.filter.setAttribute('class', 'smallButton filter');
+				this.filterInverse.setAttribute('class', 'smallButton filterInverse');
+				this.cancelSelection.setAttribute('class', 'smallButton filterCancel');
+			} else {
+				this.filter.setAttribute('class', 'smallButton filterDisabled');
+				this.filterInverse.setAttribute('class', 'smallButton filterInverseDisabled');
+				this.cancelSelection.setAttribute('class', 'smallButton filterCancelDisabled');
+			}
+		});
+	}
+};
+
 function FilterBar(parent, parentDiv) {
+	FilterBarFactory.push(this);
 
 	var bar = this;
 
@@ -31680,15 +33280,7 @@
 	}
 
 	this.reset = function(show) {
-		if (show) {
-			this.filter.setAttribute('class', 'smallButton filter');
-			this.filterInverse.setAttribute('class', 'smallButton filterInverse');
-			this.cancelSelection.setAttribute('class', 'smallButton filterCancel');
-		} else {
-			this.filter.setAttribute('class', 'smallButton filterDisabled');
-			this.filterInverse.setAttribute('class', 'smallButton filterInverseDisabled');
-			this.cancelSelection.setAttribute('class', 'smallButton filterCancelDisabled');
-		}
+		FilterBarFactory.resetAll(show);
 	};
 
 };
@@ -32434,724 +34026,762 @@
 	};
 
 };
-/*
-* MapConfig.js
-*
-* Copyright (c) 2012, Stefan Jänicke. All rights reserved.
-*
-* This library is free software; you can redistribute it and/or
-* modify it under the terms of the GNU Lesser General Public
-* License as published by the Free Software Foundation; either
-* version 3 of the License, or (at your option) any later version.
-*
-* This library is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-* Lesser General Public License for more details.
-*
-* You should have received a copy of the GNU Lesser General Public
-* License along with this library; if not, write to the Free Software
-* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
-* MA 02110-1301  USA
-*/
-
-/**
- * @class MapConfig
- * Map Configuration File
- * @author Stefan Jänicke (stjaenicke@informatik.uni-leipzig.de)
- * @release 1.0
- * @release date: 2012-07-27
- * @version date: 2012-07-27
- */
-function MapConfig(options) {
-
-	this.options = {
-		mapWidth : false, // false or desired width css definition for the map
-		mapHeight : false, // false or desired height css definition for the map
-		mapTitle : 'GeoTemCo Map View', // title will be shown in map header
-		mapIndex : 0, // index = position in location array; for multiple locations the 2nd map refers to index 1
-		alternativeMap :  [
-		/*		{
-					name: 'Barrington Roman Empire',
-					url: 'http://pelagios.dme.ait.ac.at/tilesets/imperium/${z}/${x}/${y}.png',
-					type:'XYZ',
-					attribution: "(c) Barrington Roman Empiry, <a href='http://pelagios.dme.ait.ac.at/maps/greco-roman/'>Pelagios</a>"
-				},
-				{
-					name: 'Maps-for-Free Relief Map',
-					url: 'http://maps-for-free.com/layer/relief/z${z}/row${y}/${z}_${x}-${y}.jpg',
-					type:'XYZ',
-					attribution: "(c) <a href='http://www.maps-for-free.com/html/about.html'>Maps for Free</a>"
-				},
-				{
-					name: 'Contemporary Map (2010)',
-					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
-					layer: 'historic:cntry2010',
-					attribution: "(c) <a href='http://epp.eurostat.ec.europa.eu/portal/page/portal/gisco_Geographical_information_maps/popups/references/administrative_units_statistical_units_1'>EuroStat</a>"
-				},
-				{
-					name: 'Historical Map of 2006',
-					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
-					layer: 'historic:cntry2006',
-					attribution: "(c) <a href='http://epp.eurostat.ec.europa.eu/portal/page/portal/gisco_Geographical_information_maps/popups/references/administrative_units_statistical_units_1'>EuroStat</a>"
-				},
-				{
-					name: 'Historical Map of 1994',
-					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
-					layer: 'historic:cntry1994',
-					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
-				},
-				{
-					name: 'Historical Map of 1945',
-					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
-					layer: 'historic:cntry1945',
-					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
-				},
-				{
-					name: 'Historical Map of 1938',
-					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
-					layer: 'historic:cntry1938',
-					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
-				},
-				{
-					name: 'Historical Map of 1920',
-					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
-					layer: 'historic:cntry1920',
-					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
-				},
-				{
-					name: 'Historical Map of 1914',
-					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
-					layer: 'historic:cntry1914',
-					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
-				},
-				{
-					name: 'Historical Map of 1880',
-					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
-					layer: 'historic:cntry1880',
-					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
-				},
-				{
-					name: 'Historical Map of 1815',
-					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
-					layer: 'historic:cntry1815',
-					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
-				},
-				{
-					name: 'Historical Map of 1783',
-					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
-					layer: 'historic:cntry1783',
-					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
-				},
-				{
-					name: 'Historical Map of 1715',
-					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
-					layer: 'historic:cntry1715',
-					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
-				},
-				{
-					name: 'Historical Map of 1650',
-					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
-					layer: 'historic:cntry1650',
-					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
-				},
-				{
-					name: 'Historical Map of 1530',
-					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
-					layer: 'historic:cntry1530',
-					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
-				},
-				{
-					name: 'Historical Map of 1492',
-					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
-					layer: 'historic:cntry1492',
-					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
-				},
-				{
-					name: 'Historical Map of 1279',
-					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
-					layer: 'historic:cntry1279',
-					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
-				},
-				{
-					name: 'Historical Map of 1000',
-					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
-					layer: 'historic:cntry1000',
-					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
-				},
-				{
-					name: 'Historical Map of 800',
-					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
-					layer: 'historic:cntry800',
-					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
-				},
-				{
-					name: 'Historical Map of 600',
-					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
-					layer: 'historic:cntry600',
-					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
-				},
-				{
-					name: 'Historical Map of 400',
-					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
-					layer: 'historic:cntry400',
-					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
-				},
-				{
-					name: 'Historical Map of 1 BC',
-					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
-					layer: 'historic:cntry1bc',
-					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
-				},
-				{
-					name: 'Historical Map of 200 BC',
-					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
-					layer: 'historic:cntry200bc',
-					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
-				},
-				{
-					name: 'Historical Map of 323 BC',
-					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
-					layer: 'historic:cntry323bc',
-					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
-				},
-				{
-					name: 'Historical Map of 500 BC',
-					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
-					layer: 'historic:cntry500bc',
-					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
-				},
-*/
-				{
-					name: 'Historical Map of 1000 BC',
-					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
-					layer: 'historic:cntry1000bc',
-					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
-				},
-				{
-					name: 'Historical Map of 2000 BC',
-					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
-					layer: 'historic:cntry2000bc',
-					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
-				},
-		],
-		legend : true, // if a legend at the bottom of the map should be shown or not
-		mapMerge : false, // if the elements of distinct datasets should be merged into one set or not
-		useGraphics : false,  // if different graphics should represent different datasets or not
-		graphics : [
-			{
-				shape: "circle",
-				rotation: 0
-			},
-			{
-				shape: "square",
-				rotation: 0
-			},
-			{
-				shape: "triangle",
-				rotation: 0
-			},
-			{
-				shape: "square",
-				rotation: 45
-			}
-		],
-		googleMaps : false, // enable/disable Google maps (actually, no Google Maps API key is required)
-		bingMaps : false, // enable/disable Bing maps (you need to set the Bing Maps API key below)
-		bingApiKey : 'none', // bing maps api key, see informations at http://bingmapsportal.com/
-		osmMaps : true, // enable/disable OSM maps
-		osmMapsMapQuest : true, // enable/disable OSM maps with MapQuest tiles
-		baseLayer : 'Open Street Map', // initial layer to show (e.g. 'Google Streets')
-		resetMap : true, // show/hide map reset button
-		countrySelect : true, // show/hide map country selection control button
-		polygonSelect : true, // show/hide map polygon selection control button
-		circleSelect : true, // show/hide map circle selection control button
-		squareSelect : true, // show/hide map square selection control button
-		multiSelection : true, // true, if multiple polygons or multiple circles should be selectable
-		popups : true, // enabled popups will show popup windows for circles on the map
-		olNavigation : false, // show/hide OpenLayers navigation panel
-		olLayerSwitcher : false, // show/hide OpenLayers layer switcher
-		olMapOverview : false, // show/hide OpenLayers map overview
-		olKeyboardDefaults : true, // (de)activate Openlayers keyboard defaults
-		olScaleLine : false, // (de)activate Openlayers keyboard defaults
-		geoLocation : true, // show/hide GeoLocation feature
-		boundaries : {
-			minLon : -29,
-			minLat : 35,
-			maxLon : 44,
-			maxLat : 67
-		}, // initial map boundaries or 'false' for no boundaries
-		mapBackground : '#bbd0ed',
-		labelGrid : true, // show label grid on hover
-		maxPlaceLabels : 6, // Integer value for fixed number of place labels: 0 --> unlimited, 1 --> 1 label (won't be shown in popup, 2 --> is not possible because of others & all labels --> 3 labels, [3,...,N] --> [3,...,N] place labels)
-		selectDefault : true, // true, if strongest label should be selected as default
-		maxLabelIncrease : 2, // maximum increase (in em) for the font size of a label
-		labelHover : false, // true, to update on label hover
-		ieHighlightLabel : "color: COLOR1; background-color: COLOR0; filter:'progid:DXImageTransform.Microsoft.Alpha(Opacity=80)';-ms-filter:'progid:DXImageTransform.Microsoft.Alpha(Opacity=80)';", // css code for a highlighted place label in IE
-		highlightLabel : "color: COLOR0; text-shadow: 0 0 0.4em black, 0 0 0.4em black, 0 0 0.4em black, 0 0 0.4em COLOR0;", // css code for a highlighted place label
-		ieSelectedLabel : "color: COLOR1; font-weight: bold;", // css code for a selected place label in IE
-		selectedLabel : "color: COLOR1; font-weight: bold;", // css code for a selected place label
-		ieUnselectedLabel : "color: COLOR1; font-weight: normal;", // css code for an unselected place label in IE
-		unselectedLabel : "color: COLOR1; font-weight: normal;", // css code for an unselected place label
-		ieHoveredLabel : "color: COLOR1; font-weight: bold;", // css code for a hovered place label in IE
-		hoveredLabel : "color: COLOR1; font-weight: bold;", // css code for a hovered place label
-		circleGap : 0, // gap between the circles on the map (>=0)
-		circleOverlap : {
-			type: 'area', // 'area' or 'diameter' is possible
-			overlap: 1 // the percentage of allowed overlap (0<=overlap<=1)
-		}, // maximum allowed overlap in percent (if circleGap = 0, circleOverlap will be used)
-		minimumRadius : 1, // minimum radius of a circle with mimimal weight (>0)
-		circleOutline : 2, // false for no outline or a pixel value v with 0 < v
-		circleOpacity : 'balloon', // 'balloon' for dynamic opacity of the circles or a value t with 0 <= t <= 1
-		minTransparency : 0.55, // maximum transparency of a circle
-		maxTransparency : 0.8, // minimum transparency of a circle
-		binning : 'generic', // binning algorithm for the map, possible values are: 'generic', 'square', 'hexagonal', 'triangular' or false for 'no binning'
-		noBinningRadii : 'dynamic', // for 'no binning': 'static' for only minimum radii, 'dynamic' for increasing radii for increasing weights
-		circlePackings : true, // if circles of multiple result sets should be displayed in circle packs, if a binning is performed
-		binCount : 10, // number of bins for x and y dimension for lowest zoom level
-		showDescriptions : true, // true to show descriptions of data items (must be provided by kml/json), false if not
-		mapSelection : true, // show/hide select map dropdown
-		binningSelection : false, // show/hide binning algorithms dropdown
-		mapSelectionTools : true, // show/hide map selector tools
-		dataInformation : true, // show/hide data information
-		overlayVisibility : false, // initial visibility of additional overlays
-		//proxyHost : 'php/proxy.php?address=',	//required for selectCountry feature, if the requested GeoServer and GeoTemCo are NOT on the same server
-		placenameTagsStyle : 'value' // the style of the placenames "surrounding" a circle on hover. 'zoom' for tags based on zoom level (old behaviour), 'value' for new value-based
-
-	};
-	if ( typeof options != 'undefined') {
-		$.extend(this.options, options);
-	}
-	
-	//if the user can change shape/color graphics have to be used
-	//but this will use circles as default shape
-	if (GeoTemConfig.allowUserShapeAndColorChange){
-		this.options.useGraphics = true;
-	}
-
-};
-
-MapConfig.prototype.getGraphic = function(id){
-	var dataset = GeoTemConfig.datasets[id];
-
-	var graphic;
-	if (typeof dataset.graphic !== "undefined"){
-		graphic = dataset.graphic;
-	} else{
-		graphic = this.options.graphics[id % this.options.graphics.length];
-	}
-	
-	var color;
-	if (typeof dataset.color !== "undefined"){
-		color = dataset.color;
-	} else{
-		color = GeoTemConfig.getColor(id);
-	}
-	return {
-		shape: graphic.shape,
-		rotation: graphic.rotation,
-		color: color
-	};
-};
-/*
-* MapGui.js
-*
-* Copyright (c) 2012, Stefan Jänicke. All rights reserved.
-*
-* This library is free software; you can redistribute it and/or
-* modify it under the terms of the GNU Lesser General Public
-* License as published by the Free Software Foundation; either
-* version 3 of the License, or (at your option) any later version.
-*
-* This library is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-* Lesser General Public License for more details.
-*
-* You should have received a copy of the GNU Lesser General Public
-* License along with this library; if not, write to the Free Software
-* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
-* MA 02110-1301  USA
-*/
-
-/**
- * @class MapGui
- * Map GUI Implementation
- * @author Stefan Jänicke (stjaenicke@informatik.uni-leipzig.de)
- * @release 1.0
- * @release date: 2012-07-27
- * @version date: 2012-07-27
- *
- * @param {MapWidget} parent map widget object
- * @param {HTML object} div parent div to append the map gui
- * @param {JSON} options map configuration
- */
-function MapGui(map, div, options, iid) {
-
-	this.map = map;
-
-	this.container = div;
-	if (options.mapWidth) {
-		this.container.style.width = options.mapWidth;
-	}
-	if (options.mapHeight) {
-		this.container.style.height = options.mapHeight;
-	}
-	this.container.style.position = 'relative';
-
-	this.mapWindow = document.createElement("div");
-	this.mapWindow.setAttribute('class', 'mapWindow');
-	this.mapWindow.id = "mapWindow"+iid;
-	this.mapWindow.style.background = options.mapBackground;
-	this.container.appendChild(this.mapWindow);
-
-	this.mapContainer = document.createElement("div");
-	this.mapContainer.setAttribute('class', 'mapContainer');
-	this.mapContainer.id = "mapContainer"+iid;
-	this.mapContainer.style.position = "absolute";
-	this.mapContainer.style.zIndex = 0;
-	this.mapWindow.appendChild(this.mapContainer);
-
-	var toolbarTable = document.createElement("table");
-	toolbarTable.setAttribute('class', 'absoluteToolbar ddbToolbar');
-	this.container.appendChild(toolbarTable);
-	this.mapToolbar = toolbarTable;
-
-	var titles = document.createElement("tr");
-	toolbarTable.appendChild(titles);
-	var tools = document.createElement("tr");
-	toolbarTable.appendChild(tools);
-
-	if (options.mapSelection) {
-		this.mapTypeTitle = document.createElement("td");
-		titles.appendChild(this.mapTypeTitle);
-		this.mapTypeTitle.innerHTML = GeoTemConfig.getString('mapType');
-		this.mapTypeSelector = document.createElement("td");
-		tools.appendChild(this.mapTypeSelector);
-	}
-
-	if (options.mapSelectionTools) {
-		this.mapSelectorTitle = document.createElement("td");
-		titles.appendChild(this.mapSelectorTitle);
-		this.mapSelectorTitle.innerHTML = GeoTemConfig.getString('mapSelectorTools');
-		var mapSelectorTools = document.createElement("td");
-		var selectorTools = this.map.initSelectorTools();
-		for (var i in selectorTools ) {
-			mapSelectorTools.appendChild(selectorTools[i].button);
-		}
-		tools.appendChild(mapSelectorTools);
-	}
-
-	if (options.binningSelection) {
-		this.binningTitle = document.createElement("td");
-		titles.appendChild(this.binningTitle);
-		this.binningTitle.innerHTML = GeoTemConfig.getString('binningType');
-		this.binningSelector = document.createElement("td");
-		tools.appendChild(this.binningSelector);
-	}
-
-	if (GeoTemConfig.allowFilter) {
-		this.filterTitle = document.createElement("td");
-		titles.appendChild(this.filterTitle);
-		this.filterTitle.innerHTML = GeoTemConfig.getString('filter');
-		this.filterOptions = document.createElement("td");
-		tools.appendChild(this.filterOptions);
-	}
-
-	if (options.dataInformation) {
-		this.infoTitle = document.createElement("td");
-		this.infoTitle.innerHTML = options.mapTitle;
-		titles.appendChild(this.infoTitle);
-		var mapSum = document.createElement("td");
-		this.mapElements = document.createElement("div");
-		this.mapElements.setAttribute('class', 'ddbElementsCount');
-		mapSum.appendChild(this.mapElements);
-		tools.appendChild(mapSum);
-	}
-	
-	this.lockTitle = document.createElement("td");
-	titles.appendChild(this.lockTitle);
-	this.lockIcon = document.createElement("td");
-	var lockButton = document.createElement("div");
-	$(lockButton).addClass('mapControl');
-	var activateLock = function() {
-		map.navigation.deactivate();
-	}
-	var deactivateLock = function() {
-		map.navigation.activate();
-	}
-	var lockMapControl = new MapControl(this.map, lockButton, 'lock', activateLock, deactivateLock);
-	tools.appendChild(lockMapControl.button);
-
-
-	var gui = this;
-	if (navigator.geolocation && options.geoLocation) {
-		this.geoActive = false;
-		this.geoLocation = document.createElement("div");
-		this.geoLocation.setAttribute('class', 'geoLocationOff');
-		this.geoLocation.title = GeoTemConfig.getString('activateGeoLocation');
-		this.container.appendChild(this.geoLocation);
-		this.geoLocation.style.left = "20px";
-		this.geoLocation.onclick = function() {
-			var changeStyle = function() {
-				if (gui.geoActive) {
-					gui.geoLocation.setAttribute('class', 'geoLocationOn');
-					gui.geoLocation.title = GeoTemConfig.getString(GeoTemConfig.language, 'deactivateGeoLocation');
-				} else {
-					gui.geoLocation.setAttribute('class', 'geoLocationOff');
-					gui.geoLocation.title = GeoTemConfig.getString(GeoTemConfig.language, 'activateGeoLocation');
-				}
-			}
-			if (!gui.geoActive) {
-				if ( typeof gui.longitude == 'undefined') {
-					navigator.geolocation.getCurrentPosition(function(position) {
-						gui.longitude = position.coords.longitude;
-						gui.latitude = position.coords.latitude;
-						gui.map.setMarker(gui.longitude, gui.latitude);
-						gui.geoActive = true;
-						changeStyle();
-					}, function(msg) {
-						console.log( typeof msg == 'string' ? msg : "error");
-					});
-				} else {
-					gui.map.setMarker(gui.longitude, gui.latitude);
-					gui.geoActive = true;
-					changeStyle();
-				}
-			} else {
-				gui.map.removeMarker();
-				gui.geoActive = false;
-				changeStyle();
-			}
-		}
-	}
-
-	if (!options.olNavigation) {
-		this.map.zoomSlider = new MapZoomSlider(this.map, "vertical");
-		this.container.appendChild(this.map.zoomSlider.div);
-		this.map.zoomSlider.div.style.left = "20px";
-	}
-
-	if (options.resetMap) {
-		this.homeButton = document.createElement("div");
-		this.homeButton.setAttribute('class', 'mapHome');
-		this.homeButton.title = GeoTemConfig.getString('home');
-		this.container.appendChild(this.homeButton);
-		this.homeButton.style.left = "20px";
-		this.homeButton.onclick = function() {
-			if (map.mds.getAllObjects() == null){
-				map.openlayersMap.setCenter(new OpenLayers.LonLat(0, 0));
-				map.openlayersMap.zoomTo(0);
-			}
-			gui.map.drawObjectLayer(true);
-		}
-	}
-
-	if (options.legend) {
-		this.legendDiv = document.createElement("div");
-		this.legendDiv.setAttribute('class', 'mapLegend');
-		this.mapWindow.appendChild(this.legendDiv);
-	}
-
-	var linkForOsm = 'http://www.openstreetmap.org/';
-	var linkForLicense = 'http://creativecommons.org/licenses/by-sa/2.0/';
-	this.osmLink = document.createElement("div");
-	this.osmLink.setAttribute('class', 'osmLink');
-	this.osmLink.innerHTML = '(c) <a href=' + linkForOsm + '>OpenStreetMap contributors</a>, <a href=' + linkForLicense + '>CC-BY-SA</a>';
-	this.mapWindow.appendChild(this.osmLink);
-	this.osmMapQuestLink = document.createElement("div");
-	this.osmMapQuestLink.setAttribute('class', 'osmLink');
-	this.osmMapQuestLink.innerHTML = '(c) Data, imagery and map information provided by MapQuest <a href="http://www.mapquest.com/" target="_blank">MapQuest</a> <img src="http://developer.mapquest.com/content/osm/mq_logo.png"> <a href=' + linkForOsm + '>OpenStreetMap contributors</a>, <a href=' + linkForLicense + '>CC-BY-SA</a>';
-	this.mapWindow.appendChild(this.osmMapQuestLink);
-
-	//		var tooltip = document.createElement("div");
-	//		tooltip.setAttribute('class','ddbTooltip');
-	//		toolbarTable.appendChild(tooltip);
-
-	//		var tooltip = document.createElement("div");
-	//		tooltip.setAttribute('class','ddbTooltip');
-	//		toolbarTable.appendChild(tooltip);
-	//
-	//		tooltip.onmouseover = function(){
-	//			/*
-	//		    Publisher.Publish('TooltipContent', {
-	//						content: GeoTemConfig.getString(GeoTemConfig.language,'timeHelp'),
-	//						target: $(tooltip)
-	//					    });
-	//			*/
-	//		}
-	//		tooltip.onmouseout = function(){
-	//		 //   Publisher.Publish('TooltipContent');
-	//		}
-	//		//vhz tooltip on click should open a help file if defined in GeoTemConfig
-	//		if(GeoTemConfig.helpURL) {
-	//			tooltip.onclick = function () {
-	//
-	//			}
-	//		}
-
-	//		}
-	//		tooltip.onmouseout = function(){
-	//   			Publisher.Publish('TooltipContent');
-	//		}
-
-	this.resize = function() {
-		var w = this.container.offsetWidth;
-		var h = this.container.offsetHeight;
-//		this.mapWindow.style.width = w + "px";
-		this.mapWindow.style.height = h + "px";
-//		this.mapContainer.style.width = w + "px";
-		this.mapContainer.style.height = h + "px";
-		var top = toolbarTable.offsetHeight + 20;
-		if (options.olLayerSwitcher) {
-			var switcherDiv = $('.olControlLayerSwitcher')[0];
-			$(switcherDiv).css('top', top + "px");
-		}
-		if ( typeof this.geoLocation != "undefined") {
-			this.geoLocation.style.top = top + "px";
-			top += this.geoLocation.offsetHeight + 4;
-		}
-		if (options.olNavigation) {
-			var panZoomBar = $('.olControlPanZoom')[0];
-			$(panZoomBar).css('top', top + 'px');
-			$(panZoomBar).css('left', '12px');
-			var zoomOut = document.getElementById('OpenLayers.Control.PanZoom_23_zoomout');
-			top += $(zoomOut).height() + $(zoomOut).position().top + 4;
-		} else {
-			this.map.zoomSlider.div.style.top = top + "px";
-			top += this.map.zoomSlider.div.offsetHeight + 2;
-		}
-		if (options.resetMap) {
-			this.homeButton.style.top = top + "px";
-		}
-		this.headerHeight = toolbarTable.offsetHeight;
-		this.headerWidth = toolbarTable.offsetWidth;
-		this.map.openlayersMap.updateSize();
-		this.map.drawObjectLayer(true);
-	};
-
-	this.updateLegend = function(datasets){
-		$(this.legendDiv).empty();
-		var table = $('<table style="margin:10px"/>').appendTo(this.legendDiv);
-		for( var i=0; i<datasets.length; i++ ){
-			var row = $('<tr/>').appendTo(table);
-			if( options.useGraphics ){
-				var graphic = map.config.getGraphic(i);
-				var fill = 'rgb(' + graphic.color.r0 + ',' + graphic.color.g0 + ',' + graphic.color.b0 + ')';
-				var stroke = 'rgb(' + graphic.color.r1 + ',' + graphic.color.g1 + ',' + graphic.color.b1 + ')';
-				var rot = graphic.rotation;
-				var svg;
-				if( graphic.shape == 'circle' ){
-					svg = '<svg style="width:20px;height:20px;"><circle cx="10" cy="10" r="7" stroke="'+stroke+'" stroke-width="2" fill="'+fill+'"/></svg>';
-				}
-				else if( graphic.shape == 'square' ){
-					svg = '<svg style="width:20px;height:20px;"><polygon points="4,4 16,4 16,16 4,16" style="fill:'+fill+';stroke:'+stroke+';stroke-width:2" transform="rotate('+rot+' 10,10)"/></svg>';
-				}
-				else if( graphic.shape == 'triangle' ){
-					svg = '<svg style="width:20px;height:20px;"><polygon points="3,17 17,17 10,5" style="fill:'+fill+';stroke:'+stroke+';stroke-width:2" transform="rotate('+rot+' 10,10)"/></svg>';
-				}
-				$('<td>'+svg+'</td>').appendTo(row);
-			}
-			else {
-				var c = GeoTemConfig.getColor(i);
-				var fill = 'rgb(' + c.r0 + ',' + c.g0 + ',' + c.b0 + ')';
-				var stroke = 'rgb(' + c.r1 + ',' + c.g1 + ',' + c.b1 + ')';
-				var svg = '<svg style="width:20px;height:20px;"><circle cx="10" cy="10" r="7" stroke="'+stroke+'" stroke-width="2" fill="'+fill+'"/></svg>';
-				$('<td>'+svg+'</td>').appendTo(row);
-			}			
-			$('<td>'+datasets[i].label+'</td>').appendTo(row);
-		}
-	};
-
-	this.updateSpaceQuantity = function(count) {
-		if (!options.dataInformation) {
-			return;
-		}
-		this.mapCount = count;
-		if (count != 1) {
-			this.mapElements.innerHTML = this.beautifyCount(count) + " " + GeoTemConfig.getString('results');
-		} else {
-			this.mapElements.innerHTML = this.beautifyCount(count) + " " + GeoTemConfig.getString('result');
-		}
-	}
-
-	this.setMapsDropdown = function() {
-		if (!options.mapSelection) {
-			return;
-		}
-		$(this.mapTypeSelector).empty();
-		var maps = [];
-		var gui = this;
-		var addMap = function(name, index) {
-			var setMap = function() {
-				gui.map.setMap(index);
-			}
-			maps.push({
-				name : name,
-				onclick : setMap
-			});
-		}
-		for (var i = 0; i < this.map.baseLayers.length; i++) {
-			addMap(this.map.baseLayers[i].name, i);
-		}
-		this.mapTypeDropdown = new Dropdown(this.mapTypeSelector, maps, GeoTemConfig.getString('selectMapType'));
-	}
-
-	this.setMap = function() {
-		if (options.mapSelection) {
-			this.mapTypeDropdown.setEntry(this.map.baselayerIndex);
-		}
-	}
-
-	this.setBinningDropdown = function() {
-		if (!options.binningSelection) {
-			return;
-		}
-		$(this.binningSelector).empty();
-		var binnings = [];
-		var gui = this;
-		var index = 0;
-		var entry;
-		var addBinning = function(name, id) {
-			if (options.binning == id) {
-				entry = index;
-			} else {
-				index++;
-			}
-			var setBinning = function() {
-				options.binning = id;
-				gui.map.initWidget(gui.map.datasets, false);
-				gui.map.riseLayer();
-			}
-			binnings.push({
-				name : name,
-				onclick : setBinning
-			});
-		}
-		addBinning(GeoTemConfig.getString('genericBinning'), 'generic');
-		addBinning(GeoTemConfig.getString('squareBinning'), 'square');
-		addBinning(GeoTemConfig.getString('hexagonalBinning'), 'hexagonal');
-		addBinning(GeoTemConfig.getString('triangularBinning'), 'triangular');
-		addBinning(GeoTemConfig.getString('noBinning'), false);
-		var binningDropdown = new Dropdown(this.binningSelector, binnings, GeoTemConfig.getString('binningTooltip'));
-		binningDropdown.setEntry(entry);
-	}
-	this.setBinningDropdown();
-
-	this.beautifyCount = function(count) {
-		var c = count + '';
-		var p = 0;
-		var l = c.length;
-		while (l - p > 3) {
-			p += 3;
-			c = c.substring(0, l - p) + "." + c.substring(l - p);
-			p++;
-			l++;
-		}
-		return c;
-	}
-
-};
+/*
+* MapConfig.js
+*
+* Copyright (c) 2012, Stefan Jänicke. All rights reserved.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 3 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+* MA 02110-1301  USA
+*/
+
+/**
+ * @class MapConfig
+ * Map Configuration File
+ * @author Stefan Jänicke (stjaenicke@informatik.uni-leipzig.de)
+ * @release 1.0
+ * @release date: 2012-07-27
+ * @version date: 2012-07-27
+ */
+function MapConfig(options) {
+
+	this.options = {
+		mapWidth : false, // false or desired width css definition for the map
+		mapHeight : '580px', // false or desired height css definition for the map
+		mapTitle : 'GeoTemCo Map View', // title will be shown in map header
+		mapIndex : 0, // index = position in location array; for multiple locations the 2nd map refers to index 1
+		alternativeMap :  [
+				{
+					name: 'Barrington Roman Empire',
+					url: 'http://pelagios.dme.ait.ac.at/tilesets/imperium/${z}/${x}/${y}.png',
+					type:'XYZ',
+					attribution: "(c) Barrington Roman Empiry, <a href='http://pelagios.dme.ait.ac.at/maps/greco-roman/'>Pelagios</a>"
+				},
+				{
+					name: 'Maps-for-Free Relief Map',
+					url: 'http://maps-for-free.com/layer/relief/z${z}/row${y}/${z}_${x}-${y}.jpg',
+					type:'XYZ',
+					attribution: "(c) <a href='http://www.maps-for-free.com/html/about.html'>Maps for Free</a>"
+				},
+				{
+					name: 'Contemporary Map (2010)',
+					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
+					layer: 'historic:cntry2010',
+					attribution: "(c) <a href='http://epp.eurostat.ec.europa.eu/portal/page/portal/gisco_Geographical_information_maps/popups/references/administrative_units_statistical_units_1'>EuroStat</a>"
+				},
+				{
+					name: 'Historical Map of 2006',
+					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
+					layer: 'historic:cntry2006',
+					attribution: "(c) <a href='http://epp.eurostat.ec.europa.eu/portal/page/portal/gisco_Geographical_information_maps/popups/references/administrative_units_statistical_units_1'>EuroStat</a>"
+				},
+				{
+					name: 'Historical Map of 1994',
+					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
+					layer: 'historic:cntry1994',
+					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
+				},
+				{
+					name: 'Historical Map of 1945',
+					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
+					layer: 'historic:cntry1945',
+					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
+				},
+				{
+					name: 'Historical Map of 1938',
+					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
+					layer: 'historic:cntry1938',
+					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
+				},
+				{
+					name: 'Historical Map of 1920',
+					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
+					layer: 'historic:cntry1920',
+					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
+				},
+				{
+					name: 'Historical Map of 1914',
+					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
+					layer: 'historic:cntry1914',
+					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
+				},
+				{
+					name: 'Historical Map of 1880',
+					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
+					layer: 'historic:cntry1880',
+					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
+				},
+				{
+					name: 'Historical Map of 1815',
+					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
+					layer: 'historic:cntry1815',
+					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
+				},
+				{
+					name: 'Historical Map of 1783',
+					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
+					layer: 'historic:cntry1783',
+					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
+				},
+				{
+					name: 'Historical Map of 1715',
+					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
+					layer: 'historic:cntry1715',
+					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
+				},
+				{
+					name: 'Historical Map of 1650',
+					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
+					layer: 'historic:cntry1650',
+					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
+				},
+				{
+					name: 'Historical Map of 1530',
+					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
+					layer: 'historic:cntry1530',
+					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
+				},
+				{
+					name: 'Historical Map of 1492',
+					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
+					layer: 'historic:cntry1492',
+					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
+				},
+				{
+					name: 'Historical Map of 1279',
+					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
+					layer: 'historic:cntry1279',
+					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
+				},
+				{
+					name: 'Historical Map of 1000',
+					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
+					layer: 'historic:cntry1000',
+					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
+				},
+				{
+					name: 'Historical Map of 800',
+					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
+					layer: 'historic:cntry800',
+					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
+				},
+				{
+					name: 'Historical Map of 600',
+					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
+					layer: 'historic:cntry600',
+					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
+				},
+				{
+					name: 'Historical Map of 400',
+					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
+					layer: 'historic:cntry400',
+					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
+				},
+				{
+					name: 'Historical Map of 1 BC',
+					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
+					layer: 'historic:cntry1bc',
+					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
+				},
+				{
+					name: 'Historical Map of 200 BC',
+					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
+					layer: 'historic:cntry200bc',
+					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
+				},
+				{
+					name: 'Historical Map of 323 BC',
+					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
+					layer: 'historic:cntry323bc',
+					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
+				},
+				{
+					name: 'Historical Map of 500 BC',
+					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
+					layer: 'historic:cntry500bc',
+					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
+				},
+				{
+					name: 'Historical Map of 1000 BC',
+					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
+					layer: 'historic:cntry1000bc',
+					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
+				},
+				{
+					name: 'Historical Map of 2000 BC',
+					url: 'http://geoserver.mpiwg-berlin.mpg.de/geoserver/mpiwg/wms',
+					layer: 'historic:cntry2000bc',
+					attribution: "(c) <a href='http://webcache.googleusercontent.com/search?q=cache:NbaEeiehhzQJ:library.thinkquest.org/C006628/citations.html&client=ubuntu&hl=de&gl=de&strip=1'> ThinkQuest Team C006628</a>"
+				},
+		],
+		legend : true, // if a legend at the bottom of the map should be shown or not
+		mapMerge : false, // if the elements of distinct datasets should be merged into one set or not
+		useGraphics : false,  // if different graphics should represent different datasets or not
+		graphics : [
+			{
+				shape: "circle",
+				rotation: 0
+			},
+			{
+				shape: "square",
+				rotation: 0
+			},
+			{
+				shape: "triangle",
+				rotation: 0
+			},
+			{
+				shape: "square",
+				rotation: 45
+			}
+		],
+		googleMaps : false, // enable/disable Google maps (actually, no Google Maps API key is required)
+		bingMaps : false, // enable/disable Bing maps (you need to set the Bing Maps API key below)
+		bingApiKey : 'none', // bing maps api key, see informations at http://bingmapsportal.com/
+		osmMaps : true, // enable/disable OSM maps
+		osmMapsMapQuest : true, // enable/disable OSM maps with MapQuest tiles
+		baseLayer : 'Open Street Map', // initial layer to show (e.g. 'Google Streets')
+		resetMap : true, // show/hide map reset button
+		countrySelect : true, // show/hide map country selection control button
+		polygonSelect : true, // show/hide map polygon selection control button
+		circleSelect : true, // show/hide map circle selection control button
+		squareSelect : true, // show/hide map square selection control button
+		multiSelection : true, // true, if multiple polygons or multiple circles should be selectable
+		popups : true, // enabled popups will show popup windows for circles on the map
+		olNavigation : false, // show/hide OpenLayers navigation panel
+		olLayerSwitcher : false, // show/hide OpenLayers layer switcher
+		olMapOverview : false, // show/hide OpenLayers map overview
+		olKeyboardDefaults : true, // (de)activate Openlayers keyboard defaults
+		olScaleLine : false, // (de)activate Openlayers keyboard defaults
+		geoLocation : true, // show/hide GeoLocation feature
+		boundaries : {
+			minLon : -29,
+			minLat : 35,
+			maxLon : 44,
+			maxLat : 67
+		}, // initial map boundaries or 'false' for no boundaries
+		mapBackground : '#bbd0ed',
+		labelGrid : true, // show label grid on hover
+		maxPlaceLabels : 6, // Integer value for fixed number of place labels: 0 --> unlimited, 1 --> 1 label (won't be shown in popup, 2 --> is not possible because of others & all labels --> 3 labels, [3,...,N] --> [3,...,N] place labels)
+		selectDefault : true, // true, if strongest label should be selected as default
+		maxLabelIncrease : 2, // maximum increase (in em) for the font size of a label
+		labelHover : false, // true, to update on label hover
+		ieHighlightLabel : "color: COLOR1; background-color: COLOR0; filter:'progid:DXImageTransform.Microsoft.Alpha(Opacity=80)';-ms-filter:'progid:DXImageTransform.Microsoft.Alpha(Opacity=80)';", // css code for a highlighted place label in IE
+		highlightLabel : "color: COLOR0; text-shadow: 0 0 0.4em black, 0 0 0.4em black, 0 0 0.4em black, 0 0 0.4em COLOR0;", // css code for a highlighted place label
+		ieSelectedLabel : "color: COLOR1; font-weight: bold;", // css code for a selected place label in IE
+		selectedLabel : "color: COLOR1; font-weight: bold;", // css code for a selected place label
+		ieUnselectedLabel : "color: COLOR1; font-weight: normal;", // css code for an unselected place label in IE
+		unselectedLabel : "color: COLOR1; font-weight: normal;", // css code for an unselected place label
+		ieHoveredLabel : "color: COLOR1; font-weight: bold;", // css code for a hovered place label in IE
+		hoveredLabel : "color: COLOR1; font-weight: bold;", // css code for a hovered place label
+		circleGap : 0, // gap between the circles on the map (>=0)
+		circleOverlap : {
+			type: 'area', // 'area' or 'diameter' is possible
+			overlap: 0 // the percentage of allowed overlap (0<=overlap<=1)
+		}, // maximum allowed overlap in percent (if circleGap = 0, circleOverlap will be used)
+		minimumRadius : 4, // minimum radius of a circle with mimimal weight (>0)
+		circleOutline : 2, // false for no outline or a pixel value v with 0 < v
+		circleOpacity : 'balloon', // 'balloon' for dynamic opacity of the circles or a value t with 0 <= t <= 1
+		minTransparency : 0.55, // maximum transparency of a circle
+		maxTransparency : 0.8, // minimum transparency of a circle
+		binning : 'generic', // binning algorithm for the map, possible values are: 'generic', 'square', 'hexagonal', 'triangular' or false for 'no binning'
+		noBinningRadii : 'dynamic', // for 'no binning': 'static' for only minimum radii, 'dynamic' for increasing radii for increasing weights
+		circlePackings : true, // if circles of multiple result sets should be displayed in circle packs, if a binning is performed
+		binCount : 10, // number of bins for x and y dimension for lowest zoom level
+		showDescriptions : true, // true to show descriptions of data items (must be provided by kml/json), false if not
+		mapSelection : true, // show/hide select map dropdown
+		binningSelection : false, // show/hide binning algorithms dropdown
+		mapSelectionTools : true, // show/hide map selector tools
+		dataInformation : true, // show/hide data information
+		overlayVisibility : false, // initial visibility of additional overlays
+		//proxyHost : 'php/proxy.php?address=',	//required for selectCountry feature, if the requested GeoServer and GeoTemCo are NOT on the same server
+		placenameTagsStyle : 'value', // the style of the placenames "surrounding" a circle on hover. 'zoom' for tags based on zoom level (old behaviour), 'value' for new value-based
+		hideUnselected : false //hide unselected circles (features) on highlight/selection
+
+	};
+	if ( typeof options != 'undefined') {
+		$.extend(this.options, options);
+	}
+	
+	//if the user can change shape/color graphics have to be used
+	//but this will use circles as default shape
+	if (GeoTemConfig.allowUserShapeAndColorChange){
+		this.options.useGraphics = true;
+	}
+
+};
+
+MapConfig.prototype.getGraphic = function(id){
+	var dataset = GeoTemConfig.datasets[id];
+
+	var graphic;
+	if (typeof dataset.graphic !== "undefined"){
+		graphic = dataset.graphic;
+	} else{
+		graphic = this.options.graphics[id % this.options.graphics.length];
+	}
+	
+	var color;
+	if (typeof dataset.color !== "undefined"){
+		color = dataset.color;
+	} else{
+		color = GeoTemConfig.getColor(id);
+	}
+	return {
+		shape: graphic.shape,
+		rotation: graphic.rotation,
+		color: color
+	};
+};
+/*
+* MapGui.js
+*
+* Copyright (c) 2012, Stefan Jänicke. All rights reserved.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 3 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+* MA 02110-1301  USA
+*/
+
+/**
+ * @class MapGui
+ * Map GUI Implementation
+ * @author Stefan Jänicke (stjaenicke@informatik.uni-leipzig.de)
+ * @release 1.0
+ * @release date: 2012-07-27
+ * @version date: 2012-07-27
+ *
+ * @param {MapWidget} parent map widget object
+ * @param {HTML object} div parent div to append the map gui
+ * @param {JSON} options map configuration
+ */
+function MapGui(map, div, options, iid) {
+
+	var gui = this;
+	this.map = map;
+
+	this.container = div;
+	if (options.mapWidth) {
+		this.container.style.width = options.mapWidth;
+	}
+	if (options.mapHeight) {
+		this.container.style.height = options.mapHeight;
+	}
+	this.container.style.position = 'relative';
+
+	this.mapWindow = document.createElement("div");
+	this.mapWindow.setAttribute('class', 'mapWindow');
+	this.mapWindow.id = "mapWindow"+iid;
+	this.mapWindow.style.background = options.mapBackground;
+	this.container.appendChild(this.mapWindow);
+
+	this.mapContainer = document.createElement("div");
+	this.mapContainer.setAttribute('class', 'mapContainer');
+	this.mapContainer.id = "mapContainer"+iid;
+	this.mapContainer.style.position = "absolute";
+	this.mapContainer.style.zIndex = 0;
+	this.mapWindow.appendChild(this.mapContainer);
+
+	var toolbarTable = document.createElement("table");
+	toolbarTable.setAttribute('class', 'absoluteToolbar ddbToolbar');
+	this.container.appendChild(toolbarTable);
+	this.mapToolbar = toolbarTable;
+
+	var titles = document.createElement("tr");
+	toolbarTable.appendChild(titles);
+	var tools = document.createElement("tr");
+	toolbarTable.appendChild(tools);
+
+	if (options.mapSelection) {
+		this.mapTypeTitle = document.createElement("td");
+		titles.appendChild(this.mapTypeTitle);
+		this.mapTypeTitle.innerHTML = GeoTemConfig.getString('mapType');
+		this.mapTypeSelector = document.createElement("td");
+		tools.appendChild(this.mapTypeSelector);
+	}
+
+	if (options.mapSelectionTools) {
+		this.mapSelectorTitle = document.createElement("td");
+		titles.appendChild(this.mapSelectorTitle);
+		this.mapSelectorTitle.innerHTML = GeoTemConfig.getString('mapSelectorTools');
+		var mapSelectorTools = document.createElement("td");
+		var selectorTools = this.map.initSelectorTools();
+		for (var i in selectorTools ) {
+			mapSelectorTools.appendChild(selectorTools[i].button);
+		}
+		tools.appendChild(mapSelectorTools);
+	}
+
+	if (options.binningSelection) {
+		this.binningTitle = document.createElement("td");
+		titles.appendChild(this.binningTitle);
+		this.binningTitle.innerHTML = GeoTemConfig.getString('binningType');
+		this.binningSelector = document.createElement("td");
+		tools.appendChild(this.binningSelector);
+	}
+
+	if (GeoTemConfig.allowFilter) {
+		this.filterTitle = document.createElement("td");
+		titles.appendChild(this.filterTitle);
+		this.filterTitle.innerHTML = GeoTemConfig.getString('filter');
+		this.filterOptions = document.createElement("td");
+		tools.appendChild(this.filterOptions);
+	}
+
+	if (options.dataInformation) {
+		this.infoTitle = document.createElement("td");
+		this.infoTitle.innerHTML = options.mapTitle;
+		titles.appendChild(this.infoTitle);
+		var mapSum = document.createElement("td");
+		this.mapElements = document.createElement("div");
+		this.mapElements.setAttribute('class', 'ddbElementsCount');
+		mapSum.appendChild(this.mapElements);
+		tools.appendChild(mapSum);
+	}
+	
+	this.lockTitle = document.createElement("td");
+	titles.appendChild(this.lockTitle);
+	this.lockIcon = document.createElement("td");
+	var lockButton = document.createElement("div");
+	$(lockButton).addClass('mapControl');
+	var activateLock = function() {
+		map.navigation.deactivate();
+	}
+	var deactivateLock = function() {
+		map.navigation.activate();
+	}
+	var lockMapControl = new MapControl(this.map, lockButton, 'lock', activateLock, deactivateLock);
+	tools.appendChild(lockMapControl.button);
+
+	this.fullscreenTitle = document.createElement("td");
+	titles.appendChild(this.fullscreenTitle);
+	this.fullscreenIcon = document.createElement("td");
+	var fullscreenButton = document.createElement("div");
+	$(fullscreenButton).addClass('mapControl');
+	var prevWidth;
+	var prevHeight;
+	var prevParent;
+	var activateFullscreen = function() {
+		$div=$(div);
+		$window = $(window);
+
+		prevWidth = $div.width();
+		prevHeight = $div.height();
+		prevParent = $div.parent();
+
+		$div.appendTo("body");
+		$div.css("position","absolute");
+		$div.css("top","0");
+		$div.css("left","0");
+		$div.css("z-Index","10000");
+	 	$div.width($window.width());
+		$div.height($window.height());
+
+		gui.resize();
+	}
+	var deactivateFullscreen = function() {
+		$div=$(div);
+
+		$div.appendTo(prevParent);
+		$div.css("position","relative");
+	 	$div.width(prevWidth);
+		$div.height(prevHeight);
+
+		gui.resize();
+	}
+	var fullscreenMapControl = new MapControl(this.map, fullscreenButton, 'fullscreen', activateFullscreen, deactivateFullscreen);
+	tools.appendChild(fullscreenMapControl.button);
+
+	if (navigator.geolocation && options.geoLocation) {
+		this.geoActive = false;
+		this.geoLocation = document.createElement("div");
+		this.geoLocation.setAttribute('class', 'geoLocationOff');
+		this.geoLocation.title = GeoTemConfig.getString('activateGeoLocation');
+		this.container.appendChild(this.geoLocation);
+		this.geoLocation.style.left = "20px";
+		this.geoLocation.onclick = function() {
+			var changeStyle = function() {
+				if (gui.geoActive) {
+					gui.geoLocation.setAttribute('class', 'geoLocationOn');
+					gui.geoLocation.title = GeoTemConfig.getString(GeoTemConfig.language, 'deactivateGeoLocation');
+				} else {
+					gui.geoLocation.setAttribute('class', 'geoLocationOff');
+					gui.geoLocation.title = GeoTemConfig.getString(GeoTemConfig.language, 'activateGeoLocation');
+				}
+			}
+			if (!gui.geoActive) {
+				if ( typeof gui.longitude == 'undefined') {
+					navigator.geolocation.getCurrentPosition(function(position) {
+						gui.longitude = position.coords.longitude;
+						gui.latitude = position.coords.latitude;
+						gui.map.setMarker(gui.longitude, gui.latitude);
+						gui.geoActive = true;
+						changeStyle();
+					}, function(msg) {
+						console.log( typeof msg == 'string' ? msg : "error");
+					});
+				} else {
+					gui.map.setMarker(gui.longitude, gui.latitude);
+					gui.geoActive = true;
+					changeStyle();
+				}
+			} else {
+				gui.map.removeMarker();
+				gui.geoActive = false;
+				changeStyle();
+			}
+		}
+	}
+
+	if (!options.olNavigation) {
+		this.map.zoomSlider = new MapZoomSlider(this.map, "vertical");
+		this.container.appendChild(this.map.zoomSlider.div);
+		this.map.zoomSlider.div.style.left = "20px";
+	}
+
+	if (options.resetMap) {
+		this.homeButton = document.createElement("div");
+		this.homeButton.setAttribute('class', 'mapHome');
+		this.homeButton.title = GeoTemConfig.getString('home');
+		this.container.appendChild(this.homeButton);
+		this.homeButton.style.left = "20px";
+		this.homeButton.onclick = function() {
+			if (map.mds.getAllObjects() == null){
+				map.openlayersMap.setCenter(new OpenLayers.LonLat(0, 0));
+				map.openlayersMap.zoomTo(0);
+			}
+			gui.map.drawObjectLayer(true);
+		}
+	}
+
+	if (options.legend) {
+		this.legendDiv = document.createElement("div");
+		this.legendDiv.setAttribute('class', 'mapLegend');
+		this.mapWindow.appendChild(this.legendDiv);
+	}
+
+	var linkForOsm = 'http://www.openstreetmap.org/';
+	var linkForLicense = 'http://creativecommons.org/licenses/by-sa/2.0/';
+	this.osmLink = document.createElement("div");
+	this.osmLink.setAttribute('class', 'osmLink');
+	this.osmLink.innerHTML = '(c) <a href=' + linkForOsm + '>OpenStreetMap contributors</a>, <a href=' + linkForLicense + '>CC-BY-SA</a>';
+	this.mapWindow.appendChild(this.osmLink);
+	this.osmMapQuestLink = document.createElement("div");
+	this.osmMapQuestLink.setAttribute('class', 'osmLink');
+	this.osmMapQuestLink.innerHTML = '(c) Data, imagery and map information provided by MapQuest <a href="http://www.mapquest.com/" target="_blank">MapQuest</a> <img src="http://developer.mapquest.com/content/osm/mq_logo.png"> <a href=' + linkForOsm + '>OpenStreetMap contributors</a>, <a href=' + linkForLicense + '>CC-BY-SA</a>';
+	this.mapWindow.appendChild(this.osmMapQuestLink);
+
+	//		var tooltip = document.createElement("div");
+	//		tooltip.setAttribute('class','ddbTooltip');
+	//		toolbarTable.appendChild(tooltip);
+
+	//		var tooltip = document.createElement("div");
+	//		tooltip.setAttribute('class','ddbTooltip');
+	//		toolbarTable.appendChild(tooltip);
+	//
+	//		tooltip.onmouseover = function(){
+	//			/*
+	//		    Publisher.Publish('TooltipContent', {
+	//						content: GeoTemConfig.getString(GeoTemConfig.language,'timeHelp'),
+	//						target: $(tooltip)
+	//					    });
+	//			*/
+	//		}
+	//		tooltip.onmouseout = function(){
+	//		 //   Publisher.Publish('TooltipContent');
+	//		}
+	//		//vhz tooltip on click should open a help file if defined in GeoTemConfig
+	//		if(GeoTemConfig.helpURL) {
+	//			tooltip.onclick = function () {
+	//
+	//			}
+	//		}
+
+	//		}
+	//		tooltip.onmouseout = function(){
+	//   			Publisher.Publish('TooltipContent');
+	//		}
+
+	this.resize = function() {
+		var w = this.container.offsetWidth;
+		var h = this.container.offsetHeight;
+//		this.mapWindow.style.width = w + "px";
+		this.mapWindow.style.height = h + "px";
+//		this.mapContainer.style.width = w + "px";
+		this.mapContainer.style.height = h + "px";
+		var top = toolbarTable.offsetHeight + 20;
+		if (options.olLayerSwitcher) {
+			var switcherDiv = $('.olControlLayerSwitcher')[0];
+			$(switcherDiv).css('top', top + "px");
+		}
+		if ( typeof this.geoLocation != "undefined") {
+			this.geoLocation.style.top = top + "px";
+			top += this.geoLocation.offsetHeight + 4;
+		}
+		if (options.olNavigation) {
+			var panZoomBar = $('.olControlPanZoom')[0];
+			$(panZoomBar).css('top', top + 'px');
+			$(panZoomBar).css('left', '12px');
+			var zoomOut = document.getElementById('OpenLayers.Control.PanZoom_23_zoomout');
+			top += $(zoomOut).height() + $(zoomOut).position().top + 4;
+		} else {
+			this.map.zoomSlider.div.style.top = top + "px";
+			top += this.map.zoomSlider.div.offsetHeight + 2;
+		}
+		if (options.resetMap) {
+			this.homeButton.style.top = top + "px";
+		}
+		this.headerHeight = toolbarTable.offsetHeight;
+		this.headerWidth = toolbarTable.offsetWidth;
+		this.map.openlayersMap.updateSize();
+		this.map.drawObjectLayer(true);
+	};
+
+	this.updateLegend = function(datasets){
+		$(this.legendDiv).empty();
+		var table = $('<table style="margin:10px"/>').appendTo(this.legendDiv);
+		for( var i=0; i<datasets.length; i++ ){
+			var row = $('<tr/>').appendTo(table);
+			if( options.useGraphics ){
+				var graphic = map.config.getGraphic(i);
+				var fill = 'rgb(' + graphic.color.r0 + ',' + graphic.color.g0 + ',' + graphic.color.b0 + ')';
+				var stroke = 'rgb(' + graphic.color.r1 + ',' + graphic.color.g1 + ',' + graphic.color.b1 + ')';
+				var rot = graphic.rotation;
+				var svg;
+				if( graphic.shape == 'circle' ){
+					svg = '<svg style="width:20px;height:20px;"><circle cx="10" cy="10" r="7" stroke="'+stroke+'" stroke-width="2" fill="'+fill+'"/></svg>';
+				}
+				else if( graphic.shape == 'square' ){
+					svg = '<svg style="width:20px;height:20px;"><polygon points="4,4 16,4 16,16 4,16" style="fill:'+fill+';stroke:'+stroke+';stroke-width:2" transform="rotate('+rot+' 10,10)"/></svg>';
+				}
+				else if( graphic.shape == 'triangle' ){
+					svg = '<svg style="width:20px;height:20px;"><polygon points="3,17 17,17 10,5" style="fill:'+fill+';stroke:'+stroke+';stroke-width:2" transform="rotate('+rot+' 10,10)"/></svg>';
+				}
+				$('<td>'+svg+'</td>').appendTo(row);
+			}
+			else {
+				var c = GeoTemConfig.getColor(i);
+				var fill = 'rgb(' + c.r0 + ',' + c.g0 + ',' + c.b0 + ')';
+				var stroke = 'rgb(' + c.r1 + ',' + c.g1 + ',' + c.b1 + ')';
+				var svg = '<svg style="width:20px;height:20px;"><circle cx="10" cy="10" r="7" stroke="'+stroke+'" stroke-width="2" fill="'+fill+'"/></svg>';
+				$('<td>'+svg+'</td>').appendTo(row);
+			}			
+			$('<td>'+datasets[i].label+'</td>').appendTo(row);
+		}
+	};
+
+	this.updateSpaceQuantity = function(count) {
+		if (!options.dataInformation) {
+			return;
+		}
+		this.mapCount = count;
+		if (count != 1) {
+			this.mapElements.innerHTML = this.beautifyCount(count) + " " + GeoTemConfig.getString('results');
+		} else {
+			this.mapElements.innerHTML = this.beautifyCount(count) + " " + GeoTemConfig.getString('result');
+		}
+	}
+
+	this.setMapsDropdown = function() {
+		if (!options.mapSelection) {
+			return;
+		}
+		$(this.mapTypeSelector).empty();
+		var maps = [];
+		var gui = this;
+		var addMap = function(name, index) {
+			var setMap = function() {
+				gui.map.setMap(index);
+			}
+			maps.push({
+				name : name,
+				onclick : setMap
+			});
+		}
+		for (var i = 0; i < this.map.baseLayers.length; i++) {
+			addMap(this.map.baseLayers[i].name, i);
+		}
+		this.mapTypeDropdown = new Dropdown(this.mapTypeSelector, maps, GeoTemConfig.getString('selectMapType'));
+	}
+
+	this.setMap = function() {
+		if (options.mapSelection) {
+			this.mapTypeDropdown.setEntry(this.map.baselayerIndex);
+		}
+	}
+
+	this.setBinningDropdown = function() {
+		if (!options.binningSelection) {
+			return;
+		}
+		$(this.binningSelector).empty();
+		var binnings = [];
+		var gui = this;
+		var index = 0;
+		var entry;
+		var addBinning = function(name, id) {
+			if (options.binning == id) {
+				entry = index;
+			} else {
+				index++;
+			}
+			var setBinning = function() {
+				options.binning = id;
+				gui.map.initWidget(gui.map.datasets, false);
+				gui.map.riseLayer();
+			}
+			binnings.push({
+				name : name,
+				onclick : setBinning
+			});
+		}
+		addBinning(GeoTemConfig.getString('genericBinning'), 'generic');
+		addBinning(GeoTemConfig.getString('squareBinning'), 'square');
+		addBinning(GeoTemConfig.getString('hexagonalBinning'), 'hexagonal');
+		addBinning(GeoTemConfig.getString('triangularBinning'), 'triangular');
+		addBinning(GeoTemConfig.getString('noBinning'), false);
+		var binningDropdown = new Dropdown(this.binningSelector, binnings, GeoTemConfig.getString('binningTooltip'));
+		binningDropdown.setEntry(entry);
+	}
+	this.setBinningDropdown();
+
+	this.beautifyCount = function(count) {
+		var c = count + '';
+		var p = 0;
+		var l = c.length;
+		while (l - p > 3) {
+			p += 3;
+			c = c.substring(0, l - p) + "." + c.substring(l - p);
+			p++;
+			l++;
+		}
+		return c;
+	}
+
+};
 /*
 * MapWidget.js
 *
@@ -34244,6 +35874,18 @@
 	 * updates the the object layer of the map after selections had been executed in timeplot or table or zoom level has changed
 	 */
 	highlightChanged : function(mapObjects) {
+		var hideEmptyCircles = false;
+
+		if (this.config.options.hideUnselected){
+			var overallCnt = 0;
+			for (var i in mapObjects){
+				overallCnt += mapObjects[i].length;
+			}
+			if (overallCnt > 0){
+				hideEmptyCircles = true;
+			}
+		}
+		
 		if( !GeoTemConfig.highlightEvents ){
 			return;
 		}
@@ -34257,10 +35899,19 @@
 		var polygon = this.openlayersMap.getExtent().toGeometry();
 		for (var i in points ) {
 			for (var j in points[i] ) {
+				var point = points[i][j];
+				
+				if (hideEmptyCircles){
+					point.feature.style.display = 'none';
+				} else {
+					point.feature.style.display = '';
+				} 
+					
 				this.updatePoint(points[i][j], polygon);
 			}
 		}
 		this.displayConnections();
+		this.objectLayer.redraw();
 	},
 
 	selectionChanged : function(selection) {
@@ -34683,1765 +36334,1765 @@
 		}
 	}
 }
-/*
-* TimeConfig.js
-*
-* Copyright (c) 2012, Stefan Jänicke. All rights reserved.
-*
-* This library is free software; you can redistribute it and/or
-* modify it under the terms of the GNU Lesser General Public
-* License as published by the Free Software Foundation; either
-* version 3 of the License, or (at your option) any later version.
-*
-* This library is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-* Lesser General Public License for more details.
-*
-* You should have received a copy of the GNU Lesser General Public
-* License along with this library; if not, write to the Free Software
-* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
-* MA 02110-1301  USA
-*/
-
-/**
- * @class TimeConfig
- * Time Configuration File
- * @author Stefan Jänicke (stjaenicke@informatik.uni-leipzig.de)
- * @release 1.0
- * @release date: 2012-07-27
- * @version date: 2012-07-27
- */
-function TimeConfig(options) {
-
-	this.options = {
-		timeTitle : 'GeoTemCo Time View', // title will be shown in timeplot header
-		timeIndex : 0, // index = position in date array; for multiple dates the 2nd timeplot refers to index 1
-		timeWidth : false, // false or desired width css definition for the timeplot
-		timeHeight : '100px', // false or desired height css definition for the timeplot
-		defaultMinDate : new Date(2012, 0, 1), // required, when empty timelines are possible
-		defaultMaxDate : new Date(), // required, when empty timelines are possible
-		timeCanvasFrom : '#EEE', // time widget background gradient color top
-		timeCanvasTo : '#EEE', // time widget background gradient color bottom
-		rangeBoxColor : "white", // fill color for time range box
-		rangeBorder : "1px solid #de7708", // border of frames
-		dataInformation : true, // show/hide data information
-		rangeAnimation : true, // show/hide animation buttons
-		scaleSelection : true, // show/hide scale selection buttons
-		linearScale : true, // true for linear value scaling, false for logarithmic
-		unitSelection : true, // show/hide time unit selection dropdown
-		timeUnit : -1, // minimum temporal unit (SimileAjax.DateTime or -1 if none) of the data
-		timeMerge : false // if the elements of distinct datasets should be merged into one set or not
-	};
-	if ( typeof options != 'undefined') {
-		$.extend(this.options, options);
-	}
-
-};
-/*
-* TimeGui.js
-*
-* Copyright (c) 2012, Stefan Jänicke. All rights reserved.
-*
-* This library is free software; you can redistribute it and/or
-* modify it under the terms of the GNU Lesser General Public
-* License as published by the Free Software Foundation; either
-* version 3 of the License, or (at your option) any later version.
-*
-* This library is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-* Lesser General Public License for more details.
-*
-* You should have received a copy of the GNU Lesser General Public
-* License along with this library; if not, write to the Free Software
-* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
-* MA 02110-1301  USA
-*/
-
-/**
- * @class TimeGui
- * Time GUI Implementation
- * @author Stefan Jänicke (stjaenicke@informatik.uni-leipzig.de)
- * @release 1.0
- * @release date: 2012-07-27
- * @version date: 2012-07-27
- *
- * @param {TimeWidget} parent time widget object
- * @param {HTML object} div parent div to append the time gui
- * @param {JSON} options time configuration
- */
-function TimeGui(plot, div, options, iid) {
-
-	var gui = this;
-
-	this.plot = plot;
-
-	this.container = div;
-	if (options.timeWidth) {
-		this.container.style.width = options.timeWidth;
-	}
-	if (options.timeHeight) {
-		this.container.style.height = options.timeHeight;
-	}
-	this.container.style.position = 'relative';
-
-	var w = this.container.offsetWidth;
-	var h = this.container.offsetHeight;
-
-	var toolbarTable = document.createElement("table");
-	toolbarTable.setAttribute('class', 'ddbToolbar');
-	this.container.appendChild(toolbarTable);
-
-	this.plotWindow = document.createElement("div");
-	this.plotWindow.id = "plotWindow"+iid;
-	this.plotWindow.setAttribute('class', 'plotWindow');
-//	this.plotWindow.style.width = w + "px";
-
-	this.plotWindow.style.height = (h + 12) + "px";
-	this.container.style.height = (h + 12) + "px";
-
-	this.plotWindow.onmousedown = function() {
-		return false;
-	}
-
-	this.plotContainer = document.createElement("div");
-	this.plotContainer.id = "plotContainer"+iid;
-	this.plotContainer.setAttribute('class', 'plotContainer');
-//	this.plotContainer.style.width = w + "px";
-	this.plotContainer.style.height = h + "px";
-	this.plotContainer.style.position = "absolute";
-	this.plotContainer.style.zIndex = 0;
-	this.plotContainer.style.top = "12px";
-	this.plotWindow.appendChild(this.plotContainer);
-	this.container.appendChild(this.plotWindow);
-
-	this.timeplotDiv = document.createElement("div");
-	this.timeplotDiv.style.left = "16px";
-	this.timeplotDiv.style.width = (w - 32) + "px";
-	this.timeplotDiv.style.height = h + "px";
-	this.plotContainer.appendChild(this.timeplotDiv);
-
-	var cv = document.createElement("canvas");
-	cv.setAttribute('class', 'plotCanvas');
-	this.plotWindow.appendChild(cv);
-	if (!cv.getContext && G_vmlCanvasManager)
-		cv = G_vmlCanvasManager.initElement(cv);
-	var ctx = cv.getContext('2d');
-
-	var setCanvas = function(){
-		cv.width = gui.plotWindow.clientWidth;
-		cv.height = gui.plotWindow.clientHeight;
-		var gradient = ctx.createLinearGradient(0, 0, 0, gui.plotWindow.clientHeight);
-		gradient.addColorStop(0, options.timeCanvasFrom);
-		gradient.addColorStop(1, options.timeCanvasTo);
-		ctx.fillStyle = gradient;
-		ctx.fillRect(0, 0, gui.plotWindow.clientWidth, gui.plotWindow.clientHeight);
-	}
-	setCanvas();
-
-	this.resize = function(){
-		gui.timeplotDiv.style.width = (gui.container.offsetWidth - 32) + "px";
-		ctx.clearRect(0,0,gui.plotWindow.clientWidth, gui.plotWindow.clientHeight);
-		if( typeof plot.datasets != "undefined" ){
-			plot.redrawPlot();
-			plot.resetOpacityPlots();
-		}
-		setCanvas();
-	};
-
-	var titles = document.createElement("tr");
-	toolbarTable.appendChild(titles);
-	var tools = document.createElement("tr");
-	toolbarTable.appendChild(tools);
-
-	this.timeUnitTitle = document.createElement("td");
-	this.timeUnitTitle.innerHTML = GeoTemConfig.getString('timeUnit');
-	this.timeUnitSelector = document.createElement("td");
-	if (options.unitSelection) {
-		tools.appendChild(this.timeUnitSelector);
-		titles.appendChild(this.timeUnitTitle);
-	}
-
-	this.timeAnimation = document.createElement("td");
-	this.timeAnimation.innerHTML = GeoTemConfig.getString('timeAnimation');
-	var timeAnimationTools = document.createElement("td");
-
-	var status;
-	this.updateAnimationButtons = function(s) {
-		status = s;
-		if (status == 0) {
-			gui.playButton.setAttribute('class', 'smallButton playDisabled');
-			gui.pauseButton.setAttribute('class', 'smallButton pauseDisabled');
-		} else if (status == 1) {
-			gui.playButton.setAttribute('class', 'smallButton playEnabled');
-			gui.pauseButton.setAttribute('class', 'smallButton pauseDisabled');
-		} else {
-			gui.playButton.setAttribute('class', 'smallButton playDisabled');
-			gui.pauseButton.setAttribute('class', 'smallButton pauseEnabled');
-		}
-	};
-	this.playButton = document.createElement("div");
-	this.playButton.title = GeoTemConfig.getString('playButton');
-	timeAnimationTools.appendChild(this.playButton);
-	this.playButton.onclick = function() {
-		if (status == 1) {
-			plot.play();
-		}
-	}
-
-	this.pauseButton = document.createElement("div");
-	this.pauseButton.title = GeoTemConfig.getString('pauseButton');
-	timeAnimationTools.appendChild(this.pauseButton);
-	this.pauseButton.onclick = function() {
-		if (status == 2) {
-			plot.stop();
-		}
-	}
-
-	this.valueScale = document.createElement("td");
-	this.valueScale.innerHTML = GeoTemConfig.getString('valueScale');
-	var valueScaleTools = document.createElement("td");
-
-	var linearPlot;
-	var setValueScale = function(linScale) {
-		if (linearPlot != linScale) {
-			linearPlot = linScale;
-			if (linearPlot) {
-				gui.linButton.setAttribute('class', 'smallButton linearPlotActivated');
-				gui.logButton.setAttribute('class', 'smallButton logarithmicPlotDeactivated');
-				plot.drawLinearPlot();
-			} else {
-				gui.linButton.setAttribute('class', 'smallButton linearPlotDeactivated');
-				gui.logButton.setAttribute('class', 'smallButton logarithmicPlotActivated');
-				plot.drawLogarithmicPlot();
-			}
-		}
-	};
-	this.linButton = document.createElement("div");
-	this.linButton.title = GeoTemConfig.getString('linearPlot');
-	valueScaleTools.appendChild(this.linButton);
-	this.linButton.onclick = function() {
-		setValueScale(true);
-	}
-
-	this.logButton = document.createElement("div");
-	this.logButton.title = GeoTemConfig.getString('logarithmicPlot');
-	valueScaleTools.appendChild(this.logButton);
-	this.logButton.onclick = function() {
-		setValueScale(false);
-	}
-	if (options.rangeAnimation) {
-		titles.appendChild(this.timeAnimation);
-		tools.appendChild(timeAnimationTools);
-		this.updateAnimationButtons(0);
-	}
-
-	if (options.scaleSelection) {
-		titles.appendChild(this.valueScale);
-		tools.appendChild(valueScaleTools);
-		setValueScale(options.linearScale);
-	}
-
-	if (GeoTemConfig.allowFilter) {
-		this.filterTitle = document.createElement("td");
-		titles.appendChild(this.filterTitle);
-		this.filterTitle.innerHTML = GeoTemConfig.getString('filter');
-		this.filterOptions = document.createElement("td");
-		tools.appendChild(this.filterOptions);
-	}
-
-	if (options.dataInformation) {
-		this.infoTitle = document.createElement("td");
-		this.infoTitle.innerHTML = options.timeTitle;
-		titles.appendChild(this.infoTitle);
-		var timeSum = document.createElement("td");
-		this.timeElements = document.createElement("div");
-		this.timeElements.setAttribute('class', 'ddbElementsCount');
-		timeSum.appendChild(this.timeElements);
-		tools.appendChild(timeSum);
-	}
-
-	/*
-	 var tooltip = document.createElement("div");
-	 tooltip.setAttribute('class','ddbTooltip');
-	 toolbarTable.appendChild(tooltip);
-
-	 tooltip.onmouseover = function(){
-	 /*
-	 getPublisher().Publish('TooltipContent', {
-	 content: GeoTemConfig.getString(GeoTemConfig.language,'timeHelp'),
-	 target: $(tooltip)
-	 });
-
-	 }
-	 tooltip.onmouseout = function(){
-	 //getPublisher().Publish('TooltipContent');
-	 }
-	 */
-
-	this.setHeight = function() {
-		this.container.style.height = (this.plotWindow.offsetHeight + toolbarTable.offsetHeight) + "px";
-	};
-
-	this.updateTimeQuantity = function(count) {
-		if (options.dataInformation) {
-			this.plotCount = count;
-			if (count != 1) {
-				this.timeElements.innerHTML = this.beautifyCount(count) + " " + GeoTemConfig.getString('results');
-			} else {
-				this.timeElements.innerHTML = this.beautifyCount(count) + " " + GeoTemConfig.getString('result');
-			}
-		}
-	}
-
-	this.setTimeUnitDropdown = function(units) {
-		$(this.timeUnitSelector).empty();
-		var gui = this;
-		var timeUnits = [];
-		var addUnit = function(unit, index) {
-			var setUnit = function() {
-				gui.plot.setTimeUnit(unit.unit);
-			}
-			timeUnits.push({
-				name : unit.label,
-				onclick : setUnit
-			});
-		}
-		for (var i = 0; i < units.length; i++) {
-			addUnit(units[i], i);
-		}
-		this.timeUnitDropdown = new Dropdown(this.timeUnitSelector, timeUnits, GeoTemConfig.getString('selectTimeUnit'), '100px');
-		this.timeUnitDropdown.setEntry(0);
-	}
-	this.setTimeUnitDropdown([{
-		name : 'none',
-		id : -1
-	}]);
-
-	this.beautifyCount = function(count) {
-		var c = count + '';
-		var p = 0;
-		var l = c.length;
-		while (l - p > 3) {
-			p += 3;
-			c = c.substring(0, l - p) + "." + c.substring(l - p);
-			p++;
-			l++;
-		}
-		return c;
-	}
-
-	this.hideTimeUnitSelection = function() {
-		this.timeUnitTitle.style.display = 'none';
-		this.timeUnitSelector.style.display = 'none';
-	}
-};
-/*
-* TimeWidget.js
-*
-* Copyright (c) 2012, Stefan Jänicke. All rights reserved.
-*
-* This library is free software; you can redistribute it and/or
-* modify it under the terms of the GNU Lesser General Public
-* License as published by the Free Software Foundation; either
-* version 3 of the License, or (at your option) any later version.
-*
-* This library is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-* Lesser General Public License for more details.
-*
-* You should have received a copy of the GNU Lesser General Public
-* License along with this library; if not, write to the Free Software
-* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
-* MA 02110-1301  USA
-*/
-
-/**
- * @class TimeWidget
- * TableWidget Implementation
- * @author Stefan Jänicke (stjaenicke@informatik.uni-leipzig.de)
- * @release 1.0
- * @release date: 2012-07-27
- * @version date: 2012-07-27
- *
- * @param {TimeWrapper} core wrapper for interaction to other widgets
- * @param {HTML object} div parent div to append the time widget div
- * @param {JSON} options user specified configuration that overwrites options in TimeConfig.js
- */
-TimeWidget = function(core, div, options) {
-
-	this.core = core;
-	this.core.setWidget(this);
-	this.timeplot
-	this.dataSources
-	this.eventSources
-	this.tds
-	this.timeGeometry
-	this.valueGeometry
-	this.canvas
-
-	this.leftFlagPole
-	this.rightFlagPole
-	this.rangeBox
-	this.leftHandle
-	this.rightHandle
-
-	this.leftFlagPos = null;
-	this.leftFlagTime = null;
-	this.rightFlagPos = null;
-	this.rightFlagTime = null;
-
-	this.mouseDownTime
-	this.mouseUpTime
-	this.mouseTempTime
-	this.mouseDownPos
-	this.mouseUpPos
-	this.mouseTempPos
-
-	this.status
-	this.slider
-
-	this.iid = GeoTemConfig.getIndependentId('time');
-	this.options = (new TimeConfig(options)).options;
-	this.gui = new TimeGui(this, div, this.options, this.iid);
-	this.initialize();
-
-}
-
-TimeWidget.prototype = {
-
-	/**
-	 * clears the timeplot canvas and the timeGeometry properties
-	 */
-	clearTimeplot : function() {
-		this.timeplot._clearCanvas();
-		this.timeGeometry._earliestDate = null;
-		this.timeGeometry._latestDate = null;
-		this.valueGeometry._minValue = 0;
-		this.valueGeometry._maxValue = 0;
-		this.highlightedSlice = undefined;
-		this.timeGeometry._clearLabels();
-		this.selection = new Selection();
-	},
-
-	/**
-	 * initializes the timeplot elements with arrays of time objects
-	 * @param {TimeObject[][]} timeObjects an array of time objects from different (1-4) sets
-	 */
-	initWidget : function(datasets) {
-		this.datasets = datasets;
-		var timeObjects = [];
-		for (var i = 0; i < datasets.length; i++) {
-			timeObjects.push(datasets[i].objects);
-		}
-		this.clearTimeplot();
-		this.reset();
-		for (var i = 0; i < this.timeplot._plots.length; i++) {
-			this.timeplot._plots[i].dispose();
-		}
-		this.dataSources = new Array();
-		this.plotInfos = new Array();
-		this.eventSources = new Array();
-		var granularity = 0;
-		this.count = 0;
-		for (var i = 0; i < timeObjects.length; i++) {
-			if( i==0 || !this.options.timeMerge ){
-				var eventSource = new Timeplot.DefaultEventSource();
-				var dataSource = new Timeplot.ColumnSource(eventSource, 1);
-				this.dataSources.push(dataSource);
-				this.eventSources.push(eventSource);
-				var c = GeoTemConfig.getColor(i);
-				var plotInfo = Timeplot.createPlotInfo({
-					id : "plot" + i,
-					dataSource : dataSource,
-					timeGeometry : this.timeGeometry,
-					valueGeometry : this.valueGeometry,
-					fillGradient : false,
-					lineColor : 'rgba(' + c.r1 + ',' + c.g1 + ',' + c.b1 + ', 1)',
-					fillColor : 'rgba(' + c.r0 + ',' + c.g0 + ',' + c.b0 + ', 0.3)',
-					showValues : true
-				});
-				this.plotInfos.push(plotInfo);
-			}
-			for (var j = 0; j < timeObjects[i].length; j++) {
-				var o = timeObjects[i][j];
-				if (o.isTemporal) {
-					var g = o.dates[this.options.timeIndex].granularity;
-					if (g == null) {
-						continue;
-					} else if (g > granularity) {
-						granularity = g;
-					}
-					this.count += o.weight;
-				}
-			}
-		}
-		this.timeGeometry._granularity = granularity;
-		this.timeGeometry._clearLabels();
-		this.timeplot.resetPlots(this.plotInfos);
-		if (this.plotInfos.length == 0) {
-			this.initLabels(this.timeplot.regularGrid());
-			return;
-		}
-		this.timeGeometry.extendedDataSource = this.tds;
-		this.tds.initialize(this.dataSources, this.eventSources, timeObjects, granularity, this.options.timeUnit, this.gui.timeplotDiv.offsetWidth);
-		this.gui.setTimeUnitDropdown(this.tds.availableUnits);
-		this.gui.timeUnitDropdown.setEntry(this.tds.getUnitIndex());
-		var plots = this.timeplot._plots;
-		for (var i = 0; i < plots.length; i++) {
-			plots[i].pins = [];
-			plots[i].style = this.style;
-			for (var j = 0; j < this.tds.getSliceNumber(); j++) {
-				plots[i].pins.push({
-					height : 0,
-					count : 0
-				});
-			}
-		}
-		/*
-		 var levels = Math.round( (this.tds.timeSlices.length-3)/2 );
-		 if( GeoTemConfig.timeZoom ){
-		 this.zoomSlider.setMaxAndLevels(levels,levels);
-		 }
-		 */
-		this.timeplot.repaint();
-		this.timeplot._resizeCanvas();
-		// set maximum number of slider steps
-		var slices = this.tds.timeSlices.length;
-		var numSlices = Math.floor(slices / this.canvas.width * this.canvas.height + 0.5);
-
-		this.initLabels([]);
-		this.initOverview();
-		this.gui.updateTimeQuantity(this.count);
-
-	},
-
-	setTimeUnit : function(unit) {
-		this.clearTimeplot();
-		this.reset();
-		this.tds.setTimeUnit(unit);
-		var plots = this.timeplot._plots;
-		for (var i = 0; i < plots.length; i++) {
-			plots[i].pins = [];
-			plots[i].style = this.style;
-			for (var j = 0; j < this.tds.getSliceNumber(); j++) {
-				plots[i].pins.push({
-					height : 0,
-					count : 0
-				});
-			}
-		}
-		this.initLabels([]);
-	},
-
-	/**
-	 * initializes the timeplot for the Spatio Temporal Interface.
-	 * all elements (including their events) that are needed for user interaction are instantiated here, the slider element as well
-	 */
-	initialize : function() {
-
-		this.status = 0;
-		this.selection = new Selection();
-		this.paused = true;
-		this.dataSources = new Array();
-		this.plotInfos = new Array();
-		this.eventSources = new Array();
-		this.timeGeometry = new Timeplot.DefaultTimeGeometry({
-			gridColor : "#000000",
-			axisLabelsPlacement : "top"
-		});
-		this.style = 'graph';
-		this.timeGeometry._hideLabels = true;
-		this.timeGeometry._granularity = 0;
-		this.valueGeometry = new Timeplot.LogarithmicValueGeometry({
-			min : 0
-		});
-		this.valueGeometry.actLinear();
-
-		var plot = this;
-
-		this.timeplot = Timeplot.create(this.gui.timeplotDiv, this.plotInfos);
-		this.tds = new TimeDataSource(this.options);
-
-		this.canvas = this.timeplot.getCanvas();
-
-		this.leftFlagPole = this.timeplot.putDiv("leftflagpole", "timeplot-dayflag-pole");
-		this.rightFlagPole = this.timeplot.putDiv("rightflagpole", "timeplot-dayflag-pole");
-		SimileAjax.Graphics.setOpacity(this.leftFlagPole, 50);
-		SimileAjax.Graphics.setOpacity(this.rightFlagPole, 50);
-
-		this.rangeBox = this.timeplot.putDiv("rangebox", "range-box");
-		this.rangeBox.style.backgroundColor = plot.options.rangeBoxColor;
-		this.rangeBox.style.border = plot.options.rangeBorder;
-
-		this.leftHandle = document.createElement("div");
-		this.rightHandle = document.createElement("div");
-		this.gui.plotWindow.appendChild(this.leftHandle);
-		this.gui.plotWindow.appendChild(this.rightHandle);
-		this.leftHandle.title = GeoTemConfig.getString('leftHandle');
-		this.rightHandle.title = GeoTemConfig.getString('rightHandle');
-
-		this.leftHandle.style.backgroundImage = "url(" + GeoTemConfig.path + "leftHandle.png" + ")";
-		this.leftHandle.setAttribute('class', 'plotHandle plotHandleIcon');
-		this.rightHandle.style.backgroundImage = "url(" + GeoTemConfig.path + "rightHandle.png" + ")";
-		this.rightHandle.setAttribute('class', 'plotHandle plotHandleIcon');
-
-		this.poles = this.timeplot.putDiv("poles", "pole");
-		this.timeplot.placeDiv(this.poles, {
-			left : 0,
-			bottom : 0,
-			width : this.canvas.width,
-			height : this.canvas.height,
-			display : "block"
-		});
-		this.poles.appendChild(document.createElement("canvas"));
-
-		this.filterBar = new FilterBar(this, this.gui.filterOptions);
-
-		var plot = this;
-
-		this.dragButton = document.createElement("div");
-		this.dragButton.title = GeoTemConfig.getString('dragTimeRange');
-		this.cancelButton = document.createElement("div");
-		this.cancelButton.title = GeoTemConfig.getString('clearSelection');
-		this.cancelButton.onclick = function() {
-			plot.deselection();
-		}
-
-		this.toolbar = document.createElement("div");
-		this.toolbar.setAttribute('class', 'plotToolbar');
-		this.toolbar.style.borderTop = plot.options.rangeBorder;
-		this.toolbar.style.textAlign = "center";
-		this.gui.plotWindow.appendChild(this.toolbar);
-
-		this.toolbarAbsoluteDiv = document.createElement("div");
-		this.toolbarAbsoluteDiv.setAttribute('class', 'absoluteToolbar');
-		this.toolbar.appendChild(this.toolbarAbsoluteDiv);
-
-		this.dragButton.setAttribute('class', 'dragTimeRangeAlt');
-		this.dragButton.style.backgroundImage = "url(" + GeoTemConfig.path + "drag.png" + ")";
-		//        	this.zoomButton.setAttribute('class','zoomRangeAlt');
-		this.cancelButton.setAttribute('class', 'cancelRangeAlt');
-		this.toolbarAbsoluteDiv.appendChild(this.dragButton);
-		this.toolbarAbsoluteDiv.style.width = this.dragButton.offsetWidth + "px";
-		//	        this.gui.plotWindow.appendChild(this.zoomButton);
-		this.gui.plotWindow.appendChild(this.cancelButton);
-
-		this.overview = document.createElement("div");
-		this.overview.setAttribute('class', 'timeOverview');
-		this.gui.plotWindow.appendChild(this.overview);
-
-		var mousedown = false;
-		this.shift = function(shift) {
-			if (!mousedown) {
-				return;
-			}
-			if (plot.tds.setShift(shift)) {
-				plot.redrawPlot();
-			}
-			setTimeout(function() {
-				plot.shift(shift);
-			}, 200);
-		}
-		var shiftPressed = function(shift) {
-			mousedown = true;
-			document.onmouseup = function() {
-				mousedown = false;
-				document.onmouseup = null;
-			}
-			plot.shift(shift);
-		}
-
-		this.shiftLeft = document.createElement("div");
-		this.shiftLeft.setAttribute('class', 'shiftLeft');
-		this.gui.plotWindow.appendChild(this.shiftLeft);
-		this.shiftLeft.onmousedown = function() {
-			shiftPressed(1);
-		}
-
-		this.shiftRight = document.createElement("div");
-		this.shiftRight.setAttribute('class', 'shiftRight');
-		this.gui.plotWindow.appendChild(this.shiftRight);
-		this.shiftRight.onmousedown = function() {
-			shiftPressed(-1);
-		}
-
-		this.plotLabels = document.createElement("div");
-		this.plotLabels.setAttribute('class', 'plotLabels');
-		this.gui.plotWindow.appendChild(this.plotLabels);
-
-		this.initLabels(this.timeplot.regularGrid());
-
-		//Finds the time corresponding to the position x on the timeplot
-		var getCorrelatedTime = function(x) {
-			if (x >= plot.canvas.width)
-				x = plot.canvas.width;
-			if (isNaN(x) || x < 0)
-				x = 0;
-			var t = plot.timeGeometry.fromScreen(x);
-			if (t == 0)
-				return;
-			return plot.dataSources[0].getClosestValidTime(t);
-		}
-		//Finds the position corresponding to the time t on the timeplot
-		var getCorrelatedPosition = function(t) {
-			var x = plot.timeGeometry.toScreen(t);
-			if (x >= plot.canvas.width)
-				x = plot.canvas.width;
-			if (isNaN(x) || x < 0)
-				x = 0;
-			return x;
-		}
-		//Maps the 2 positions in the right order to left and right bound of the chosen timeRange
-		var mapPositions = function(pos1, pos2) {
-			if (pos1 > pos2) {
-				plot.leftFlagPos = pos2;
-				plot.rightFlagPos = pos1;
-			} else {
-				plot.leftFlagPos = pos1;
-				plot.rightFlagPos = pos2;
-			}
-			plot.leftFlagTime = plot.dataSources[0].getClosestValidTime(plot.timeGeometry.fromScreen(plot.leftFlagPos));
-			plot.rightFlagTime = plot.dataSources[0].getClosestValidTime(plot.timeGeometry.fromScreen(plot.rightFlagPos));
-		}
-		//Sets the divs corresponding to the actual chosen timeRange
-		var setRangeDivs = function() {
-			plot.leftFlagPole.style.visibility = "visible";
-			plot.rightFlagPole.style.visibility = "visible";
-			plot.rangeBox.style.visibility = "visible";
-			plot.timeplot.placeDiv(plot.leftFlagPole, {
-				left : plot.leftFlagPos,
-				bottom : 0,
-				height : plot.canvas.height,
-				display : "block"
-			});
-			plot.timeplot.placeDiv(plot.rightFlagPole, {
-				left : plot.rightFlagPos,
-				bottom : 0,
-				height : plot.canvas.height,
-				display : "block"
-			});
-			var boxWidth = plot.rightFlagPos - plot.leftFlagPos;
-			if (plot.popup) {
-				plot.popupClickDiv.style.visibility = "visible";
-				plot.timeplot.placeDiv(plot.popupClickDiv, {
-					left : plot.leftFlagPos,
-					width : boxWidth + 1,
-					height : plot.canvas.height,
-					display : "block"
-				});
-			}
-			plot.timeplot.placeDiv(plot.rangeBox, {
-				left : plot.leftFlagPos,
-				width : boxWidth + 1,
-				height : plot.canvas.height,
-				display : "block"
-			});
-			var plots = plot.timeplot._plots;
-			for ( i = 0; i < plots.length; i++) {
-				plots[i].fullOpacityPlot(plot.leftFlagTime, plot.rightFlagTime, plot.leftFlagPos, plot.rightFlagPos, GeoTemConfig.getColor(i));
-				plots[i].opacityPlot.style.visibility = "visible";
-			}
-			var unit = plot.tds.unit;
-
-			var top = plot.gui.plotContainer.offsetTop;
-			var left = plot.gui.plotContainer.offsetLeft;
-			var leftPos = plot.leftFlagPole.offsetLeft + plot.timeplot.getElement().offsetLeft;
-			var rightPos = plot.rightFlagPole.offsetLeft + plot.timeplot.getElement().offsetLeft;
-			var rW = rightPos - leftPos;
-			var pW = plot.canvas.width;
-			var pL = plot.timeplot.getElement().offsetLeft;
-
-			var handleTop = top + Math.floor(plot.gui.timeplotDiv.offsetHeight / 2 - plot.leftHandle.offsetHeight / 2);
-			plot.leftHandle.style.visibility = "visible";
-			plot.rightHandle.style.visibility = "visible";
-			plot.leftHandle.style.left = (leftPos - plot.leftHandle.offsetWidth / 2) + "px";
-			plot.rightHandle.style.left = (rightPos - plot.rightHandle.offsetWidth + 1 + plot.rightHandle.offsetWidth / 2) + "px";
-			plot.leftHandle.style.top = handleTop + "px";
-			plot.rightHandle.style.top = handleTop + "px";
-			if (rightPos == leftPos) {
-				plot.rightHandle.style.visibility = "hidden";
-				plot.leftHandle.style.backgroundImage = "url(" + GeoTemConfig.path + "mergedHandle.png" + ")";
-			} else {
-				plot.leftHandle.style.backgroundImage = "url(" + GeoTemConfig.path + "leftHandle.png" + ")";
-			}
-			plot.cancelButton.style.visibility = "visible";
-			plot.cancelButton.style.top = top + "px";
-
-			if (rW > plot.cancelButton.offsetWidth) {
-				plot.cancelButton.style.left = (left + rightPos - plot.cancelButton.offsetWidth) + "px";
-			} else {
-				plot.cancelButton.style.left = (left + rightPos) + "px";
-			}
-			var tW = plot.toolbarAbsoluteDiv.offsetWidth;
-			if (rW >= tW) {
-				plot.toolbar.style.left = leftPos + "px";
-				plot.toolbar.style.width = (rW + 1) + "px";
-				plot.toolbarAbsoluteDiv.style.left = ((rW - tW) / 2) + "px";
-			} else {
-				plot.toolbar.style.left = (pL + plot.leftFlagPos * (pW - tW) / (pW - rW)) + "px";
-				plot.toolbar.style.width = (tW + 2) + "px";
-				plot.toolbarAbsoluteDiv.style.left = "0px";
-			}
-			plot.toolbar.style.top = (top + plot.timeplot.getElement().offsetHeight) + "px";
-			plot.toolbar.style.visibility = "visible";
-			plot.toolbarAbsoluteDiv.style.visibility = "visible";
-
-		}
-		var getAbsoluteLeft = function(div) {
-			var left = 0;
-			while (div) {
-				left += div.offsetLeft;
-				div = div.offsetParent;
-			}
-			return left;
-		}
-		var timeplotLeft = getAbsoluteLeft(plot.timeplot.getElement());
-
-		var checkPolesForStyle = function(x) {
-			if (plot.style == 'bars' && plot.leftFlagTime == plot.rightFlagTime) {
-				var index = plot.tds.getSliceIndex(plot.leftFlagTime);
-				var time1 = plot.leftFlagTime;
-				var pos1 = plot.leftFlagPos;
-				var time2, pos2;
-				if (index == 0) {
-					time2 = plot.tds.getSliceTime(index + 1);
-				} else if (index == plot.tds.getSliceNumber() - 1) {
-					time2 = plot.tds.getSliceTime(index - 1);
-				} else {
-					if (x < plot.leftFlagPos) {
-						time2 = plot.tds.getSliceTime(index - 1);
-					} else {
-						time2 = plot.tds.getSliceTime(index + 1);
-					}
-				}
-				pos2 = plot.timeGeometry.toScreen(time2);
-				mapPositions(pos1, pos2, time1, time2);
-			}
-		}
-		var startX, startY, multiplier;
-
-		// mousemove function that causes moving selection of objects and toolbar divs
-		var moveToolbar = function(start, actual) {
-			var pixelShift = actual - start;
-			if (plot.status == 2) {
-				var newTime = getCorrelatedTime(startX + pixelShift);
-				if (newTime == plot.mouseTempTime) {
-					return;
-				}
-				plot.mouseTempTime = newTime;
-				plot.mouseTempPos = plot.timeGeometry.toScreen(plot.mouseTempTime);
-				mapPositions(plot.mouseDownPos, plot.mouseTempPos);
-			} else if (plot.status == 3) {
-				pixelShift *= multiplier;
-				var plotPos = actual - timeplotLeft;
-				if (plotPos <= plot.canvas.width / 2) {
-					var newTime = getCorrelatedTime(startX + pixelShift);
-					if (newTime == plot.leftFlagTime) {
-						return;
-					}
-					plot.leftFlagTime = newTime;
-					var diff = plot.leftFlagPos;
-					plot.leftFlagPos = plot.timeGeometry.toScreen(plot.leftFlagTime);
-					diff -= plot.leftFlagPos;
-					plot.rightFlagTime = getCorrelatedTime(plot.rightFlagPos - diff);
-					plot.rightFlagPos = plot.timeGeometry.toScreen(plot.rightFlagTime);
-				} else {
-					var newTime = getCorrelatedTime(startY + pixelShift);
-					if (newTime == plot.rightFlagTime) {
-						return;
-					}
-					plot.rightFlagTime = newTime;
-					var diff = plot.rightFlagPos;
-					plot.rightFlagPos = plot.timeGeometry.toScreen(plot.rightFlagTime);
-					diff -= plot.rightFlagPos;
-					plot.leftFlagTime = getCorrelatedTime(plot.leftFlagPos - diff);
-					plot.leftFlagPos = plot.timeGeometry.toScreen(plot.leftFlagTime);
-				}
-			}
-			checkPolesForStyle(actual - timeplotLeft);
-			setRangeDivs();
-			plot.timeSelection();
-		}
-		// fakes user interaction mouse move
-		var playIt = function(start, actual, reset) {
-			if (!plot.paused) {
-				var pixel = plot.canvas.width / (plot.tds.timeSlices.length - 1 ) / 5;
-				var wait = 20 * pixel;
-				if (reset) {
-					actual = 0;
-				}
-				moveToolbar(start, actual);
-				if (plot.rightFlagPos >= plot.canvas.width) {
-					reset = true;
-					wait = 1000;
-				} else {
-					reset = false;
-				}
-				setTimeout(function() {
-					playIt(start, actual + pixel, reset)
-				}, wait);
-			}
-		}
-		var setMultiplier = function() {
-			var rangeWidth = plot.rightFlagPos - plot.leftFlagPos;
-			var toolbarWidth = plot.toolbarAbsoluteDiv.offsetWidth;
-			var plotWidth = plot.canvas.width;
-			if (rangeWidth < toolbarWidth) {
-				multiplier = (plotWidth - rangeWidth) / (plotWidth - toolbarWidth);
-			} else {
-				multiplier = 1;
-			}
-		}
-		/**
-		 * starts the animation
-		 */
-		this.play = function() {
-			if (this.leftFlagPos == null) {
-				return;
-			}
-			plot.paused = false;
-			plot.gui.updateAnimationButtons(2);
-			plot.status = 3;
-			setMultiplier();
-			startX = plot.leftFlagPos;
-			startY = plot.rightFlagPos;
-			var position = Math.round(plot.leftFlagPos);
-			playIt(position, position + 1, false);
-		}
-		/**
-		 * stops the animation
-		 */
-		this.stop = function() {
-			plot.paused = true;
-			plot.status = 0;
-			plot.gui.updateAnimationButtons(1);
-		}
-		// triggers the mousemove function to move the range and toolbar
-		var toolbarEvent = function(evt) {
-			var left = GeoTemConfig.getMousePosition(evt).left;
-			document.onmousemove = function(evt) {
-				moveToolbar(left, GeoTemConfig.getMousePosition(evt).left);
-				if (plot.popup) {
-					plot.popup.reset();
-				}
-			}
-		}
-		var initializeLeft = function() {
-			plot.mouseDownTime = plot.rightFlagTime;
-			plot.mouseTempTime = plot.leftFlagTime;
-			plot.mouseDownPos = plot.timeGeometry.toScreen(plot.mouseDownTime);
-			startX = plot.leftFlagPos;
-		}
-		var initializeRight = function() {
-			plot.mouseDownTime = plot.leftFlagTime;
-			plot.mouseTempTime = plot.rightFlagTime;
-			plot.mouseDownPos = plot.timeGeometry.toScreen(plot.mouseDownTime);
-			startX = plot.rightFlagPos;
-		}
-		var initializeDrag = function() {
-			startX = plot.leftFlagPos;
-			startY = plot.rightFlagPos;
-			setMultiplier();
-		}
-		var checkBorders = function() {
-			if (plot.style == 'bars' && plot.mouseUpTime == plot.mouseDownTime) {
-				var index = plot.tds.getSliceIndex(plot.mouseUpTime);
-				if (index == 0) {
-					plot.mouseUpTime = plot.tds.getSliceTime(index + 1);
-				} else if (index == plot.tds.getSliceNumber() - 1) {
-					plot.mouseUpTime = plot.tds.getSliceTime(index - 1);
-				} else {
-					if (plot.x < plot.leftFlagPos) {
-						plot.mouseUpTime = plot.tds.getSliceTime(index - 1);
-					} else {
-						plot.mouseUpTime = plot.tds.getSliceTime(index + 1);
-					}
-				}
-			}
-		}
-		// handles mousedown on left handle
-		this.leftHandle.onmousedown = function(evt) {
-			if (plot.status != 2) {
-
-				initializeLeft();
-				plot.status = 2;
-				toolbarEvent(evt);
-				document.onmouseup = function() {
-					document.onmousemove = null;
-					document.onmouseup = null;
-					plot.stop();
-				}
-			}
-		}
-		// handles mousedown on right handle
-		this.rightHandle.onmousedown = function(evt) {
-			if (plot.status != 2) {
-				initializeRight();
-				plot.status = 2;
-				toolbarEvent(evt);
-				document.onmouseup = function() {
-					document.onmousemove = null;
-					document.onmouseup = null;
-					plot.stop();
-				}
-			}
-		}
-		// handles mousedown on drag button
-		this.dragButton.onmousedown = function(evt) {
-			if (plot.status != 3) {
-				plot.status = 3;
-				initializeDrag();
-				toolbarEvent(evt);
-				document.onmouseup = function() {
-					document.onmousemove = null;
-					document.onmouseup = null;
-					plot.stop();
-				}
-			}
-		}
-		// handles mousedown-Event on timeplot
-		var mouseDownHandler = function(elmt, evt, target) {
-			if (plot.dataSources.length > 0) {
-
-				plot.x = Math.round(SimileAjax.DOM.getEventRelativeCoordinates(evt, plot.canvas).x);
-				if (plot.status == 0) {
-					var time = getCorrelatedTime(plot.x);
-					if (plot.leftFlagPos != null && plot.popup && time >= plot.leftFlagTime && time <= plot.rightFlagTime) {
-						var x = plot.leftFlagPos + (plot.rightFlagPos - plot.leftFlagPos) / 2;
-						var elements = [];
-						for (var i = 0; i < plot.dataSources.length; i++) {
-							elements.push([]);
-						}
-						for (var i = 0; i < plot.selectedObjects.length; i++) {
-							if (plot.selectedObjects[i].value == 1) {
-								for (var j = 0; j < plot.selectedObjects[i].objects.length; j++) {
-									elements[j] = elements[j].concat(plot.selectedObjects[i].objects[j]);
-								}
-							}
-						}
-						var labels = [];
-						for (var i = 0; i < elements.length; i++) {
-							if (elements[i].length == 0) {
-								continue;
-							}
-							var c = GeoTemConfig.getColor(i);
-							var color = 'rgb(' + c.r0 + ',' + c.g0 + ',' + c.b0 + ')';
-							var div = document.createElement("div");
-							div.setAttribute('class', 'tagCloudItem');
-							div.style.color = color;
-							var label = {
-								div : div,
-								elements : elements[i]
-							};
-							var weight = 0;
-							for (j in elements[i] ) {
-								weight += elements[i][j].weight;
-							}
-							var fs = 2 * weight / 1000;
-							if (fs > 2) {
-								fs = 2;
-							}
-							div.style.fontSize = (1 + fs) + "em";
-							div.style.textShadow = "0 0 0.4em black, 0 0 0.4em black, 0 0 0.4em black, 0 0 0.4em " + c.hex;
-							if (weight == 1) {
-								div.innerHTML = weight + " object";
-							} else {
-								div.innerHTML = weight + " objects";
-							}
-							var appendMouseFunctions = function(label, div, color) {
-								div.onclick = function() {
-									plot.popup.showLabelContent(label);
-									div.style.textShadow = "0 0 0.4em black, 0 0 0.4em black, 0 0 0.4em black, 0 0 0.4em " + color;
-								}
-								div.onmouseover = function() {
-									div.style.textShadow = "0 -1px " + color + ", 1px 0 " + color + ", 0 1px " + color + ", -1px 0 " + color;
-								}
-								div.onmouseout = function() {
-									div.style.textShadow = "0 0 0.4em black, 0 0 0.4em black, 0 0 0.4em black, 0 0 0.4em " + color;
-								}
-							}
-							appendMouseFunctions(label, div, c.hex);
-							labels.push(label);
-						}
-						if (labels.length > 0) {
-							plot.popup.createPopup(x + 20, 0, labels);
-						}
-					} else {
-						plot.deselection();
-						plot.status = 1;
-						plot.mouseDownTime = time;
-						plot.mouseTempTime = plot.mouseDownTime;
-						plot.mouseDownPos = plot.timeGeometry.toScreen(plot.mouseDownTime);
-						mapPositions(plot.mouseDownPos, plot.mouseDownPos, plot.mouseDownTime, plot.mouseDownTime);
-						// handles mouseup-Event on timeplot
-						document.onmouseup = function() {
-							if (plot.status == 1) {
-								plot.mouseUpTime = plot.mouseTempTime;
-								plot.mouseUpPos = plot.timeGeometry.toScreen(plot.mouseUpTime);
-								mapPositions(plot.mouseDownPos, plot.mouseUpPos, plot.mouseDownTime, plot.mouseUpTime);
-								checkPolesForStyle(plot.x);
-								setRangeDivs();
-								plot.timeSelection();
-								plot.gui.updateAnimationButtons(1);
-								document.onmouseup = null;
-								plot.status = 0;
-							}
-						}
-					}
-				}
-			}
-		}
-		// handles mousemove-Event on timeplot
-		var mouseMoveHandler = function(elmt, evt, target) {
-			if (plot.dataSources.length > 0) {
-				plot.x = Math.round(SimileAjax.DOM.getEventRelativeCoordinates(evt, plot.canvas).x);
-				if (plot.status == 1) {
-					plot.mouseTempTime = getCorrelatedTime(plot.x);
-					plot.mouseTempPos = plot.timeGeometry.toScreen(plot.mouseTempTime);
-					mapPositions(plot.mouseDownPos, plot.mouseTempPos, plot.mouseDownTime, plot.mouseTempTime);
-					checkPolesForStyle(plot.x);
-					setRangeDivs();
-				}
-			}
-		}
-		// handles mouseout-Event on timeplot
-		var mouseOutHandler = function(elmt, evt, target) {
-			if (plot.dataSources.length > 0) {
-				var x = Math.round(SimileAjax.DOM.getEventRelativeCoordinates(evt, plot.canvas).x);
-				var y = Math.round(SimileAjax.DOM.getEventRelativeCoordinates(evt, plot.canvas).y);
-				if (x > plot.canvas.width - 2 || isNaN(x) || x < 2) {
-					plot.timeHighlight(true);
-					plot.highlightedSlice = undefined;
-				} else if (y > plot.canvas.height - 2 || isNaN(y) || y < 2) {
-					plot.timeHighlight(true);
-					plot.highlightedSlice = undefined;
-				}
-			}
-		}
-		// handles mouse(h)over-Event on timeplot
-		var mouseHoverHandler = function(elmt, evt, target) {
-			if (plot.dataSources.length > 0) {
-				var x = Math.round(SimileAjax.DOM.getEventRelativeCoordinates(evt, plot.canvas).x);
-				var time = getCorrelatedTime(x);
-				if (time == undefined) {
-					return;
-				}
-				var highlightSlice;
-				var slices = plot.tds.timeSlices;
-				var index = plot.tds.getSliceIndex(time);
-				if (plot.style == 'graph') {
-					highlightSlice = slices[index];
-				}
-				if (plot.style == 'bars') {
-					var pos = plot.timeGeometry.toScreen(time);
-					if (x < pos && index > 0) {
-						highlightSlice = slices[index - 1];
-					} else {
-						highlightSlice = slices[index];
-					}
-				}
-				if (plot.highlightedSlice == undefined || plot.highlightedSlice != highlightSlice) {
-					plot.highlightedSlice = highlightSlice;
-					plot.timeHighlight(false);
-				}
-			}
-		}
-
-		this.redrawPlot = function() {
-			plot.clearTimeplot();
-			plot.tds.reset(this.timeGeometry);
-			plot.timeplot._prepareCanvas();
-			plot.timeplot.repaint();
-			if (plot.leftFlagPos != null) {
-				plot.leftFlagPos = getCorrelatedPosition(plot.leftFlagTime);
-				plot.rightFlagPos = getCorrelatedPosition(plot.rightFlagTime);
-				setRangeDivs();
-			} else {
-				plot.displayOverlay();
-			}
-			plot.initLabels([]);
-			plot.updateOverview();
-		}
-
-		this.resetOpacityPlots = function() {
-			var plots = plot.timeplot._plots;
-			for ( var i = 0; i < plots.length; i++) {
-				plots[i]._opacityCanvas.width = this.canvas.width;
-				plots[i]._opacityCanvas.height = this.canvas.height;
-				if( plot.leftFlagTime != null ){
-					plots[i].fullOpacityPlot(plot.leftFlagTime, plot.rightFlagTime, plot.leftFlagPos, plot.rightFlagPos, GeoTemConfig.getColor(i));
-				}
-			}
-		}
-
-		/**
-		 * handles zoom of the timeplot
-		 * @param {int} delta the change of zoom
-		 * @param {Date} time a time that corresponds to a slice, that was clicked
-		 */
-		/*
-		this.zoom = function(delta,time){
-		if( this.eventSources.length == 0 ){
-		if( GeoTemConfig.timeZoom ){
-		this.zoomSlider.setValue(0);
-		}
-		return false;
-		}
-		if( time == null ){
-		time = getCorrelatedTime(this.canvas.width/2);
-		}
-		if( this.tds.setZoom(delta,time,this.leftFlagTime,this.rightFlagTime) ){
-		this.redrawPlot();
-		}
-		if( GeoTemConfig.timeZoom ){
-		this.zoomSlider.setValue(this.tds.getZoom());
-		}
-		return true;
-		}
-		*/
-
-		// handles mousewheel event on the timeplot
-		var mouseWheelHandler = function(elmt, evt, target) {
-			if (evt.preventDefault) {
-				evt.preventDefault();
-			}
-			if (plot.dataSources.length == 0) {
-				return;
-			}
-			var delta = 0;
-			if (!evt)
-				evt = window.event;
-			if (evt.wheelDelta) {
-				delta = evt.wheelDelta / 120;
-				if (window.opera)
-					delta = -delta;
-			} else if (evt.detail) {
-				delta = -evt.detail / 3;
-			}
-			if (delta) {
-				var x = Math.round(SimileAjax.DOM.getEventRelativeCoordinates(evt, plot.canvas).x);
-				var time = getCorrelatedTime(x);
-				plot.zoom(delta, time);
-			}
-		}
-		var timeplotElement = this.timeplot.getElement();
-		SimileAjax.DOM.registerEvent(timeplotElement, "mousedown", mouseDownHandler);
-		SimileAjax.DOM.registerEvent(timeplotElement, "mousemove", mouseMoveHandler);
-		SimileAjax.DOM.registerEvent(timeplotElement, "mousemove", mouseHoverHandler);
-		SimileAjax.DOM.registerEvent(timeplotElement, "mouseout", mouseOutHandler);
-		if (GeoTemConfig.mouseWheelZoom) {
-			//SimileAjax.DOM.registerEvent(timeplotElement, "mousewheel", mouseWheelHandler);
-		}
-
-		this.gui.setHeight();
-
-	},
-
-	resetOverlay : function() {
-		this.poles.style.visibility = "hidden";
-		var plots = this.timeplot._plots;
-		for (var i = 0; i < plots.length; i++) {
-			for (var j = 0; j < plots[i].pins.length; j++) {
-				plots[i].pins[j] = {
-					height : 0,
-					count : 0
-				};
-			}
-		}
-	},
-
-	/**
-	 * resets the timeplot to non selection status
-	 */
-	reset : function() {
-
-		this.leftFlagPole.style.visibility = "hidden";
-		this.rightFlagPole.style.visibility = "hidden";
-		this.rangeBox.style.visibility = "hidden";
-		this.leftHandle.style.visibility = "hidden";
-		this.rightHandle.style.visibility = "hidden";
-		this.toolbar.style.visibility = "hidden";
-		this.toolbarAbsoluteDiv.style.visibility = "hidden";
-		this.cancelButton.style.visibility = "hidden";
-
-		var plots = this.timeplot._plots;
-		for (var i = 0; i < plots.length; i++) {
-			plots[i].opacityPlot.style.visibility = "hidden";
-		}
-		this.resetOverlay();
-		this.filterBar.reset(false);
-
-		var slices = this.tds.timeSlices;
-		if (slices != undefined) {
-			for (var i = 0; i < slices.length; i++) {
-				slices[i].reset();
-			}
-		}
-
-		this.status = 0;
-		this.stop();
-		this.gui.updateAnimationButtons(0);
-
-		this.leftFlagPos = null;
-		this.leftFlagTime = null;
-		this.rightFlagPos = null;
-		this.rightFlagTime = null;
-
-		this.mouseDownTime = null;
-		this.mouseUpTime = null;
-		this.mouseTempTime = null;
-
-		this.mouseDownPos = null;
-		this.mouseUpPos = null;
-		this.mouseTempPos = null;
-
-		if (this.popup) {
-			this.popup.reset();
-			this.popupClickDiv.style.visibility = "hidden";
-		}
-
-	},
-
-	/**
-	 * sets a pole on the timeplot
-	 * @param {Date} time the time of the specific timeslice
-	 * @param {int[]} the number of selected elements per dataset
-	 */
-	displayOverlay : function() {
-		this.poles.style.visibility = "visible";
-		var cv = this.poles.getElementsByTagName("canvas")[0];
-		cv.width = this.canvas.width;
-		cv.height = this.canvas.height;
-		if (!cv.getContext && G_vmlCanvasManager) {
-			cv = G_vmlCanvasManager.initElement(cv);
-		}
-		var ctx = cv.getContext('2d');
-		ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
-		var plots = this.timeplot._plots;
-		var slices = this.tds.timeSlices;
-		for (var i = 0; i < slices.length; i++) {
-			if (this.style == 'bars' && i + 1 == slices.length) {
-				return;
-			}
-			if (slices[i].overlay() == 0) {
-				continue;
-			}
-			var projStacks = slices[i].projStacks;
-			var time = slices[i].date;
-			var pos;
-			if (this.style == 'graph') {
-				pos = this.timeGeometry.toScreen(time);
-			} else if (this.style == 'bars') {
-				var x1 = this.timeGeometry.toScreen(time);
-				var x2 = this.timeGeometry.toScreen(slices[i + 1].date);
-				pos = (x1 + x2 ) / 2;
-			}
-			var heights = [];
-			var h = 0;
-			for (var j = 0; j < projStacks.length; j++) {
-				var data = plots[j]._dataSource.getData();
-				for (var k = 0; k < data.times.length; k++) {
-					if (data.times[k].getTime() == time.getTime()) {
-						var height = plots[j]._valueGeometry.toScreen(plots[j]._dataSource.getData().values[k]) * projStacks[j].overlay / projStacks[j].value;
-						heights.push(height);
-						plots[j].pins[i] = {
-							height : height,
-							count : projStacks[j].overlay
-						};
-						if (height > h) {
-							h = height;
-						}
-						break;
-					}
-				}
-			}
-			ctx.fillStyle = "rgb(102,102,102)";
-			ctx.beginPath();
-			ctx.rect(pos - 1, this.canvas.height - h, 2, h);
-			ctx.fill();
-			for (var j = 0; j < heights.length; j++) {
-				if (heights[j] > 0) {
-					var color = GeoTemConfig.getColor(j);
-					ctx.fillStyle = "rgba(" + color.r1 + "," + color.g1 + "," + color.b1 + ",0.6)";
-					ctx.beginPath();
-					ctx.arc(pos, this.canvas.height - heights[j], 2.5, 0, Math.PI * 2, true);
-					ctx.closePath();
-					ctx.fill();
-				}
-			}
-		}
-	},
-
-	/**
-	 * updates the timeplot by displaying place poles, after a selection had been executed in another widget
-	 */
-	highlightChanged : function(timeObjects) {
-		if( !GeoTemConfig.highlightEvents ){
-			return;
-		}
-		this.resetOverlay();
-		if (this.selection.valid()) {
-			if (!this.selection.equal(this)) {
-				this.tds.setOverlay(GeoTemConfig.mergeObjects(timeObjects, this.selection.getObjects(this)));
-			} else {
-				this.tds.setOverlay(timeObjects);
-			}
-		} else {
-			this.tds.setOverlay(timeObjects);
-		}
-		this.displayOverlay();
-	},
-
-	/**
-	 * updates the timeplot by displaying place poles, after a selection had been executed in another widget
-	 */
-	selectionChanged : function(selection) {
-		if( !GeoTemConfig.selectionEvents ){
-			return;
-		}
-		this.reset();
-		this.selection = selection;
-		this.tds.setOverlay(selection.objects);
-		this.displayOverlay();
-	},
-
-	/**
-	 * returns the approximate left position of a slice inside the overview representation
-	 * @param {Date} time time of the slice
-	 */
-	getOverviewLeft : function(time) {
-		var w = this.overview.offsetWidth;
-		var s = this.tds.earliest().getTime();
-		var e = this.tds.latest().getTime();
-		var t = time.getTime();
-		return Math.round(w * (t - s) / (e - s));
-	},
-
-	/**
-	 * visualizes the overview div (shows viewable part of zoomed timeplot)
-	 */
-	initOverview : function() {
-		var labels = this.timeGeometry._grid;
-		if (labels.length == 0) {
-			var plot = this;
-			setTimeout(function() {
-				plot.initOverview();
-			}, 10);
-			return;
-		}
-
-		this.overview.style.width = this.canvas.width + "px";
-		var left = this.gui.timeplotDiv.offsetLeft;
-		this.overview.innerHTML = "";
-		this.overview.style.left = left + "px";
-
-		this.overviewRange = document.createElement("div");
-		this.overviewRange.setAttribute('class', 'overviewRange');
-		this.overview.appendChild(this.overviewRange);
-
-		for (var i = 0; i < labels.length; i++) {
-			var label = document.createElement("div");
-			label.setAttribute('class', 'overviewLabel');
-			label.innerHTML = labels[i].label;
-			label.style.left = Math.floor(labels[i].x) + "px";
-			this.overview.appendChild(label);
-		}
-
-		this.updateOverview();
-	},
-
-	/**
-	 * visualizes the labels of the timeplot
-	 */
-	initLabels : function(labels) {
-		if (labels.length == 0) {
-			labels = this.timeGeometry._grid;
-			if (labels.length == 0) {
-				var plot = this;
-				setTimeout(function() {
-					plot.initLabels([]);
-				}, 10);
-				return;
-			}
-		}
-		this.plotLabels.style.width = this.canvas.width + "px";
-		var left = this.gui.timeplotDiv.offsetLeft;
-		this.plotLabels.style.left = left + "px";
-		this.plotLabels.innerHTML = "";
-		for (var i = 0; i < labels.length; i++) {
-			var label = document.createElement("div");
-			label.setAttribute('class', 'plotLabel');
-			label.innerHTML = labels[i].label;
-			label.style.left = Math.floor(labels[i].x) + "px";
-			this.plotLabels.appendChild(label);
-		}
-	},
-
-	/**
-	 * updates the overview div
-	 */
-	updateOverview : function() {
-		if (this.tds.getZoom() > 0) {
-			this.plotLabels.style.visibility = "hidden";
-			this.timeGeometry._hideLabels = false;
-			this.overview.style.visibility = "visible";
-			this.shiftLeft.style.visibility = "visible";
-			this.shiftRight.style.visibility = "visible";
-			var left = this.getOverviewLeft(this.tds.timeSlices[this.tds.leftSlice].date);
-			var right = this.getOverviewLeft(this.tds.timeSlices[this.tds.rightSlice].date);
-			this.overviewRange.style.left = left + "px";
-			this.overviewRange.style.width = (right - left) + "px";
-		} else {
-			this.timeGeometry._hideLabels = true;
-			this.plotLabels.style.visibility = "visible";
-			this.overview.style.visibility = "hidden";
-			this.shiftLeft.style.visibility = "hidden";
-			this.shiftRight.style.visibility = "hidden";
-		}
-	},
-
-	/**
-	 * returns the time slices which are created by the extended data source
-	 */
-	getSlices : function() {
-		return this.tds.timeSlices;
-	},
-
-	timeSelection : function() {
-		var slices = this.tds.timeSlices;
-		var ls, rs;
-		for (var i = 0; i < slices.length; i++) {
-			if (slices[i].date.getTime() == this.leftFlagTime.getTime())
-				ls = i;
-			if (slices[i].date.getTime() == this.rightFlagTime.getTime()) {
-				if (this.style == 'graph') {
-					rs = i;
-				}
-				if (this.style == 'bars') {
-					rs = i - 1;
-				}
-			}
-		}
-		var selectedObjects = [];
-		for (var i = 0; i < GeoTemConfig.datasets.length; i++) {
-			selectedObjects.push([]);
-		}
-		for (var i = 0; i < slices.length; i++) {
-			if (i >= ls && i <= rs) {
-				for (var j in slices[i].stacks ) {
-					selectedObjects[j] = selectedObjects[j].concat(slices[i].stacks[j].elements);
-				}
-			}
-		}
-		this.selection = new Selection(selectedObjects, this);
-		this.core.triggerSelection(this.selection);
-		this.filterBar.reset(true);
-	},
-
-	deselection : function() {
-		this.reset();
-		this.selection = new Selection();
-		this.core.triggerSelection(this.selection);
-	},
-
-	filtering : function() {
-		for (var i = 0; i < this.datasets.length; i++) {
-			this.datasets[i].objects = this.selection.objects[i];
-		}
-		this.core.triggerRefining(this.datasets);
-	},
-
-	inverseFiltering : function() {
-		var slices = this.tds.timeSlices;
-		var ls, rs;
-		for (var i = 0; i < slices.length; i++) {
-			if (slices[i].date.getTime() == this.leftFlagTime.getTime())
-				ls = i;
-			if (slices[i].date.getTime() == this.rightFlagTime.getTime()) {
-				if (this.style == 'graph') {
-					rs = i;
-				}
-				if (this.style == 'bars') {
-					rs = i - 1;
-				}
-			}
-		}
-		var selectedObjects = [];
-		for (var i = 0; i < GeoTemConfig.datasets.length; i++) {
-			selectedObjects.push([]);
-		}
-		for (var i = 0; i < slices.length; i++) {
-			if (i >= ls && i <= rs) {
-				continue;
-			}
-			for (var j in slices[i].stacks ) {
-				selectedObjects[j] = selectedObjects[j].concat(slices[i].stacks[j].elements);
-			}
-		}
-		this.selection = new Selection(selectedObjects, this);
-		this.filtering();
-	},
-
-	timeHighlight : function(undo) {
-		if (this.status == 0) {
-			var s = this.highlightedSlice;
-			var timeObjects = [];
-			for (var i = 0; i < this.tds.size(); i++) {
-				timeObjects.push([]);
-			}
-			var add = true;
-			if (this.leftFlagTime != null) {
-				if (this.style == 'graph' && s.date >= this.leftFlagTime && s.date <= this.rightFlagTime) {
-					add = false;
-				}
-				if (this.style == 'bars' && s.date >= this.leftFlagTime && s.date < this.rightFlagTime) {
-					add = false;
-				}
-			}
-			if (!undo && add) {
-				for (var i in s.stacks ) {
-					timeObjects[i] = timeObjects[i].concat(s.stacks[i].elements);
-				}
-			}
-			this.core.triggerHighlight(timeObjects);
-		}
-	},
-
-	timeRefining : function() {
-		this.core.triggerRefining(this.selection.objects);
-	},
-
-	setStyle : function(style) {
-		this.style = style;
-	},
-
-	drawLinearPlot : function() {
-		if ( typeof this.valueGeometry != 'undefined') {			
-			this.valueGeometry.actLinear();
-			this.timeplot.repaint();
-			this.resetOpacityPlots();
-			this.displayOverlay();
-		}
-	},
-
-	drawLogarithmicPlot : function() {
-		if ( typeof this.valueGeometry != 'undefined') {
-			this.valueGeometry.actLogarithmic();
-			this.timeplot.repaint();
-			this.resetOpacityPlots();
-			this.displayOverlay();
-		}
-	}
-}
-/*
-* TableConfig.js
-*
-* Copyright (c) 2012, Stefan Jänicke. All rights reserved.
-*
-* This library is free software; you can redistribute it and/or
-* modify it under the terms of the GNU Lesser General Public
-* License as published by the Free Software Foundation; either
-* version 3 of the License, or (at your option) any later version.
-*
-* This library is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-* Lesser General Public License for more details.
-*
-* You should have received a copy of the GNU Lesser General Public
-* License along with this library; if not, write to the Free Software
-* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
-* MA 02110-1301  USA
-*/
-
-/**
- * @class TableConfig
- * Table Configuration File
- * @author Stefan Jänicke (stjaenicke@informatik.uni-leipzig.de)
- * @release 1.0
- * @release date: 2012-07-27
- * @version date: 2012-07-27
- */
-function TableConfig(options) {
-
-	this.options = {
-		tableWidth : false, // false or desired width css definition for the table
-		tableHeight : false, // false or desired height css definition for the table
-		validResultsPerPage : [10, 20, 50, 100], // valid number of elements per page
-		initialResultsPerPage : 10, // initial number of elements per page
-		tableSorting : true, // true, if sorting of columns should be possible
-		tableContentOffset : 250, // maximum display number of characters in a table cell
-		tableSelectPage : true, // selection of complete table pages
-		tableSelectAll : false, // selection of complete tables
-		tableShowSelected : true, // show selected objects only option
-		tableKeepShowSelected : true, // don't revert to show all on "reset" (e.g. selection)
-		tableInvertSelection : true, // show invert selection option
-		tableSelectByText : true, // select objects by full-text search
-		tableCreateNewFromSelected : true, // create new dataset from selected objects
-		unselectedCellColor : '#EEE', // color for an unselected row/tab
-		verticalAlign : 'top', // vertical alignment of the table cells ('top','center','bottom')
-	};
-	if ( typeof options != 'undefined') {
-		$.extend(this.options, options);
-	}
-
-};
-/*
-* TableGui.js
-*
-* Copyright (c) 2012, Stefan Jänicke. All rights reserved.
-*
-* This library is free software; you can redistribute it and/or
-* modify it under the terms of the GNU Lesser General Public
-* License as published by the Free Software Foundation; either
-* version 3 of the License, or (at your option) any later version.
-*
-* This library is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-* Lesser General Public License for more details.
-*
-* You should have received a copy of the GNU Lesser General Public
-* License along with this library; if not, write to the Free Software
-* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
-* MA 02110-1301  USA
-*/
-
-/**
- * @class TableGui
- * Table GUI Implementation
- * @author Stefan Jänicke (stjaenicke@informatik.uni-leipzig.de)
- * @release 1.0
- * @release date: 2012-07-27
- * @version date: 2012-07-27
- *
- * @param {TableWidget} parent table widget object
- * @param {HTML object} div parent div to append the table gui
- * @param {JSON} options table configuration
- */
-function TableGui(table, div, options) {
-
-	this.tableContainer = div;
-	if (options.tableWidth) {
-		this.tableContainer.style.width = options.tableWidth;
-	}
-	if (options.tableHeight) {
-		this.tableContainer.style.height = options.tableHeight;
-	}
-	this.tableContainer.style.position = 'relative';
-
-	this.tabs = document.createElement('div');
-	this.tabs.setAttribute('class', 'tableTabs');
-	div.appendChild(this.tabs);
-
-	this.input = document.createElement('div');
-	this.input.setAttribute('class', 'tableInput');
-	div.appendChild(this.input);
-
-};
+/*
+* TimeConfig.js
+*
+* Copyright (c) 2012, Stefan Jänicke. All rights reserved.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 3 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+* MA 02110-1301  USA
+*/
+
+/**
+ * @class TimeConfig
+ * Time Configuration File
+ * @author Stefan Jänicke (stjaenicke@informatik.uni-leipzig.de)
+ * @release 1.0
+ * @release date: 2012-07-27
+ * @version date: 2012-07-27
+ */
+function TimeConfig(options) {
+
+	this.options = {
+		timeTitle : 'GeoTemCo Time View', // title will be shown in timeplot header
+		timeIndex : 0, // index = position in date array; for multiple dates the 2nd timeplot refers to index 1
+		timeWidth : false, // false or desired width css definition for the timeplot
+		timeHeight : '100px', // false or desired height css definition for the timeplot
+		defaultMinDate : new Date(2012, 0, 1), // required, when empty timelines are possible
+		defaultMaxDate : new Date(), // required, when empty timelines are possible
+		timeCanvasFrom : '#EEE', // time widget background gradient color top
+		timeCanvasTo : '#EEE', // time widget background gradient color bottom
+		rangeBoxColor : "white", // fill color for time range box
+		rangeBorder : "1px solid #de7708", // border of frames
+		dataInformation : true, // show/hide data information
+		rangeAnimation : true, // show/hide animation buttons
+		scaleSelection : true, // show/hide scale selection buttons
+		linearScale : true, // true for linear value scaling, false for logarithmic
+		unitSelection : true, // show/hide time unit selection dropdown
+		timeUnit : -1, // minimum temporal unit (SimileAjax.DateTime or -1 if none) of the data
+		timeMerge : false // if the elements of distinct datasets should be merged into one set or not
+	};
+	if ( typeof options != 'undefined') {
+		$.extend(this.options, options);
+	}
+
+};
+/*
+* TimeGui.js
+*
+* Copyright (c) 2012, Stefan Jänicke. All rights reserved.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 3 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+* MA 02110-1301  USA
+*/
+
+/**
+ * @class TimeGui
+ * Time GUI Implementation
+ * @author Stefan Jänicke (stjaenicke@informatik.uni-leipzig.de)
+ * @release 1.0
+ * @release date: 2012-07-27
+ * @version date: 2012-07-27
+ *
+ * @param {TimeWidget} parent time widget object
+ * @param {HTML object} div parent div to append the time gui
+ * @param {JSON} options time configuration
+ */
+function TimeGui(plot, div, options, iid) {
+
+	var gui = this;
+
+	this.plot = plot;
+
+	this.container = div;
+	if (options.timeWidth) {
+		this.container.style.width = options.timeWidth;
+	}
+	if (options.timeHeight) {
+		this.container.style.height = options.timeHeight;
+	}
+	this.container.style.position = 'relative';
+
+	var w = this.container.offsetWidth;
+	var h = this.container.offsetHeight;
+
+	var toolbarTable = document.createElement("table");
+	toolbarTable.setAttribute('class', 'ddbToolbar');
+	this.container.appendChild(toolbarTable);
+
+	this.plotWindow = document.createElement("div");
+	this.plotWindow.id = "plotWindow"+iid;
+	this.plotWindow.setAttribute('class', 'plotWindow');
+//	this.plotWindow.style.width = w + "px";
+
+	this.plotWindow.style.height = (h + 12) + "px";
+	this.container.style.height = (h + 12) + "px";
+
+	this.plotWindow.onmousedown = function() {
+		return false;
+	}
+
+	this.plotContainer = document.createElement("div");
+	this.plotContainer.id = "plotContainer"+iid;
+	this.plotContainer.setAttribute('class', 'plotContainer');
+//	this.plotContainer.style.width = w + "px";
+	this.plotContainer.style.height = h + "px";
+	this.plotContainer.style.position = "absolute";
+	this.plotContainer.style.zIndex = 0;
+	this.plotContainer.style.top = "12px";
+	this.plotWindow.appendChild(this.plotContainer);
+	this.container.appendChild(this.plotWindow);
+
+	this.timeplotDiv = document.createElement("div");
+	this.timeplotDiv.style.left = "16px";
+	this.timeplotDiv.style.width = (w - 32) + "px";
+	this.timeplotDiv.style.height = h + "px";
+	this.plotContainer.appendChild(this.timeplotDiv);
+
+	var cv = document.createElement("canvas");
+	cv.setAttribute('class', 'plotCanvas');
+	this.plotWindow.appendChild(cv);
+	if (!cv.getContext && G_vmlCanvasManager)
+		cv = G_vmlCanvasManager.initElement(cv);
+	var ctx = cv.getContext('2d');
+
+	var setCanvas = function(){
+		cv.width = gui.plotWindow.clientWidth;
+		cv.height = gui.plotWindow.clientHeight;
+		var gradient = ctx.createLinearGradient(0, 0, 0, gui.plotWindow.clientHeight);
+		gradient.addColorStop(0, options.timeCanvasFrom);
+		gradient.addColorStop(1, options.timeCanvasTo);
+		ctx.fillStyle = gradient;
+		ctx.fillRect(0, 0, gui.plotWindow.clientWidth, gui.plotWindow.clientHeight);
+	}
+	setCanvas();
+
+	this.resize = function(){
+		gui.timeplotDiv.style.width = (gui.container.offsetWidth - 32) + "px";
+		ctx.clearRect(0,0,gui.plotWindow.clientWidth, gui.plotWindow.clientHeight);
+		if( typeof plot.datasets != "undefined" ){
+			plot.redrawPlot();
+			plot.resetOpacityPlots();
+		}
+		setCanvas();
+	};
+
+	var titles = document.createElement("tr");
+	toolbarTable.appendChild(titles);
+	var tools = document.createElement("tr");
+	toolbarTable.appendChild(tools);
+
+	this.timeUnitTitle = document.createElement("td");
+	this.timeUnitTitle.innerHTML = GeoTemConfig.getString('timeUnit');
+	this.timeUnitSelector = document.createElement("td");
+	if (options.unitSelection) {
+		tools.appendChild(this.timeUnitSelector);
+		titles.appendChild(this.timeUnitTitle);
+	}
+
+	this.timeAnimation = document.createElement("td");
+	this.timeAnimation.innerHTML = GeoTemConfig.getString('timeAnimation');
+	var timeAnimationTools = document.createElement("td");
+
+	var status;
+	this.updateAnimationButtons = function(s) {
+		status = s;
+		if (status == 0) {
+			gui.playButton.setAttribute('class', 'smallButton playDisabled');
+			gui.pauseButton.setAttribute('class', 'smallButton pauseDisabled');
+		} else if (status == 1) {
+			gui.playButton.setAttribute('class', 'smallButton playEnabled');
+			gui.pauseButton.setAttribute('class', 'smallButton pauseDisabled');
+		} else {
+			gui.playButton.setAttribute('class', 'smallButton playDisabled');
+			gui.pauseButton.setAttribute('class', 'smallButton pauseEnabled');
+		}
+	};
+	this.playButton = document.createElement("div");
+	this.playButton.title = GeoTemConfig.getString('playButton');
+	timeAnimationTools.appendChild(this.playButton);
+	this.playButton.onclick = function() {
+		if (status == 1) {
+			plot.play();
+		}
+	}
+
+	this.pauseButton = document.createElement("div");
+	this.pauseButton.title = GeoTemConfig.getString('pauseButton');
+	timeAnimationTools.appendChild(this.pauseButton);
+	this.pauseButton.onclick = function() {
+		if (status == 2) {
+			plot.stop();
+		}
+	}
+
+	this.valueScale = document.createElement("td");
+	this.valueScale.innerHTML = GeoTemConfig.getString('valueScale');
+	var valueScaleTools = document.createElement("td");
+
+	var linearPlot;
+	var setValueScale = function(linScale) {
+		if (linearPlot != linScale) {
+			linearPlot = linScale;
+			if (linearPlot) {
+				gui.linButton.setAttribute('class', 'smallButton linearPlotActivated');
+				gui.logButton.setAttribute('class', 'smallButton logarithmicPlotDeactivated');
+				plot.drawLinearPlot();
+			} else {
+				gui.linButton.setAttribute('class', 'smallButton linearPlotDeactivated');
+				gui.logButton.setAttribute('class', 'smallButton logarithmicPlotActivated');
+				plot.drawLogarithmicPlot();
+			}
+		}
+	};
+	this.linButton = document.createElement("div");
+	this.linButton.title = GeoTemConfig.getString('linearPlot');
+	valueScaleTools.appendChild(this.linButton);
+	this.linButton.onclick = function() {
+		setValueScale(true);
+	}
+
+	this.logButton = document.createElement("div");
+	this.logButton.title = GeoTemConfig.getString('logarithmicPlot');
+	valueScaleTools.appendChild(this.logButton);
+	this.logButton.onclick = function() {
+		setValueScale(false);
+	}
+	if (options.rangeAnimation) {
+		titles.appendChild(this.timeAnimation);
+		tools.appendChild(timeAnimationTools);
+		this.updateAnimationButtons(0);
+	}
+
+	if (options.scaleSelection) {
+		titles.appendChild(this.valueScale);
+		tools.appendChild(valueScaleTools);
+		setValueScale(options.linearScale);
+	}
+
+	if (GeoTemConfig.allowFilter) {
+		this.filterTitle = document.createElement("td");
+		titles.appendChild(this.filterTitle);
+		this.filterTitle.innerHTML = GeoTemConfig.getString('filter');
+		this.filterOptions = document.createElement("td");
+		tools.appendChild(this.filterOptions);
+	}
+
+	if (options.dataInformation) {
+		this.infoTitle = document.createElement("td");
+		this.infoTitle.innerHTML = options.timeTitle;
+		titles.appendChild(this.infoTitle);
+		var timeSum = document.createElement("td");
+		this.timeElements = document.createElement("div");
+		this.timeElements.setAttribute('class', 'ddbElementsCount');
+		timeSum.appendChild(this.timeElements);
+		tools.appendChild(timeSum);
+	}
+
+	/*
+	 var tooltip = document.createElement("div");
+	 tooltip.setAttribute('class','ddbTooltip');
+	 toolbarTable.appendChild(tooltip);
+
+	 tooltip.onmouseover = function(){
+	 /*
+	 getPublisher().Publish('TooltipContent', {
+	 content: GeoTemConfig.getString(GeoTemConfig.language,'timeHelp'),
+	 target: $(tooltip)
+	 });
+
+	 }
+	 tooltip.onmouseout = function(){
+	 //getPublisher().Publish('TooltipContent');
+	 }
+	 */
+
+	this.setHeight = function() {
+		this.container.style.height = (this.plotWindow.offsetHeight + toolbarTable.offsetHeight) + "px";
+	};
+
+	this.updateTimeQuantity = function(count) {
+		if (options.dataInformation) {
+			this.plotCount = count;
+			if (count != 1) {
+				this.timeElements.innerHTML = this.beautifyCount(count) + " " + GeoTemConfig.getString('results');
+			} else {
+				this.timeElements.innerHTML = this.beautifyCount(count) + " " + GeoTemConfig.getString('result');
+			}
+		}
+	}
+
+	this.setTimeUnitDropdown = function(units) {
+		$(this.timeUnitSelector).empty();
+		var gui = this;
+		var timeUnits = [];
+		var addUnit = function(unit, index) {
+			var setUnit = function() {
+				gui.plot.setTimeUnit(unit.unit);
+			}
+			timeUnits.push({
+				name : unit.label,
+				onclick : setUnit
+			});
+		}
+		for (var i = 0; i < units.length; i++) {
+			addUnit(units[i], i);
+		}
+		this.timeUnitDropdown = new Dropdown(this.timeUnitSelector, timeUnits, GeoTemConfig.getString('selectTimeUnit'), '100px');
+		this.timeUnitDropdown.setEntry(0);
+	}
+	this.setTimeUnitDropdown([{
+		name : 'none',
+		id : -1
+	}]);
+
+	this.beautifyCount = function(count) {
+		var c = count + '';
+		var p = 0;
+		var l = c.length;
+		while (l - p > 3) {
+			p += 3;
+			c = c.substring(0, l - p) + "." + c.substring(l - p);
+			p++;
+			l++;
+		}
+		return c;
+	}
+
+	this.hideTimeUnitSelection = function() {
+		this.timeUnitTitle.style.display = 'none';
+		this.timeUnitSelector.style.display = 'none';
+	}
+};
+/*
+* TimeWidget.js
+*
+* Copyright (c) 2012, Stefan Jänicke. All rights reserved.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 3 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+* MA 02110-1301  USA
+*/
+
+/**
+ * @class TimeWidget
+ * TableWidget Implementation
+ * @author Stefan Jänicke (stjaenicke@informatik.uni-leipzig.de)
+ * @release 1.0
+ * @release date: 2012-07-27
+ * @version date: 2012-07-27
+ *
+ * @param {TimeWrapper} core wrapper for interaction to other widgets
+ * @param {HTML object} div parent div to append the time widget div
+ * @param {JSON} options user specified configuration that overwrites options in TimeConfig.js
+ */
+TimeWidget = function(core, div, options) {
+
+	this.core = core;
+	this.core.setWidget(this);
+	this.timeplot
+	this.dataSources
+	this.eventSources
+	this.tds
+	this.timeGeometry
+	this.valueGeometry
+	this.canvas
+
+	this.leftFlagPole
+	this.rightFlagPole
+	this.rangeBox
+	this.leftHandle
+	this.rightHandle
+
+	this.leftFlagPos = null;
+	this.leftFlagTime = null;
+	this.rightFlagPos = null;
+	this.rightFlagTime = null;
+
+	this.mouseDownTime
+	this.mouseUpTime
+	this.mouseTempTime
+	this.mouseDownPos
+	this.mouseUpPos
+	this.mouseTempPos
+
+	this.status
+	this.slider
+
+	this.iid = GeoTemConfig.getIndependentId('time');
+	this.options = (new TimeConfig(options)).options;
+	this.gui = new TimeGui(this, div, this.options, this.iid);
+	this.initialize();
+
+}
+
+TimeWidget.prototype = {
+
+	/**
+	 * clears the timeplot canvas and the timeGeometry properties
+	 */
+	clearTimeplot : function() {
+		this.timeplot._clearCanvas();
+		this.timeGeometry._earliestDate = null;
+		this.timeGeometry._latestDate = null;
+		this.valueGeometry._minValue = 0;
+		this.valueGeometry._maxValue = 0;
+		this.highlightedSlice = undefined;
+		this.timeGeometry._clearLabels();
+		this.selection = new Selection();
+	},
+
+	/**
+	 * initializes the timeplot elements with arrays of time objects
+	 * @param {TimeObject[][]} timeObjects an array of time objects from different (1-4) sets
+	 */
+	initWidget : function(datasets) {
+		this.datasets = datasets;
+		var timeObjects = [];
+		for (var i = 0; i < datasets.length; i++) {
+			timeObjects.push(datasets[i].objects);
+		}
+		this.clearTimeplot();
+		this.reset();
+		for (var i = 0; i < this.timeplot._plots.length; i++) {
+			this.timeplot._plots[i].dispose();
+		}
+		this.dataSources = new Array();
+		this.plotInfos = new Array();
+		this.eventSources = new Array();
+		var granularity = 0;
+		this.count = 0;
+		for (var i = 0; i < timeObjects.length; i++) {
+			if( i==0 || !this.options.timeMerge ){
+				var eventSource = new Timeplot.DefaultEventSource();
+				var dataSource = new Timeplot.ColumnSource(eventSource, 1);
+				this.dataSources.push(dataSource);
+				this.eventSources.push(eventSource);
+				var c = GeoTemConfig.getColor(i);
+				var plotInfo = Timeplot.createPlotInfo({
+					id : "plot" + i,
+					dataSource : dataSource,
+					timeGeometry : this.timeGeometry,
+					valueGeometry : this.valueGeometry,
+					fillGradient : false,
+					lineColor : 'rgba(' + c.r1 + ',' + c.g1 + ',' + c.b1 + ', 1)',
+					fillColor : 'rgba(' + c.r0 + ',' + c.g0 + ',' + c.b0 + ', 0.3)',
+					showValues : true
+				});
+				this.plotInfos.push(plotInfo);
+			}
+			for (var j = 0; j < timeObjects[i].length; j++) {
+				var o = timeObjects[i][j];
+				if (o.isTemporal) {
+					var g = o.dates[this.options.timeIndex].granularity;
+					if (g == null) {
+						continue;
+					} else if (g > granularity) {
+						granularity = g;
+					}
+					this.count += o.weight;
+				}
+			}
+		}
+		this.timeGeometry._granularity = granularity;
+		this.timeGeometry._clearLabels();
+		this.timeplot.resetPlots(this.plotInfos);
+		if (this.plotInfos.length == 0) {
+			this.initLabels(this.timeplot.regularGrid());
+			return;
+		}
+		this.timeGeometry.extendedDataSource = this.tds;
+		this.tds.initialize(this.dataSources, this.eventSources, timeObjects, granularity, this.options.timeUnit, this.gui.timeplotDiv.offsetWidth);
+		this.gui.setTimeUnitDropdown(this.tds.availableUnits);
+		this.gui.timeUnitDropdown.setEntry(this.tds.getUnitIndex());
+		var plots = this.timeplot._plots;
+		for (var i = 0; i < plots.length; i++) {
+			plots[i].pins = [];
+			plots[i].style = this.style;
+			for (var j = 0; j < this.tds.getSliceNumber(); j++) {
+				plots[i].pins.push({
+					height : 0,
+					count : 0
+				});
+			}
+		}
+		/*
+		 var levels = Math.round( (this.tds.timeSlices.length-3)/2 );
+		 if( GeoTemConfig.timeZoom ){
+		 this.zoomSlider.setMaxAndLevels(levels,levels);
+		 }
+		 */
+		this.timeplot.repaint();
+		this.timeplot._resizeCanvas();
+		// set maximum number of slider steps
+		var slices = this.tds.timeSlices.length;
+		var numSlices = Math.floor(slices / this.canvas.width * this.canvas.height + 0.5);
+
+		this.initLabels([]);
+		this.initOverview();
+		this.gui.updateTimeQuantity(this.count);
+
+	},
+
+	setTimeUnit : function(unit) {
+		this.clearTimeplot();
+		this.reset();
+		this.tds.setTimeUnit(unit);
+		var plots = this.timeplot._plots;
+		for (var i = 0; i < plots.length; i++) {
+			plots[i].pins = [];
+			plots[i].style = this.style;
+			for (var j = 0; j < this.tds.getSliceNumber(); j++) {
+				plots[i].pins.push({
+					height : 0,
+					count : 0
+				});
+			}
+		}
+		this.initLabels([]);
+	},
+
+	/**
+	 * initializes the timeplot for the Spatio Temporal Interface.
+	 * all elements (including their events) that are needed for user interaction are instantiated here, the slider element as well
+	 */
+	initialize : function() {
+
+		this.status = 0;
+		this.selection = new Selection();
+		this.paused = true;
+		this.dataSources = new Array();
+		this.plotInfos = new Array();
+		this.eventSources = new Array();
+		this.timeGeometry = new Timeplot.DefaultTimeGeometry({
+			gridColor : "#000000",
+			axisLabelsPlacement : "top"
+		});
+		this.style = 'graph';
+		this.timeGeometry._hideLabels = true;
+		this.timeGeometry._granularity = 0;
+		this.valueGeometry = new Timeplot.LogarithmicValueGeometry({
+			min : 0
+		});
+		this.valueGeometry.actLinear();
+
+		var plot = this;
+
+		this.timeplot = Timeplot.create(this.gui.timeplotDiv, this.plotInfos);
+		this.tds = new TimeDataSource(this.options);
+
+		this.canvas = this.timeplot.getCanvas();
+
+		this.leftFlagPole = this.timeplot.putDiv("leftflagpole", "timeplot-dayflag-pole");
+		this.rightFlagPole = this.timeplot.putDiv("rightflagpole", "timeplot-dayflag-pole");
+		SimileAjax.Graphics.setOpacity(this.leftFlagPole, 50);
+		SimileAjax.Graphics.setOpacity(this.rightFlagPole, 50);
+
+		this.rangeBox = this.timeplot.putDiv("rangebox", "range-box");
+		this.rangeBox.style.backgroundColor = plot.options.rangeBoxColor;
+		this.rangeBox.style.border = plot.options.rangeBorder;
+
+		this.leftHandle = document.createElement("div");
+		this.rightHandle = document.createElement("div");
+		this.gui.plotWindow.appendChild(this.leftHandle);
+		this.gui.plotWindow.appendChild(this.rightHandle);
+		this.leftHandle.title = GeoTemConfig.getString('leftHandle');
+		this.rightHandle.title = GeoTemConfig.getString('rightHandle');
+
+		this.leftHandle.style.backgroundImage = "url(" + GeoTemConfig.path + "leftHandle.png" + ")";
+		this.leftHandle.setAttribute('class', 'plotHandle plotHandleIcon');
+		this.rightHandle.style.backgroundImage = "url(" + GeoTemConfig.path + "rightHandle.png" + ")";
+		this.rightHandle.setAttribute('class', 'plotHandle plotHandleIcon');
+
+		this.poles = this.timeplot.putDiv("poles", "pole");
+		this.timeplot.placeDiv(this.poles, {
+			left : 0,
+			bottom : 0,
+			width : this.canvas.width,
+			height : this.canvas.height,
+			display : "block"
+		});
+		this.poles.appendChild(document.createElement("canvas"));
+
+		this.filterBar = new FilterBar(this, this.gui.filterOptions);
+
+		var plot = this;
+
+		this.dragButton = document.createElement("div");
+		this.dragButton.title = GeoTemConfig.getString('dragTimeRange');
+		this.cancelButton = document.createElement("div");
+		this.cancelButton.title = GeoTemConfig.getString('clearSelection');
+		this.cancelButton.onclick = function() {
+			plot.deselection();
+		}
+
+		this.toolbar = document.createElement("div");
+		this.toolbar.setAttribute('class', 'plotToolbar');
+		this.toolbar.style.borderTop = plot.options.rangeBorder;
+		this.toolbar.style.textAlign = "center";
+		this.gui.plotWindow.appendChild(this.toolbar);
+
+		this.toolbarAbsoluteDiv = document.createElement("div");
+		this.toolbarAbsoluteDiv.setAttribute('class', 'absoluteToolbar');
+		this.toolbar.appendChild(this.toolbarAbsoluteDiv);
+
+		this.dragButton.setAttribute('class', 'dragTimeRangeAlt');
+		this.dragButton.style.backgroundImage = "url(" + GeoTemConfig.path + "drag.png" + ")";
+		//        	this.zoomButton.setAttribute('class','zoomRangeAlt');
+		this.cancelButton.setAttribute('class', 'cancelRangeAlt');
+		this.toolbarAbsoluteDiv.appendChild(this.dragButton);
+		this.toolbarAbsoluteDiv.style.width = this.dragButton.offsetWidth + "px";
+		//	        this.gui.plotWindow.appendChild(this.zoomButton);
+		this.gui.plotWindow.appendChild(this.cancelButton);
+
+		this.overview = document.createElement("div");
+		this.overview.setAttribute('class', 'timeOverview');
+		this.gui.plotWindow.appendChild(this.overview);
+
+		var mousedown = false;
+		this.shift = function(shift) {
+			if (!mousedown) {
+				return;
+			}
+			if (plot.tds.setShift(shift)) {
+				plot.redrawPlot();
+			}
+			setTimeout(function() {
+				plot.shift(shift);
+			}, 200);
+		}
+		var shiftPressed = function(shift) {
+			mousedown = true;
+			document.onmouseup = function() {
+				mousedown = false;
+				document.onmouseup = null;
+			}
+			plot.shift(shift);
+		}
+
+		this.shiftLeft = document.createElement("div");
+		this.shiftLeft.setAttribute('class', 'shiftLeft');
+		this.gui.plotWindow.appendChild(this.shiftLeft);
+		this.shiftLeft.onmousedown = function() {
+			shiftPressed(1);
+		}
+
+		this.shiftRight = document.createElement("div");
+		this.shiftRight.setAttribute('class', 'shiftRight');
+		this.gui.plotWindow.appendChild(this.shiftRight);
+		this.shiftRight.onmousedown = function() {
+			shiftPressed(-1);
+		}
+
+		this.plotLabels = document.createElement("div");
+		this.plotLabels.setAttribute('class', 'plotLabels');
+		this.gui.plotWindow.appendChild(this.plotLabels);
+
+		this.initLabels(this.timeplot.regularGrid());
+
+		//Finds the time corresponding to the position x on the timeplot
+		var getCorrelatedTime = function(x) {
+			if (x >= plot.canvas.width)
+				x = plot.canvas.width;
+			if (isNaN(x) || x < 0)
+				x = 0;
+			var t = plot.timeGeometry.fromScreen(x);
+			if (t == 0)
+				return;
+			return plot.dataSources[0].getClosestValidTime(t);
+		}
+		//Finds the position corresponding to the time t on the timeplot
+		var getCorrelatedPosition = function(t) {
+			var x = plot.timeGeometry.toScreen(t);
+			if (x >= plot.canvas.width)
+				x = plot.canvas.width;
+			if (isNaN(x) || x < 0)
+				x = 0;
+			return x;
+		}
+		//Maps the 2 positions in the right order to left and right bound of the chosen timeRange
+		var mapPositions = function(pos1, pos2) {
+			if (pos1 > pos2) {
+				plot.leftFlagPos = pos2;
+				plot.rightFlagPos = pos1;
+			} else {
+				plot.leftFlagPos = pos1;
+				plot.rightFlagPos = pos2;
+			}
+			plot.leftFlagTime = plot.dataSources[0].getClosestValidTime(plot.timeGeometry.fromScreen(plot.leftFlagPos));
+			plot.rightFlagTime = plot.dataSources[0].getClosestValidTime(plot.timeGeometry.fromScreen(plot.rightFlagPos));
+		}
+		//Sets the divs corresponding to the actual chosen timeRange
+		var setRangeDivs = function() {
+			plot.leftFlagPole.style.visibility = "visible";
+			plot.rightFlagPole.style.visibility = "visible";
+			plot.rangeBox.style.visibility = "visible";
+			plot.timeplot.placeDiv(plot.leftFlagPole, {
+				left : plot.leftFlagPos,
+				bottom : 0,
+				height : plot.canvas.height,
+				display : "block"
+			});
+			plot.timeplot.placeDiv(plot.rightFlagPole, {
+				left : plot.rightFlagPos,
+				bottom : 0,
+				height : plot.canvas.height,
+				display : "block"
+			});
+			var boxWidth = plot.rightFlagPos - plot.leftFlagPos;
+			if (plot.popup) {
+				plot.popupClickDiv.style.visibility = "visible";
+				plot.timeplot.placeDiv(plot.popupClickDiv, {
+					left : plot.leftFlagPos,
+					width : boxWidth + 1,
+					height : plot.canvas.height,
+					display : "block"
+				});
+			}
+			plot.timeplot.placeDiv(plot.rangeBox, {
+				left : plot.leftFlagPos,
+				width : boxWidth + 1,
+				height : plot.canvas.height,
+				display : "block"
+			});
+			var plots = plot.timeplot._plots;
+			for ( i = 0; i < plots.length; i++) {
+				plots[i].fullOpacityPlot(plot.leftFlagTime, plot.rightFlagTime, plot.leftFlagPos, plot.rightFlagPos, GeoTemConfig.getColor(i));
+				plots[i].opacityPlot.style.visibility = "visible";
+			}
+			var unit = plot.tds.unit;
+
+			var top = plot.gui.plotContainer.offsetTop;
+			var left = plot.gui.plotContainer.offsetLeft;
+			var leftPos = plot.leftFlagPole.offsetLeft + plot.timeplot.getElement().offsetLeft;
+			var rightPos = plot.rightFlagPole.offsetLeft + plot.timeplot.getElement().offsetLeft;
+			var rW = rightPos - leftPos;
+			var pW = plot.canvas.width;
+			var pL = plot.timeplot.getElement().offsetLeft;
+
+			var handleTop = top + Math.floor(plot.gui.timeplotDiv.offsetHeight / 2 - plot.leftHandle.offsetHeight / 2);
+			plot.leftHandle.style.visibility = "visible";
+			plot.rightHandle.style.visibility = "visible";
+			plot.leftHandle.style.left = (leftPos - plot.leftHandle.offsetWidth / 2) + "px";
+			plot.rightHandle.style.left = (rightPos - plot.rightHandle.offsetWidth + 1 + plot.rightHandle.offsetWidth / 2) + "px";
+			plot.leftHandle.style.top = handleTop + "px";
+			plot.rightHandle.style.top = handleTop + "px";
+			if (rightPos == leftPos) {
+				plot.rightHandle.style.visibility = "hidden";
+				plot.leftHandle.style.backgroundImage = "url(" + GeoTemConfig.path + "mergedHandle.png" + ")";
+			} else {
+				plot.leftHandle.style.backgroundImage = "url(" + GeoTemConfig.path + "leftHandle.png" + ")";
+			}
+			plot.cancelButton.style.visibility = "visible";
+			plot.cancelButton.style.top = top + "px";
+
+			if (rW > plot.cancelButton.offsetWidth) {
+				plot.cancelButton.style.left = (left + rightPos - plot.cancelButton.offsetWidth) + "px";
+			} else {
+				plot.cancelButton.style.left = (left + rightPos) + "px";
+			}
+			var tW = plot.toolbarAbsoluteDiv.offsetWidth;
+			if (rW >= tW) {
+				plot.toolbar.style.left = leftPos + "px";
+				plot.toolbar.style.width = (rW + 1) + "px";
+				plot.toolbarAbsoluteDiv.style.left = ((rW - tW) / 2) + "px";
+			} else {
+				plot.toolbar.style.left = (pL + plot.leftFlagPos * (pW - tW) / (pW - rW)) + "px";
+				plot.toolbar.style.width = (tW + 2) + "px";
+				plot.toolbarAbsoluteDiv.style.left = "0px";
+			}
+			plot.toolbar.style.top = (top + plot.timeplot.getElement().offsetHeight) + "px";
+			plot.toolbar.style.visibility = "visible";
+			plot.toolbarAbsoluteDiv.style.visibility = "visible";
+
+		}
+		var getAbsoluteLeft = function(div) {
+			var left = 0;
+			while (div) {
+				left += div.offsetLeft;
+				div = div.offsetParent;
+			}
+			return left;
+		}
+		var timeplotLeft = getAbsoluteLeft(plot.timeplot.getElement());
+
+		var checkPolesForStyle = function(x) {
+			if (plot.style == 'bars' && plot.leftFlagTime == plot.rightFlagTime) {
+				var index = plot.tds.getSliceIndex(plot.leftFlagTime);
+				var time1 = plot.leftFlagTime;
+				var pos1 = plot.leftFlagPos;
+				var time2, pos2;
+				if (index == 0) {
+					time2 = plot.tds.getSliceTime(index + 1);
+				} else if (index == plot.tds.getSliceNumber() - 1) {
+					time2 = plot.tds.getSliceTime(index - 1);
+				} else {
+					if (x < plot.leftFlagPos) {
+						time2 = plot.tds.getSliceTime(index - 1);
+					} else {
+						time2 = plot.tds.getSliceTime(index + 1);
+					}
+				}
+				pos2 = plot.timeGeometry.toScreen(time2);
+				mapPositions(pos1, pos2, time1, time2);
+			}
+		}
+		var startX, startY, multiplier;
+
+		// mousemove function that causes moving selection of objects and toolbar divs
+		var moveToolbar = function(start, actual) {
+			var pixelShift = actual - start;
+			if (plot.status == 2) {
+				var newTime = getCorrelatedTime(startX + pixelShift);
+				if (newTime == plot.mouseTempTime) {
+					return;
+				}
+				plot.mouseTempTime = newTime;
+				plot.mouseTempPos = plot.timeGeometry.toScreen(plot.mouseTempTime);
+				mapPositions(plot.mouseDownPos, plot.mouseTempPos);
+			} else if (plot.status == 3) {
+				pixelShift *= multiplier;
+				var plotPos = actual - timeplotLeft;
+				if (plotPos <= plot.canvas.width / 2) {
+					var newTime = getCorrelatedTime(startX + pixelShift);
+					if (newTime == plot.leftFlagTime) {
+						return;
+					}
+					plot.leftFlagTime = newTime;
+					var diff = plot.leftFlagPos;
+					plot.leftFlagPos = plot.timeGeometry.toScreen(plot.leftFlagTime);
+					diff -= plot.leftFlagPos;
+					plot.rightFlagTime = getCorrelatedTime(plot.rightFlagPos - diff);
+					plot.rightFlagPos = plot.timeGeometry.toScreen(plot.rightFlagTime);
+				} else {
+					var newTime = getCorrelatedTime(startY + pixelShift);
+					if (newTime == plot.rightFlagTime) {
+						return;
+					}
+					plot.rightFlagTime = newTime;
+					var diff = plot.rightFlagPos;
+					plot.rightFlagPos = plot.timeGeometry.toScreen(plot.rightFlagTime);
+					diff -= plot.rightFlagPos;
+					plot.leftFlagTime = getCorrelatedTime(plot.leftFlagPos - diff);
+					plot.leftFlagPos = plot.timeGeometry.toScreen(plot.leftFlagTime);
+				}
+			}
+			checkPolesForStyle(actual - timeplotLeft);
+			setRangeDivs();
+			plot.timeSelection();
+		}
+		// fakes user interaction mouse move
+		var playIt = function(start, actual, reset) {
+			if (!plot.paused) {
+				var pixel = plot.canvas.width / (plot.tds.timeSlices.length - 1 ) / 5;
+				var wait = 20 * pixel;
+				if (reset) {
+					actual = 0;
+				}
+				moveToolbar(start, actual);
+				if (plot.rightFlagPos >= plot.canvas.width) {
+					reset = true;
+					wait = 1000;
+				} else {
+					reset = false;
+				}
+				setTimeout(function() {
+					playIt(start, actual + pixel, reset)
+				}, wait);
+			}
+		}
+		var setMultiplier = function() {
+			var rangeWidth = plot.rightFlagPos - plot.leftFlagPos;
+			var toolbarWidth = plot.toolbarAbsoluteDiv.offsetWidth;
+			var plotWidth = plot.canvas.width;
+			if (rangeWidth < toolbarWidth) {
+				multiplier = (plotWidth - rangeWidth) / (plotWidth - toolbarWidth);
+			} else {
+				multiplier = 1;
+			}
+		}
+		/**
+		 * starts the animation
+		 */
+		this.play = function() {
+			if (this.leftFlagPos == null) {
+				return;
+			}
+			plot.paused = false;
+			plot.gui.updateAnimationButtons(2);
+			plot.status = 3;
+			setMultiplier();
+			startX = plot.leftFlagPos;
+			startY = plot.rightFlagPos;
+			var position = Math.round(plot.leftFlagPos);
+			playIt(position, position + 1, false);
+		}
+		/**
+		 * stops the animation
+		 */
+		this.stop = function() {
+			plot.paused = true;
+			plot.status = 0;
+			plot.gui.updateAnimationButtons(1);
+		}
+		// triggers the mousemove function to move the range and toolbar
+		var toolbarEvent = function(evt) {
+			var left = GeoTemConfig.getMousePosition(evt).left;
+			document.onmousemove = function(evt) {
+				moveToolbar(left, GeoTemConfig.getMousePosition(evt).left);
+				if (plot.popup) {
+					plot.popup.reset();
+				}
+			}
+		}
+		var initializeLeft = function() {
+			plot.mouseDownTime = plot.rightFlagTime;
+			plot.mouseTempTime = plot.leftFlagTime;
+			plot.mouseDownPos = plot.timeGeometry.toScreen(plot.mouseDownTime);
+			startX = plot.leftFlagPos;
+		}
+		var initializeRight = function() {
+			plot.mouseDownTime = plot.leftFlagTime;
+			plot.mouseTempTime = plot.rightFlagTime;
+			plot.mouseDownPos = plot.timeGeometry.toScreen(plot.mouseDownTime);
+			startX = plot.rightFlagPos;
+		}
+		var initializeDrag = function() {
+			startX = plot.leftFlagPos;
+			startY = plot.rightFlagPos;
+			setMultiplier();
+		}
+		var checkBorders = function() {
+			if (plot.style == 'bars' && plot.mouseUpTime == plot.mouseDownTime) {
+				var index = plot.tds.getSliceIndex(plot.mouseUpTime);
+				if (index == 0) {
+					plot.mouseUpTime = plot.tds.getSliceTime(index + 1);
+				} else if (index == plot.tds.getSliceNumber() - 1) {
+					plot.mouseUpTime = plot.tds.getSliceTime(index - 1);
+				} else {
+					if (plot.x < plot.leftFlagPos) {
+						plot.mouseUpTime = plot.tds.getSliceTime(index - 1);
+					} else {
+						plot.mouseUpTime = plot.tds.getSliceTime(index + 1);
+					}
+				}
+			}
+		}
+		// handles mousedown on left handle
+		this.leftHandle.onmousedown = function(evt) {
+			if (plot.status != 2) {
+
+				initializeLeft();
+				plot.status = 2;
+				toolbarEvent(evt);
+				document.onmouseup = function() {
+					document.onmousemove = null;
+					document.onmouseup = null;
+					plot.stop();
+				}
+			}
+		}
+		// handles mousedown on right handle
+		this.rightHandle.onmousedown = function(evt) {
+			if (plot.status != 2) {
+				initializeRight();
+				plot.status = 2;
+				toolbarEvent(evt);
+				document.onmouseup = function() {
+					document.onmousemove = null;
+					document.onmouseup = null;
+					plot.stop();
+				}
+			}
+		}
+		// handles mousedown on drag button
+		this.dragButton.onmousedown = function(evt) {
+			if (plot.status != 3) {
+				plot.status = 3;
+				initializeDrag();
+				toolbarEvent(evt);
+				document.onmouseup = function() {
+					document.onmousemove = null;
+					document.onmouseup = null;
+					plot.stop();
+				}
+			}
+		}
+		// handles mousedown-Event on timeplot
+		var mouseDownHandler = function(elmt, evt, target) {
+			if (plot.dataSources.length > 0) {
+
+				plot.x = Math.round(SimileAjax.DOM.getEventRelativeCoordinates(evt, plot.canvas).x);
+				if (plot.status == 0) {
+					var time = getCorrelatedTime(plot.x);
+					if (plot.leftFlagPos != null && plot.popup && time >= plot.leftFlagTime && time <= plot.rightFlagTime) {
+						var x = plot.leftFlagPos + (plot.rightFlagPos - plot.leftFlagPos) / 2;
+						var elements = [];
+						for (var i = 0; i < plot.dataSources.length; i++) {
+							elements.push([]);
+						}
+						for (var i = 0; i < plot.selectedObjects.length; i++) {
+							if (plot.selectedObjects[i].value == 1) {
+								for (var j = 0; j < plot.selectedObjects[i].objects.length; j++) {
+									elements[j] = elements[j].concat(plot.selectedObjects[i].objects[j]);
+								}
+							}
+						}
+						var labels = [];
+						for (var i = 0; i < elements.length; i++) {
+							if (elements[i].length == 0) {
+								continue;
+							}
+							var c = GeoTemConfig.getColor(i);
+							var color = 'rgb(' + c.r0 + ',' + c.g0 + ',' + c.b0 + ')';
+							var div = document.createElement("div");
+							div.setAttribute('class', 'tagCloudItem');
+							div.style.color = color;
+							var label = {
+								div : div,
+								elements : elements[i]
+							};
+							var weight = 0;
+							for (j in elements[i] ) {
+								weight += elements[i][j].weight;
+							}
+							var fs = 2 * weight / 1000;
+							if (fs > 2) {
+								fs = 2;
+							}
+							div.style.fontSize = (1 + fs) + "em";
+							div.style.textShadow = "0 0 0.4em black, 0 0 0.4em black, 0 0 0.4em black, 0 0 0.4em " + c.hex;
+							if (weight == 1) {
+								div.innerHTML = weight + " object";
+							} else {
+								div.innerHTML = weight + " objects";
+							}
+							var appendMouseFunctions = function(label, div, color) {
+								div.onclick = function() {
+									plot.popup.showLabelContent(label);
+									div.style.textShadow = "0 0 0.4em black, 0 0 0.4em black, 0 0 0.4em black, 0 0 0.4em " + color;
+								}
+								div.onmouseover = function() {
+									div.style.textShadow = "0 -1px " + color + ", 1px 0 " + color + ", 0 1px " + color + ", -1px 0 " + color;
+								}
+								div.onmouseout = function() {
+									div.style.textShadow = "0 0 0.4em black, 0 0 0.4em black, 0 0 0.4em black, 0 0 0.4em " + color;
+								}
+							}
+							appendMouseFunctions(label, div, c.hex);
+							labels.push(label);
+						}
+						if (labels.length > 0) {
+							plot.popup.createPopup(x + 20, 0, labels);
+						}
+					} else {
+						plot.deselection();
+						plot.status = 1;
+						plot.mouseDownTime = time;
+						plot.mouseTempTime = plot.mouseDownTime;
+						plot.mouseDownPos = plot.timeGeometry.toScreen(plot.mouseDownTime);
+						mapPositions(plot.mouseDownPos, plot.mouseDownPos, plot.mouseDownTime, plot.mouseDownTime);
+						// handles mouseup-Event on timeplot
+						document.onmouseup = function() {
+							if (plot.status == 1) {
+								plot.mouseUpTime = plot.mouseTempTime;
+								plot.mouseUpPos = plot.timeGeometry.toScreen(plot.mouseUpTime);
+								mapPositions(plot.mouseDownPos, plot.mouseUpPos, plot.mouseDownTime, plot.mouseUpTime);
+								checkPolesForStyle(plot.x);
+								setRangeDivs();
+								plot.timeSelection();
+								plot.gui.updateAnimationButtons(1);
+								document.onmouseup = null;
+								plot.status = 0;
+							}
+						}
+					}
+				}
+			}
+		}
+		// handles mousemove-Event on timeplot
+		var mouseMoveHandler = function(elmt, evt, target) {
+			if (plot.dataSources.length > 0) {
+				plot.x = Math.round(SimileAjax.DOM.getEventRelativeCoordinates(evt, plot.canvas).x);
+				if (plot.status == 1) {
+					plot.mouseTempTime = getCorrelatedTime(plot.x);
+					plot.mouseTempPos = plot.timeGeometry.toScreen(plot.mouseTempTime);
+					mapPositions(plot.mouseDownPos, plot.mouseTempPos, plot.mouseDownTime, plot.mouseTempTime);
+					checkPolesForStyle(plot.x);
+					setRangeDivs();
+				}
+			}
+		}
+		// handles mouseout-Event on timeplot
+		var mouseOutHandler = function(elmt, evt, target) {
+			if (plot.dataSources.length > 0) {
+				var x = Math.round(SimileAjax.DOM.getEventRelativeCoordinates(evt, plot.canvas).x);
+				var y = Math.round(SimileAjax.DOM.getEventRelativeCoordinates(evt, plot.canvas).y);
+				if (x > plot.canvas.width - 2 || isNaN(x) || x < 2) {
+					plot.timeHighlight(true);
+					plot.highlightedSlice = undefined;
+				} else if (y > plot.canvas.height - 2 || isNaN(y) || y < 2) {
+					plot.timeHighlight(true);
+					plot.highlightedSlice = undefined;
+				}
+			}
+		}
+		// handles mouse(h)over-Event on timeplot
+		var mouseHoverHandler = function(elmt, evt, target) {
+			if (plot.dataSources.length > 0) {
+				var x = Math.round(SimileAjax.DOM.getEventRelativeCoordinates(evt, plot.canvas).x);
+				var time = getCorrelatedTime(x);
+				if (time == undefined) {
+					return;
+				}
+				var highlightSlice;
+				var slices = plot.tds.timeSlices;
+				var index = plot.tds.getSliceIndex(time);
+				if (plot.style == 'graph') {
+					highlightSlice = slices[index];
+				}
+				if (plot.style == 'bars') {
+					var pos = plot.timeGeometry.toScreen(time);
+					if (x < pos && index > 0) {
+						highlightSlice = slices[index - 1];
+					} else {
+						highlightSlice = slices[index];
+					}
+				}
+				if (plot.highlightedSlice == undefined || plot.highlightedSlice != highlightSlice) {
+					plot.highlightedSlice = highlightSlice;
+					plot.timeHighlight(false);
+				}
+			}
+		}
+
+		this.redrawPlot = function() {
+			plot.clearTimeplot();
+			plot.tds.reset(this.timeGeometry);
+			plot.timeplot._prepareCanvas();
+			plot.timeplot.repaint();
+			if (plot.leftFlagPos != null) {
+				plot.leftFlagPos = getCorrelatedPosition(plot.leftFlagTime);
+				plot.rightFlagPos = getCorrelatedPosition(plot.rightFlagTime);
+				setRangeDivs();
+			} else {
+				plot.displayOverlay();
+			}
+			plot.initLabels([]);
+			plot.updateOverview();
+		}
+
+		this.resetOpacityPlots = function() {
+			var plots = plot.timeplot._plots;
+			for ( var i = 0; i < plots.length; i++) {
+				plots[i]._opacityCanvas.width = this.canvas.width;
+				plots[i]._opacityCanvas.height = this.canvas.height;
+				if( plot.leftFlagTime != null ){
+					plots[i].fullOpacityPlot(plot.leftFlagTime, plot.rightFlagTime, plot.leftFlagPos, plot.rightFlagPos, GeoTemConfig.getColor(i));
+				}
+			}
+		}
+
+		/**
+		 * handles zoom of the timeplot
+		 * @param {int} delta the change of zoom
+		 * @param {Date} time a time that corresponds to a slice, that was clicked
+		 */
+		/*
+		this.zoom = function(delta,time){
+		if( this.eventSources.length == 0 ){
+		if( GeoTemConfig.timeZoom ){
+		this.zoomSlider.setValue(0);
+		}
+		return false;
+		}
+		if( time == null ){
+		time = getCorrelatedTime(this.canvas.width/2);
+		}
+		if( this.tds.setZoom(delta,time,this.leftFlagTime,this.rightFlagTime) ){
+		this.redrawPlot();
+		}
+		if( GeoTemConfig.timeZoom ){
+		this.zoomSlider.setValue(this.tds.getZoom());
+		}
+		return true;
+		}
+		*/
+
+		// handles mousewheel event on the timeplot
+		var mouseWheelHandler = function(elmt, evt, target) {
+			if (evt.preventDefault) {
+				evt.preventDefault();
+			}
+			if (plot.dataSources.length == 0) {
+				return;
+			}
+			var delta = 0;
+			if (!evt)
+				evt = window.event;
+			if (evt.wheelDelta) {
+				delta = evt.wheelDelta / 120;
+				if (window.opera)
+					delta = -delta;
+			} else if (evt.detail) {
+				delta = -evt.detail / 3;
+			}
+			if (delta) {
+				var x = Math.round(SimileAjax.DOM.getEventRelativeCoordinates(evt, plot.canvas).x);
+				var time = getCorrelatedTime(x);
+				plot.zoom(delta, time);
+			}
+		}
+		var timeplotElement = this.timeplot.getElement();
+		SimileAjax.DOM.registerEvent(timeplotElement, "mousedown", mouseDownHandler);
+		SimileAjax.DOM.registerEvent(timeplotElement, "mousemove", mouseMoveHandler);
+		SimileAjax.DOM.registerEvent(timeplotElement, "mousemove", mouseHoverHandler);
+		SimileAjax.DOM.registerEvent(timeplotElement, "mouseout", mouseOutHandler);
+		if (GeoTemConfig.mouseWheelZoom) {
+			//SimileAjax.DOM.registerEvent(timeplotElement, "mousewheel", mouseWheelHandler);
+		}
+
+		this.gui.setHeight();
+
+	},
+
+	resetOverlay : function() {
+		this.poles.style.visibility = "hidden";
+		var plots = this.timeplot._plots;
+		for (var i = 0; i < plots.length; i++) {
+			for (var j = 0; j < plots[i].pins.length; j++) {
+				plots[i].pins[j] = {
+					height : 0,
+					count : 0
+				};
+			}
+		}
+	},
+
+	/**
+	 * resets the timeplot to non selection status
+	 */
+	reset : function() {
+
+		this.leftFlagPole.style.visibility = "hidden";
+		this.rightFlagPole.style.visibility = "hidden";
+		this.rangeBox.style.visibility = "hidden";
+		this.leftHandle.style.visibility = "hidden";
+		this.rightHandle.style.visibility = "hidden";
+		this.toolbar.style.visibility = "hidden";
+		this.toolbarAbsoluteDiv.style.visibility = "hidden";
+		this.cancelButton.style.visibility = "hidden";
+
+		var plots = this.timeplot._plots;
+		for (var i = 0; i < plots.length; i++) {
+			plots[i].opacityPlot.style.visibility = "hidden";
+		}
+		this.resetOverlay();
+		this.filterBar.reset(false);
+
+		var slices = this.tds.timeSlices;
+		if (slices != undefined) {
+			for (var i = 0; i < slices.length; i++) {
+				slices[i].reset();
+			}
+		}
+
+		this.status = 0;
+		this.stop();
+		this.gui.updateAnimationButtons(0);
+
+		this.leftFlagPos = null;
+		this.leftFlagTime = null;
+		this.rightFlagPos = null;
+		this.rightFlagTime = null;
+
+		this.mouseDownTime = null;
+		this.mouseUpTime = null;
+		this.mouseTempTime = null;
+
+		this.mouseDownPos = null;
+		this.mouseUpPos = null;
+		this.mouseTempPos = null;
+
+		if (this.popup) {
+			this.popup.reset();
+			this.popupClickDiv.style.visibility = "hidden";
+		}
+
+	},
+
+	/**
+	 * sets a pole on the timeplot
+	 * @param {Date} time the time of the specific timeslice
+	 * @param {int[]} the number of selected elements per dataset
+	 */
+	displayOverlay : function() {
+		this.poles.style.visibility = "visible";
+		var cv = this.poles.getElementsByTagName("canvas")[0];
+		cv.width = this.canvas.width;
+		cv.height = this.canvas.height;
+		if (!cv.getContext && G_vmlCanvasManager) {
+			cv = G_vmlCanvasManager.initElement(cv);
+		}
+		var ctx = cv.getContext('2d');
+		ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
+		var plots = this.timeplot._plots;
+		var slices = this.tds.timeSlices;
+		for (var i = 0; i < slices.length; i++) {
+			if (this.style == 'bars' && i + 1 == slices.length) {
+				return;
+			}
+			if (slices[i].overlay() == 0) {
+				continue;
+			}
+			var projStacks = slices[i].projStacks;
+			var time = slices[i].date;
+			var pos;
+			if (this.style == 'graph') {
+				pos = this.timeGeometry.toScreen(time);
+			} else if (this.style == 'bars') {
+				var x1 = this.timeGeometry.toScreen(time);
+				var x2 = this.timeGeometry.toScreen(slices[i + 1].date);
+				pos = (x1 + x2 ) / 2;
+			}
+			var heights = [];
+			var h = 0;
+			for (var j = 0; j < projStacks.length; j++) {
+				var data = plots[j]._dataSource.getData();
+				for (var k = 0; k < data.times.length; k++) {
+					if (data.times[k].getTime() == time.getTime()) {
+						var height = plots[j]._valueGeometry.toScreen(plots[j]._dataSource.getData().values[k]) * projStacks[j].overlay / projStacks[j].value;
+						heights.push(height);
+						plots[j].pins[i] = {
+							height : height,
+							count : projStacks[j].overlay
+						};
+						if (height > h) {
+							h = height;
+						}
+						break;
+					}
+				}
+			}
+			ctx.fillStyle = "rgb(102,102,102)";
+			ctx.beginPath();
+			ctx.rect(pos - 1, this.canvas.height - h, 2, h);
+			ctx.fill();
+			for (var j = 0; j < heights.length; j++) {
+				if (heights[j] > 0) {
+					var color = GeoTemConfig.getColor(j);
+					ctx.fillStyle = "rgba(" + color.r1 + "," + color.g1 + "," + color.b1 + ",0.6)";
+					ctx.beginPath();
+					ctx.arc(pos, this.canvas.height - heights[j], 2.5, 0, Math.PI * 2, true);
+					ctx.closePath();
+					ctx.fill();
+				}
+			}
+		}
+	},
+
+	/**
+	 * updates the timeplot by displaying place poles, after a selection had been executed in another widget
+	 */
+	highlightChanged : function(timeObjects) {
+		if( !GeoTemConfig.highlightEvents ){
+			return;
+		}
+		this.resetOverlay();
+		if (this.selection.valid()) {
+			if (!this.selection.equal(this)) {
+				this.tds.setOverlay(GeoTemConfig.mergeObjects(timeObjects, this.selection.getObjects(this)));
+			} else {
+				this.tds.setOverlay(timeObjects);
+			}
+		} else {
+			this.tds.setOverlay(timeObjects);
+		}
+		this.displayOverlay();
+	},
+
+	/**
+	 * updates the timeplot by displaying place poles, after a selection had been executed in another widget
+	 */
+	selectionChanged : function(selection) {
+		if( !GeoTemConfig.selectionEvents ){
+			return;
+		}
+		this.reset();
+		this.selection = selection;
+		this.tds.setOverlay(selection.objects);
+		this.displayOverlay();
+	},
+
+	/**
+	 * returns the approximate left position of a slice inside the overview representation
+	 * @param {Date} time time of the slice
+	 */
+	getOverviewLeft : function(time) {
+		var w = this.overview.offsetWidth;
+		var s = this.tds.earliest().getTime();
+		var e = this.tds.latest().getTime();
+		var t = time.getTime();
+		return Math.round(w * (t - s) / (e - s));
+	},
+
+	/**
+	 * visualizes the overview div (shows viewable part of zoomed timeplot)
+	 */
+	initOverview : function() {
+		var labels = this.timeGeometry._grid;
+		if (labels.length == 0) {
+			var plot = this;
+			setTimeout(function() {
+				plot.initOverview();
+			}, 10);
+			return;
+		}
+
+		this.overview.style.width = this.canvas.width + "px";
+		var left = this.gui.timeplotDiv.offsetLeft;
+		this.overview.innerHTML = "";
+		this.overview.style.left = left + "px";
+
+		this.overviewRange = document.createElement("div");
+		this.overviewRange.setAttribute('class', 'overviewRange');
+		this.overview.appendChild(this.overviewRange);
+
+		for (var i = 0; i < labels.length; i++) {
+			var label = document.createElement("div");
+			label.setAttribute('class', 'overviewLabel');
+			label.innerHTML = labels[i].label;
+			label.style.left = Math.floor(labels[i].x) + "px";
+			this.overview.appendChild(label);
+		}
+
+		this.updateOverview();
+	},
+
+	/**
+	 * visualizes the labels of the timeplot
+	 */
+	initLabels : function(labels) {
+		if (labels.length == 0) {
+			labels = this.timeGeometry._grid;
+			if (labels.length == 0) {
+				var plot = this;
+				setTimeout(function() {
+					plot.initLabels([]);
+				}, 10);
+				return;
+			}
+		}
+		this.plotLabels.style.width = this.canvas.width + "px";
+		var left = this.gui.timeplotDiv.offsetLeft;
+		this.plotLabels.style.left = left + "px";
+		this.plotLabels.innerHTML = "";
+		for (var i = 0; i < labels.length; i++) {
+			var label = document.createElement("div");
+			label.setAttribute('class', 'plotLabel');
+			label.innerHTML = labels[i].label;
+			label.style.left = Math.floor(labels[i].x) + "px";
+			this.plotLabels.appendChild(label);
+		}
+	},
+
+	/**
+	 * updates the overview div
+	 */
+	updateOverview : function() {
+		if (this.tds.getZoom() > 0) {
+			this.plotLabels.style.visibility = "hidden";
+			this.timeGeometry._hideLabels = false;
+			this.overview.style.visibility = "visible";
+			this.shiftLeft.style.visibility = "visible";
+			this.shiftRight.style.visibility = "visible";
+			var left = this.getOverviewLeft(this.tds.timeSlices[this.tds.leftSlice].date);
+			var right = this.getOverviewLeft(this.tds.timeSlices[this.tds.rightSlice].date);
+			this.overviewRange.style.left = left + "px";
+			this.overviewRange.style.width = (right - left) + "px";
+		} else {
+			this.timeGeometry._hideLabels = true;
+			this.plotLabels.style.visibility = "visible";
+			this.overview.style.visibility = "hidden";
+			this.shiftLeft.style.visibility = "hidden";
+			this.shiftRight.style.visibility = "hidden";
+		}
+	},
+
+	/**
+	 * returns the time slices which are created by the extended data source
+	 */
+	getSlices : function() {
+		return this.tds.timeSlices;
+	},
+
+	timeSelection : function() {
+		var slices = this.tds.timeSlices;
+		var ls, rs;
+		for (var i = 0; i < slices.length; i++) {
+			if (slices[i].date.getTime() == this.leftFlagTime.getTime())
+				ls = i;
+			if (slices[i].date.getTime() == this.rightFlagTime.getTime()) {
+				if (this.style == 'graph') {
+					rs = i;
+				}
+				if (this.style == 'bars') {
+					rs = i - 1;
+				}
+			}
+		}
+		var selectedObjects = [];
+		for (var i = 0; i < GeoTemConfig.datasets.length; i++) {
+			selectedObjects.push([]);
+		}
+		for (var i = 0; i < slices.length; i++) {
+			if (i >= ls && i <= rs) {
+				for (var j in slices[i].stacks ) {
+					selectedObjects[j] = selectedObjects[j].concat(slices[i].stacks[j].elements);
+				}
+			}
+		}
+		this.selection = new Selection(selectedObjects, this);
+		this.core.triggerSelection(this.selection);
+		this.filterBar.reset(true);
+	},
+
+	deselection : function() {
+		this.reset();
+		this.selection = new Selection();
+		this.core.triggerSelection(this.selection);
+	},
+
+	filtering : function() {
+		for (var i = 0; i < this.datasets.length; i++) {
+			this.datasets[i].objects = this.selection.objects[i];
+		}
+		this.core.triggerRefining(this.datasets);
+	},
+
+	inverseFiltering : function() {
+		var slices = this.tds.timeSlices;
+		var ls, rs;
+		for (var i = 0; i < slices.length; i++) {
+			if (slices[i].date.getTime() == this.leftFlagTime.getTime())
+				ls = i;
+			if (slices[i].date.getTime() == this.rightFlagTime.getTime()) {
+				if (this.style == 'graph') {
+					rs = i;
+				}
+				if (this.style == 'bars') {
+					rs = i - 1;
+				}
+			}
+		}
+		var selectedObjects = [];
+		for (var i = 0; i < GeoTemConfig.datasets.length; i++) {
+			selectedObjects.push([]);
+		}
+		for (var i = 0; i < slices.length; i++) {
+			if (i >= ls && i <= rs) {
+				continue;
+			}
+			for (var j in slices[i].stacks ) {
+				selectedObjects[j] = selectedObjects[j].concat(slices[i].stacks[j].elements);
+			}
+		}
+		this.selection = new Selection(selectedObjects, this);
+		this.filtering();
+	},
+
+	timeHighlight : function(undo) {
+		if (this.status == 0) {
+			var s = this.highlightedSlice;
+			var timeObjects = [];
+			for (var i = 0; i < this.tds.size(); i++) {
+				timeObjects.push([]);
+			}
+			var add = true;
+			if (this.leftFlagTime != null) {
+				if (this.style == 'graph' && s.date >= this.leftFlagTime && s.date <= this.rightFlagTime) {
+					add = false;
+				}
+				if (this.style == 'bars' && s.date >= this.leftFlagTime && s.date < this.rightFlagTime) {
+					add = false;
+				}
+			}
+			if (!undo && add) {
+				for (var i in s.stacks ) {
+					timeObjects[i] = timeObjects[i].concat(s.stacks[i].elements);
+				}
+			}
+			this.core.triggerHighlight(timeObjects);
+		}
+	},
+
+	timeRefining : function() {
+		this.core.triggerRefining(this.selection.objects);
+	},
+
+	setStyle : function(style) {
+		this.style = style;
+	},
+
+	drawLinearPlot : function() {
+		if ( typeof this.valueGeometry != 'undefined') {			
+			this.valueGeometry.actLinear();
+			this.timeplot.repaint();
+			this.resetOpacityPlots();
+			this.displayOverlay();
+		}
+	},
+
+	drawLogarithmicPlot : function() {
+		if ( typeof this.valueGeometry != 'undefined') {
+			this.valueGeometry.actLogarithmic();
+			this.timeplot.repaint();
+			this.resetOpacityPlots();
+			this.displayOverlay();
+		}
+	}
+}
+/*
+* TableConfig.js
+*
+* Copyright (c) 2012, Stefan Jänicke. All rights reserved.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 3 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+* MA 02110-1301  USA
+*/
+
+/**
+ * @class TableConfig
+ * Table Configuration File
+ * @author Stefan Jänicke (stjaenicke@informatik.uni-leipzig.de)
+ * @release 1.0
+ * @release date: 2012-07-27
+ * @version date: 2012-07-27
+ */
+function TableConfig(options) {
+
+	this.options = {
+		tableWidth : false, // false or desired width css definition for the table
+		tableHeight : false, // false or desired height css definition for the table
+		validResultsPerPage : [10, 20, 50, 100], // valid number of elements per page
+		initialResultsPerPage : 10, // initial number of elements per page
+		tableSorting : true, // true, if sorting of columns should be possible
+		tableContentOffset : 250, // maximum display number of characters in a table cell
+		tableSelectPage : true, // selection of complete table pages
+		tableSelectAll : false, // selection of complete tables
+		tableShowSelected : true, // show selected objects only option
+		tableKeepShowSelected : true, // don't revert to show all on "reset" (e.g. selection)
+		tableInvertSelection : true, // show invert selection option
+		tableSelectByText : true, // select objects by full-text search
+		tableCreateNewFromSelected : true, // create new dataset from selected objects
+		unselectedCellColor : '#EEE', // color for an unselected row/tab
+		verticalAlign : 'top', // vertical alignment of the table cells ('top','center','bottom')
+	};
+	if ( typeof options != 'undefined') {
+		$.extend(this.options, options);
+	}
+
+};
+/*
+* TableGui.js
+*
+* Copyright (c) 2012, Stefan Jänicke. All rights reserved.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 3 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+* MA 02110-1301  USA
+*/
+
+/**
+ * @class TableGui
+ * Table GUI Implementation
+ * @author Stefan Jänicke (stjaenicke@informatik.uni-leipzig.de)
+ * @release 1.0
+ * @release date: 2012-07-27
+ * @version date: 2012-07-27
+ *
+ * @param {TableWidget} parent table widget object
+ * @param {HTML object} div parent div to append the table gui
+ * @param {JSON} options table configuration
+ */
+function TableGui(table, div, options) {
+
+	this.tableContainer = div;
+	if (options.tableWidth) {
+		this.tableContainer.style.width = options.tableWidth;
+	}
+	if (options.tableHeight) {
+		this.tableContainer.style.height = options.tableHeight;
+	}
+	this.tableContainer.style.position = 'relative';
+
+	this.tabs = document.createElement('div');
+	this.tabs.setAttribute('class', 'tableTabs');
+	div.appendChild(this.tabs);
+
+	this.input = document.createElement('div');
+	this.input.setAttribute('class', 'tableInput');
+	div.appendChild(this.input);
+
+};
 /*
 * TableWidget.js
 *
@@ -36735,14 +38386,11 @@
 				this.tables[this.activeTable].hide();
 				var c = GeoTemConfig.getColor(this.activeTable);
 				this.tableTabs[this.activeTable].style.backgroundColor = 'rgb(' + c.r0 + ',' + c.g0 + ',' + c.b0 + ')';
-				this.tableTabs[this.activeTable].style.paddingBottom = '5px';
 			}
 			this.activeTable = index;
 			this.tables[this.activeTable].show();
 			var c = GeoTemConfig.getColor(this.activeTable);
 			this.tableTabs[this.activeTable].style.backgroundColor = 'rgb(' + c.r1 + ',' + c.g1 + ',' + c.b1 + ')';
-			this.tableTabs[this.activeTable].style.paddingBottom = '8px';
-			this.gui.tabs.style.borderBottom = '6px solid rgb(' + c.r1 + ',' + c.g1 + ',' + c.b1 + ')';
 			this.core.triggerRise(index);
 		}
 
@@ -37689,6 +39337,7 @@
 		this.addCSVLoader();
 		this.addLocalKMLLoader();
 		this.addLocalCSVLoader();
+		this.addLocalXLSXLoader();
 		
 		// trigger change event on the select so 
 		// that only the first loader div will be shown
@@ -38001,7 +39650,63 @@
 		},this));
 
 		$(this.parent.gui.loaders).append(this.localStorageLoaderTab);
-	}
+	},
+	
+	addLocalXLSXLoader : function() {
+		//taken from http://oss.sheetjs.com/js-xlsx/
+		var fixdata = function(data) {
+			var o = "", l = 0, w = 10240;
+			for(; l<data.byteLength/w; ++l) o+=String.fromCharCode.apply(null,new Uint8Array(data.slice(l*w,l*w+w)));
+			o+=String.fromCharCode.apply(null, new Uint8Array(data.slice(o.length)));
+			return o;
+		}
+		
+		$(this.parent.gui.loaderTypeSelect).append("<option value='LocalXLSXLoader'>local XLS/XLSX File</option>");
+		
+		this.LocalXLSXLoader = document.createElement("div");
+		$(this.LocalXLSXLoader).attr("id","LocalXLSXLoader");
+		
+		this.xlsxFile = document.createElement("input");
+		$(this.xlsxFile).attr("type","file");
+		$(this.LocalXLSXLoader).append(this.xlsxFile);
+		
+		this.loadLocalXLSXButton = document.createElement("button");
+		$(this.loadLocalXLSXButton).text("load XLS/XLSX");
+		$(this.LocalXLSXLoader).append(this.loadLocalXLSXButton);
+
+		$(this.loadLocalXLSXButton).click($.proxy(function(){
+			var filelist = $(this.xlsxFile).get(0).files;
+			if (filelist.length > 0){
+				var file = filelist[0];
+				var fileName = file.name;
+				var reader = new FileReader();
+				
+				reader.onloadend = ($.proxy(function(theFile) {
+			        return function(e) {
+			        	var workbook;
+			        	var json;
+			        	if (fileName.toLowerCase().indexOf("xlsx")!=-1){
+			        		workbook = XLSX.read(btoa(fixdata(reader.result)), {type: 'base64'});
+			        		var csv = XLSX.utils.sheet_to_csv(workbook.Sheets[workbook.SheetNames[0]]);
+			        		var json = GeoTemConfig.convertCsv(csv);
+			        	} else {
+			        		workbook = XLS.read(btoa(fixdata(reader.result)), {type: 'base64'});
+			        		var csv = XLS.utils.sheet_to_csv(workbook.Sheets[workbook.SheetNames[0]]);
+			        		var json = GeoTemConfig.convertCsv(csv);
+			        	}
+			        	
+						var dataSet = new Dataset(GeoTemConfig.loadJson(json), fileName);
+						if (dataSet != null)
+							this.distributeDataset(dataSet);			
+			        };
+			    }(file),this));
+
+				reader.readAsArrayBuffer(file);
+			}
+		},this));
+
+		$(this.parent.gui.loaders).append(this.LocalXLSXLoader);
+	},
 };
 /*
 * DataloaderConfig.js
@@ -38134,6 +39839,8 @@
 	this.gui = new DataloaderGui(this, div, this.options);
 	
 	this.dataLoader = new Dataloader(this);
+	
+	this.datasets = [];
 }
 
 DataloaderWidget.prototype = {
@@ -38176,77 +39883,17 @@
 	reset : function() {
 	},
 	
-	loadFromURL : function() {
-		var dataLoaderWidget = this;
-		//using jQuery-URL-Parser (https://github.com/skruse/jQuery-URL-Parser)
-		var datasets = [];
-		$.each($.url().param(),function(paramName, paramValue){
-			//startsWith and endsWith defined in SIMILE Ajax (string.js)
-			var fileName = dataLoaderWidget.dataLoader.getFileName(paramValue);
-			var origURL = paramValue;
-			if (typeof GeoTemConfig.proxy != 'undefined')
-				paramValue = GeoTemConfig.proxy + paramValue;
-			if (paramName.toLowerCase().startsWith("kml")){
-				var kmlDoc = GeoTemConfig.getKml(paramValue);
-				var dataSet = new Dataset(GeoTemConfig.loadKml(kmlDoc), fileName, origURL);
-				if (dataSet != null){
-					var datasetID = parseInt(paramName.substr(3));
-					if (!isNaN(datasetID)){
-						datasets[datasetID] = dataSet;
-					} else {
-						datasets.push(dataSet);							
-					}
-				}
-			}
-			else if (paramName.toLowerCase().startsWith("csv")){
-				var json = GeoTemConfig.getCsv(paramValue);
-				var dataSet = new Dataset(GeoTemConfig.loadJson(json), fileName, origURL);
-				if (dataSet != null){
-					var datasetID = parseInt(paramName.substr(3));
-					if (!isNaN(datasetID)){
-						datasets[datasetID] = dataSet;
-					} else {
-						datasets.push(dataSet);							
-					}
-				}
-			}
-			else if (paramName.toLowerCase().startsWith("json")){
-				var json = GeoTemConfig.getJson(paramValue);
-				var dataSet = new Dataset(GeoTemConfig.loadJson(json), fileName, origURL);
-				if (dataSet != null){
-					var datasetID = parseInt(paramName.substr(4));
-					if (!isNaN(datasetID)){
-						datasets[datasetID] = dataSet;
-					} else {
-						datasets.push(dataSet);							
-					}
-				}
-			}
-			else if (paramName.toLowerCase().startsWith("local")){
-				var csv = $.remember({name:encodeURIComponent(origURL)});
-				//TODO: this is a bad idea and will be changed upon having a better
-				//usage model for local stored data
-				var fileName = origURL.substring("GeoBrowser_dataset_".length);
-				var json = GeoTemConfig.convertCsv(csv);
-				var dataSet = new Dataset(GeoTemConfig.loadJson(json), fileName, origURL, "local");
-				if (dataSet != null){
-					var datasetID = parseInt(paramName.substr(5));
-					if (!isNaN(datasetID)){
-						datasets[datasetID] = dataSet;
-					} else {
-						datasets.push(dataSet);							
-					}
-				}
-			}
-		});
+	loadRenames : function(){
 		//load (optional!) attribute renames
 		//each rename param is {latitude:..,longitude:..,place:..,date:..,timeSpanBegin:..,timeSpanEnd:..}
 		//examples:
 		//	&rename1={"latitude":"lat1","longitude":"lon1"}
 		//	&rename2=[{"latitude":"lat1","longitude":"lon1"},{"latitude":"lat2","longitude":"lon2"}]
+		var dataLoaderWidget = this;
+		var datasets = dataLoaderWidget.datasets;
 		$.each($.url().param(),function(paramName, paramValue){
 			if (paramName.toLowerCase().startsWith("rename")){
-				var datasetID = parseInt(paramName.substr(6));
+				var datasetID = parseInt(paramName.replace(/\D/g,''));
 				var dataset;
 				if (isNaN(datasetID)){
 					var dataset;
@@ -38327,12 +39974,17 @@
 				}
 			}
 		});
+	},
+	
+	loadFilters : function(){
 		//load (optional!) filters
 		//those will create a new(!) dataset, that only contains the filtered IDs
+		var dataLoaderWidget = this;
+		var datasets = dataLoaderWidget.datasets;
 		$.each($.url().param(),function(paramName, paramValue){
 			//startsWith and endsWith defined in SIMILE Ajax (string.js)
 			if (paramName.toLowerCase().startsWith("filter")){
-				var datasetID = parseInt(paramName.substr(6));
+				var datasetID = parseInt(paramName.replace(/\D/g,''));
 				var dataset;
 				if (isNaN(datasetID)){
 					var dataset;
@@ -38367,12 +40019,17 @@
 				}
 
 			}
-		});
+		});		
+	},
+	
+	loadColors : function(){
 		//Load the (optional!) dataset colors
+		var dataLoaderWidget = this;
+		var datasets = dataLoaderWidget.datasets;
 		$.each($.url().param(),function(paramName, paramValue){
 			if (paramName.toLowerCase().startsWith("color")){
 				//color is 1-based, index is 0-based!
-				var datasetID = parseInt(paramName.substring("color".length))-1;
+				var datasetID = parseInt(paramName.replace(/\D/g,''));
 				if (datasets.length > datasetID){
 					if (typeof datasets[datasetID].color === "undefined"){
 						var color = new Object();
@@ -38408,21 +40065,147 @@
 					}	
 				}
 			}	
-		});
-		//delete undefined entries in the array
-		//(can happen if the sequence given in the URL is not complete
-		// e.g. kml0=..,kml2=..)
-		//this also reorders the array,	 starting with 0
-		var tempDatasets = [];
-		for(var index in datasets){
-			if (datasets[index] instanceof Dataset){
-				tempDatasets.push(datasets[index]);
-			}
-		}
-		datasets = tempDatasets;
-		
-		if (datasets.length > 0)
-			dataLoaderWidget.dataLoader.distributeDatasets(datasets);
+		});		
+	},
+	
+	loadFromURL : function() {
+		var dataLoaderWidget = this;
+		dataLoaderWidget.datasets = [];
+		//using jQuery-URL-Parser (https://github.com/skruse/jQuery-URL-Parser)
+		var datasets = dataLoaderWidget.datasets;
+		var parametersHash = $.url().param();
+		var parametersArray = [];
+		$.each(parametersHash,function(paramName, paramValue){
+			parametersArray.push({paramName:paramName, paramValue:paramValue});
+		});
+		
+		var parseParam = function(paramNr){
+			
+			if (paramNr==parametersArray.length){
+				dataLoaderWidget.loadRenames();
+				dataLoaderWidget.loadFilters();
+				dataLoaderWidget.loadColors();
+
+				//delete undefined entries in the array
+				//(can happen if the sequence given in the URL is not complete
+				// e.g. kml0=..,kml2=..)
+				//this also reorders the array,	 starting with 0
+				var tempDatasets = [];
+				for(var index in datasets){
+					if (datasets[index] instanceof Dataset){
+						tempDatasets.push(datasets[index]);
+					}
+				}
+				datasets = tempDatasets;
+				
+				if (datasets.length > 0){
+					dataLoaderWidget.dataLoader.distributeDatasets(datasets);
+				}
+				return;
+			}
+			
+			var paramName = parametersArray[paramNr].paramName;
+			var paramValue = parametersArray[paramNr].paramValue;
+
+			var datasetID = parseInt(paramName.replace(/\D/g,''));
+			
+			//startsWith and endsWith defined in SIMILE Ajax (string.js)
+			var fileName = dataLoaderWidget.dataLoader.getFileName(paramValue);
+			var origURL = paramValue;
+			if (typeof GeoTemConfig.proxy != 'undefined')
+				paramValue = GeoTemConfig.proxy + paramValue;
+			if (paramName.toLowerCase().startsWith("kml")){
+				GeoTemConfig.getKml(paramValue, function(kmlDoc){
+					var dataSet = new Dataset(GeoTemConfig.loadKml(kmlDoc), fileName, origURL);
+					if (dataSet != null){
+						if (!isNaN(datasetID)){
+							datasets[datasetID] = dataSet;
+						} else {
+							datasets.push(dataSet);							
+						}
+					}
+					setTimeout(function(){parseParam(paramNr+1)},1);
+				});
+			}
+			else if (paramName.toLowerCase().startsWith("csv")){
+				GeoTemConfig.getCsv(paramValue,function(json){
+					var dataSet = new Dataset(GeoTemConfig.loadJson(json), fileName, origURL);
+					if (dataSet != null){
+						if (!isNaN(datasetID)){
+							datasets[datasetID] = dataSet;
+						} else {
+							datasets.push(dataSet);							
+						}
+					}
+					setTimeout(function(){parseParam(paramNr+1)},1);
+				});
+			}
+			else if (paramName.toLowerCase().startsWith("json")){
+				GeoTemConfig.getJson(paramValue,function(json ){
+					var dataSet = new Dataset(GeoTemConfig.loadJson(json), fileName, origURL);
+					if (dataSet != null){
+						if (!isNaN(datasetID)){
+							datasets[datasetID] = dataSet;
+						} else {
+							datasets.push(dataSet);							
+						}
+					}
+					setTimeout(function(){parseParam(paramNr+1)},1);
+				});
+			}
+			else if (paramName.toLowerCase().startsWith("local")){
+				var csv = $.remember({name:encodeURIComponent(origURL)});
+				//TODO: this is a bad idea and will be changed upon having a better
+				//usage model for local stored data
+				var fileName = origURL.substring("GeoBrowser_dataset_".length);
+				var json = GeoTemConfig.convertCsv(csv);
+				var dataSet = new Dataset(GeoTemConfig.loadJson(json), fileName, origURL, "local");
+				if (dataSet != null){
+					if (!isNaN(datasetID)){
+						datasets[datasetID] = dataSet;
+					} else {
+						datasets.push(dataSet);							
+					}
+				}
+				setTimeout(function(){parseParam(paramNr+1)},1);
+			} else if (paramName.toLowerCase().startsWith("xls")){
+				GeoTemConfig.getBinary(paramValue,function(binaryData){
+					var data = new Uint8Array(binaryData);
+					var arr = new Array();
+					for(var i = 0; i != data.length; ++i){
+						arr[i] = String.fromCharCode(data[i]);
+					}
+					
+					var workbook;
+		        	var json;
+		        	if (paramName.toLowerCase().startsWith("xlsx")){
+		        		workbook = XLSX.read(arr.join(""), {type:"binary"});
+		        		var csv = XLSX.utils.sheet_to_csv(workbook.Sheets[workbook.SheetNames[0]]);
+		        		var json = GeoTemConfig.convertCsv(csv);
+		        	} else {
+		        		workbook = XLS.read(arr.join(""), {type:"binary"});
+		        		var csv = XLS.utils.sheet_to_csv(workbook.Sheets[workbook.SheetNames[0]]);
+		        		var json = GeoTemConfig.convertCsv(csv);
+		        	}
+		        	
+					var dataSet = new Dataset(GeoTemConfig.loadJson(json), fileName, origURL);
+					if (dataSet != null){
+						if (!isNaN(datasetID)){
+							datasets[datasetID] = dataSet;
+						} else {
+							datasets.push(dataSet);							
+						}
+					}
+					setTimeout(function(){parseParam(paramNr+1)},1);
+				});
+			} else {
+				setTimeout(function(){parseParam(paramNr+1)},1);
+			}
+		};
+		
+		if (parametersArray.length>0){
+			parseParam(0)
+		}
 	}
 };
 /*
@@ -39193,8 +40976,6 @@
 								weight = this.weight * ticks.lastTickPercentage;
 							else
 								weight = this.weight;
-							
-							weight = this.weight;
 						}
 
 						chartDataCounter[i] += weight;
@@ -39969,7 +41750,7 @@
 		var overallSpan = rangeSlider.parent.overallMax-rangeSlider.parent.overallMin;
 		//only add spans that are not too small for the data
 		for (var i = 0; i < fixedSpans.length; i++){
-			if (	(fixedSpans[i].asMilliseconds() > (smallestSpan.asMilliseconds() * 0.5)) &&
+			if (	(fixedSpans[i].asMilliseconds() > (smallestSpan.asMilliseconds() * 0.25)) &&
 					(fixedSpans[i].asMilliseconds() < overallSpan)
 					&&
 					(
@@ -40313,9 +42094,18 @@
 		if( !GeoTemConfig.selectionEvents ){
 			return;
 		}
-		if (selection.valid())
-			fuzzyTimeline.selected = selection.objects;
-		else 
+		if ((typeof selection.objects !== "undefined")&&
+			(selection.objects.length == GeoTemConfig.datasets.length)){
+			var objectCount = 0;
+			for (var i=0, il=selection.objects.length; i < il; i++){
+				objectCount += selection.objects[i].length;
+			}
+			if (objectCount > 0){
+				fuzzyTimeline.selected = selection.objects;
+			} else {
+				delete fuzzyTimeline.selected;
+			}
+		} else 
 			delete fuzzyTimeline.selected;
 		if (fuzzyTimeline.viewMode === "density")
 			this.density.selectionChanged(fuzzyTimeline.selected);
@@ -40734,12 +42524,11 @@
 		fuzzyTimeline.zoomFactor = zoomFactor;
 		if (zoomFactor > 1){
 			$(fuzzyTimeline.gui.plotDiv).width(zoomFactor*100+"%");
-			//leave place for the scrollbar
-			$(fuzzyTimeline.gui.plotDiv).height(fuzzyTimeline.gui.plotDIVHeight-20);
 		} else{
 			$(fuzzyTimeline.gui.plotDiv).width("100%");
-			$(fuzzyTimeline.gui.plotDiv).height(fuzzyTimeline.gui.plotDIVHeight);
-		}
+		}
+		//leave place for the scrollbar
+		$(fuzzyTimeline.gui.plotDiv).height(fuzzyTimeline.gui.plotDIVHeight-20);
 		
 		//fit handles
 		//this does not make much sense, as the selections are _completely_ different
@@ -41599,7 +43388,7 @@
 				$(this.pieChartDiv).empty();
 				
 				//calculate height (flot NEEDS a height)				
-				var parentHeight = $(this.parent.gui.pieChartsDiv).outerHeight(true);// - $(this.parent.gui.columnSelectorDiv).outerHeight(true);//***lchang
+				var parentHeight = $(this.parent.gui.pieChartsDiv).outerHeight(true) - $(this.parent.gui.columnSelectorDiv).outerHeight(true);
 				var pieChartCount = 0;
 				$(this.parent.pieCharts).each(function(){
 					if (this instanceof PieChart)
@@ -41607,11 +43396,7 @@
 				});
 				var height = (parentHeight/pieChartCount) - $(this.informationDIV).outerHeight(true);
 				if (pieChart.options.restrictPieChartSize !== false)
-					height = Math.min(height, /*$(window).height()*/parentHeight * pieChart.options.restrictPieChartSize);//***lchang
-				if(height>300)
-					height=300;
-				if(height<150)
-					height=150;
+					height = Math.min(height, $(window).height() * pieChart.options.restrictPieChartSize);
 				$(this.pieChartDiv).height(height);
 	
 				$.plot($(this.pieChartDiv), chartData,
@@ -41761,7 +43546,7 @@
 	this.dialog.html("").dialog({modal: true}).dialog('open');
 
 	//to asure that the dialog is above (z-index of) the toolbars
-	$(".ui-front").css("z-index","20011");
+	$(".ui-front").css("z-index","10001");
 	
 	var allNumeric = this.loadValues(datasetIndex, columnName);	
 
@@ -42119,7 +43904,7 @@
 function PieChartConfig(options) {
 
 	this.options = {
-			restrictPieChartSize : 0.8, // restrict size to percantage of window size (false for no restriction)
+			restrictPieChartSize : 0.25, // restrict size to percantage of window size (false for no restriction)
 			localStoragePrefix : "GeoBrowser_PieChart_", // prefix for value name in LocalStorage
 			allowLocalStorage : true, //whether LocalStorage save and load should be allowed (and buttons shown) 
 	};
@@ -42259,9 +44044,6 @@
 	this.pieChartsDiv.id = "pieChartsDivID";
 	div.appendChild(this.pieChartsDiv);
 	$(this.pieChartsDiv).height("100%");
-	var h=$(div).height();
-	h-=30;
-	$(this.pieChartsDiv).height(h);
 };
 
 PieChartGui.prototype = {
@@ -43212,6 +44994,453 @@
 	},
 };
 /*
+* LineOverlay.js
+*
+* Copyright (c) 2013, Sebastian Kruse. All rights reserved.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 3 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+* MA 02110-1301  USA
+*/
+
+/**
+ * @class LineOverlay
+ * Implementation for an overlay showing lines between points 
+ * @author Sebastian Kruse (skruse@mpiwg-berlin.mpg.de)
+ *
+ * @param {HTML object} parent div to append the LineOverlay
+ */
+function LineOverlay(parent) {
+
+	this.lineOverlay = this;
+	
+	this.parent = parent;
+	this.options = parent.options;
+	this.attachedMapWidgets = parent.attachedMapWidgets;
+
+	this.overlays = [];
+
+	this.initialize();
+}
+
+LineOverlay.prototype = {
+
+	initialize : function() {
+	}
+};
+/*
+* LineOverlayConfig.js
+*
+* Copyright (c) 2013, Sebastian Kruse. All rights reserved.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 3 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+* MA 02110-1301  USA
+*/
+
+/**
+ * @class LineOverlayConfig
+ * LineOverlay Configuration File
+ * @author Sebastian Kruse (skruse@mpiwg-berlin.mpg.de)
+ */
+function LineOverlayConfig(options) {
+	this.options = {
+			showArrows : true,
+			showLines : "both", //which directions will be shown: "both", "inbound", "outbound"
+			onlyShowSelectedOrHighlighted : false, //only show lines in case of selection/highlight 
+	}
+
+	if ( typeof options != 'undefined') {
+		$.extend(this.options, options);
+	}
+
+};
+/*
+* LineOverlayWidget.js
+*
+* Copyright (c) 2013, Sebastian Kruse. All rights reserved.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 3 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+* MA 02110-1301  USA
+*/
+
+//calculate angle between line and x-axis
+//credits: geometricnet (http://geometricnet.sourceforge.net/examples/directions.html)
+bearing = function(x1,y1,x2,y2) {
+	b_x = 0;
+	b_y = 1;
+	a_x = x2 - x1;
+	a_y = y2 - y1;
+	angle_rad = Math.acos((a_x*b_x+a_y*b_y)/Math.sqrt(a_x*a_x+a_y*a_y)) ;
+	angle = 360/(2*Math.PI)*angle_rad;
+	if (a_x < 0) {
+	    return 360 - angle;
+	} else {
+	    return angle;
+	}
+};
+
+/**
+ * @class LineOverlayWidget
+ * Implementation for the widget interactions of an overlay showing lines between points
+ * @author Sebastian Kruse (skruse@mpiwg-berlin.mpg.de)
+ *
+ * @param {WidgetWrapper} core wrapper for interaction to other widgets
+ * @param {JSON} options user specified configuration that overwrites options in OverlayloaderConfig.js
+ */
+LineOverlayWidget = function (core, options) {
+
+	this.core = core;
+	this.core.setWidget(this);
+
+	this.options = (new LineOverlayConfig(options)).options;
+	
+	this.attachedMapWidgets = new Array();
+	
+	this.lineOverlay = new LineOverlay(this);
+	this.lines = [];
+	this.multiLineFeature;
+	
+	this.selected = [];
+}
+
+/**
+ * @param {Number} dataSet number of dataSet in dataSet array
+ * @param {Number} objectID number of DataObject in objects array
+ */
+
+function Line(objectStart, objectEnd ) {
+	this.objectStart = objectStart;
+	this.objectEnd = objectEnd;
+}
+
+LineOverlayWidget.prototype = {
+
+	initWidget : function() {
+		var lineOverlayWidget = this;
+		this.drawLines();
+	},
+
+	highlightChanged : function(objects) {
+		if( !GeoTemConfig.highlightEvents ){
+			return;
+		}
+		this.drawLines(GeoTemConfig.mergeObjects(objects,this.selected));
+	},
+
+	selectionChanged : function(selection) {
+		if( !GeoTemConfig.selectionEvents ){
+			return;
+		}
+		if (selection.valid())
+			this.selected = selection.objects;
+		else
+			this.selected = [];
+
+		this.drawLines(this.selected);
+	},
+
+	triggerHighlight : function(item) {
+	},
+
+	tableSelection : function() {
+	},
+
+	deselection : function() {
+	},
+
+	filtering : function() {
+	},
+
+	inverseFiltering : function() {
+	},
+
+	triggerRefining : function() {
+	},
+
+	reset : function() {
+	},
+	
+	//identical to the function in PieChartWidget
+	//here cause widgets may be used independed of each other
+	getElementData : function(dataObject, watchedColumn, selectionFunction) {
+		var columnData;
+		if (watchedColumn.indexOf("[") === -1){
+			columnData = dataObject[watchedColumn];
+			if (typeof columnData === "undefined"){
+				columnData = dataObject.tableContent[watchedColumn];
+			};
+		} else {
+			try {
+				var columnName = watchedColumn.split("[")[0];
+				var IndexAndAttribute = watchedColumn.split("[")[1];
+				if (IndexAndAttribute.indexOf("]") != -1){
+					var arrayIndex = IndexAndAttribute.split("]")[0];
+					var attribute = IndexAndAttribute.split("]")[1];
+					
+					if (typeof attribute === "undefined")
+						columnData = dataObject[columnName][arrayIndex];
+					else{
+						attribute = attribute.split(".")[1];
+						columnData = dataObject[columnName][arrayIndex][attribute];
+					}
+				}
+			} catch(e) {
+				if (typeof console !== undefined)
+					console.error(e);
+				
+				delete columnData;
+			}
+		}
+		
+		if ( (typeof columnData !== "undefined") && (typeof selectionFunction !== "undefined") )
+			columnData = selectionFunction(columnData);
+		
+		return(columnData);
+	},
+	
+	matchColumns : function(dataSet1, columnName1, dataSet2, columnName2) {
+		var lineOverlayWidget = this;
+		lineOverlayWidget.lines;
+		$(GeoTemConfig.datasets[dataSet1].objects).each(function(){
+			var object1 = this;
+			var data1 = lineOverlayWidget.getElementData(object1, columnName1);
+			//split because there could be multiple comma separated values 
+			data1 = data1.split(",");
+			
+			$(GeoTemConfig.datasets[dataSet2].objects).each(function(){
+				var object2 = this;
+				//avoid reflexive and double entries
+				if ((columnName1 === columnName2)&&(dataSet1 === dataSet2)&&(object1.index<=object2.index))
+					return;
+				var data2 = lineOverlayWidget.getElementData(object2, columnName2);
+				//split because there could be multiple comma separated values 
+				data2 = data2.split(",");
+				
+				//check if at least one pair matches
+				for(var i = 0; i < data1.length; i++ ){
+					var firstVal = data1[i];
+					if (data2.indexOf(firstVal) !== -1){
+						lineOverlayWidget.lines.push(new Line(object1, object2));
+						break;
+					}
+				}				
+			});
+		});
+	},
+	
+	getXYofObject : function(cs,dataObject){
+		//iterata over datasets
+		var x,y;
+		var found = false;
+		$(cs).each(function(){
+			//iterate over circles
+			$(this).each(function(){
+				var circle = this;
+				//iterata over objects in this circle;
+				var index = $.inArray(dataObject,circle.elements); 
+				if (index !== -1){
+					x = circle.feature.geometry.x;
+					y = circle.feature.geometry.y;
+					found = true;
+					return false;
+				}
+			});
+			//break loop
+			if (found === true)
+				return false;
+		});
+		
+		return ({x:x,y:y});
+	},
+	
+	/**
+	 * @param {DataObjects[][]} objects set of objects to limit to
+	 */
+	drawLines : function(objects) {
+		var flatObjects = [];
+		if (	(typeof objects !== "undefined") &&
+				(objects instanceof Array) &&
+				(objects.length > 0) ) {
+			$(objects).each(function(){
+				$.merge(flatObjects, this);				
+			});
+		}
+		var lineOverlayWidget = this;
+		
+		$(lineOverlayWidget.attachedMapWidgets).each(function(){
+			var mapWidget = this.mapWidget;
+			var lineLayer = this.lineLayer;
+
+			var map = mapWidget.openlayersMap;
+			var cs = mapWidget.mds.getObjectsByZoom();
+			
+			mapWidget.openlayersMap.setLayerIndex(lineLayer, 99);
+
+			lineLayer.removeAllFeatures();
+
+			var lineElements = [];
+			
+			var checkIfLineInPreset = function(){return false;};
+			if (lineOverlayWidget.options.showLines === "inbound"){
+				checkIfLineInPreset = function(objectStart,objectEnd,flatObjects){
+					return ($.inArray(objectEnd, flatObjects) === -1);
+				};
+			} else if (lineOverlayWidget.options.showLines === "outbound"){
+				checkIfLineInPreset = function(objectStart,objectEnd,flatObjects){
+					return ($.inArray(objectStart, flatObjects) === -1);
+				};
+			} else /*if (lineOverlayWidget.options.showLines === "both")*/{
+				checkIfLineInPreset = function(objectStart,objectEnd,flatObjects){
+					return (	($.inArray(objectStart, flatObjects) === -1) &&
+								($.inArray(objectEnd, flatObjects) === -1) );
+				};
+			}
+			
+			$(lineOverlayWidget.lines).each(function(){
+				var line = this;
+				
+				if ((lineOverlayWidget.options.onlyShowSelectedOrHighlighted === true) || (flatObjects.length > 0)){
+					//if objects are limited, check whether start or end are within 
+					if (checkIfLineInPreset(line.objectStart, line.objectEnd, flatObjects))
+						return;
+				}
+				//get XY-val of start Object
+				var xyStart = lineOverlayWidget.getXYofObject(cs, line.objectStart);
+				//continue if no valid XY-coords where found
+				if ( (typeof xyStart.x === "undefined") && (typeof xyStart.y === "undefined") )
+					return;
+				var xyEnd = lineOverlayWidget.getXYofObject(cs, line.objectEnd);
+				//continue if no valid XY-coords where found
+				if ( (typeof xyEnd.x === "undefined") && (typeof xyEnd.y === "undefined") )
+					return;
+
+				//do not draw 0-length lines (from same circle)
+				if ( (xyStart.x === xyEnd.x) && (xyStart.y === xyEnd.y) )
+					return;
+
+				var points = new Array(
+						   new OpenLayers.Geometry.Point(xyStart.x, xyStart.y),
+						   new OpenLayers.Geometry.Point(xyEnd.x, xyEnd.y)
+						);
+
+				var line = new OpenLayers.Geometry.LineString(points);
+
+				//Only draw each line once. Unfortunately this check is faster
+				//than drawing multiple lines.
+				var found = false;
+				$(lineElements).each(function(){
+					var checkLine = this.line;
+					if ((	(checkLine.components[0].x === line.components[0].x) &&
+							(checkLine.components[0].y === line.components[0].y) &&
+							(checkLine.components[1].x === line.components[1].x) &&
+							(checkLine.components[1].y === line.components[1].y) ) ||
+						// if lines are "directional" (arrows) the opposite one isn't the same anymore!
+						(	(lineOverlayWidget.options.showArrows === false) &&
+							(checkLine.components[0].x === line.components[1].x) &&
+							(checkLine.components[0].y === line.components[1].y) &&
+							(checkLine.components[1].x === line.components[0].x) &&
+							(checkLine.components[1].y === line.components[0].y) ) ){
+						found = true;
+						//increase width of this line
+						this.width++;
+						//and don't draw it again
+						return false;
+					}
+				});
+				
+				if (found === true)
+					return;
+
+				lineElements.push({line:line,width:1});
+			});
+
+			$(lineElements).each(function(){ 
+				var line = this.line;
+				var width = this.width;
+				
+				if (lineOverlayWidget.options.showArrows === true){
+					var xyStart = line.components[0];
+					var xyEnd = line.components[1];
+				    var arrowFeature = new OpenLayers.Feature.Vector(
+						new OpenLayers.Geometry.Point(xyEnd.x-((xyEnd.x-xyStart.x)*0.03), xyEnd.y-((xyEnd.y-xyStart.y)*0.03)), 
+						{
+							type: "triangle",
+							angle: bearing(xyStart.x,xyStart.y,xyEnd.x,xyEnd.y),
+							width: width+1
+						}
+					);
+					lineLayer.addFeatures(arrowFeature);
+				}
+
+				var lineFeature = new OpenLayers.Feature.Vector(line,{width:width});
+				lineLayer.addFeatures(lineFeature);
+			});
+		});
+	},
+	
+	attachMapWidget : function(mapWidget) {
+	    var styles = new OpenLayers.StyleMap({
+	        "default": {
+	            graphicName: "${type}",
+	            rotation: "${angle}",
+	            pointRadius: "${width}",
+	            strokeColor: '#0000ff', 
+	            strokeOpacity: 0.5,
+	            strokeWidth: "${width}",
+	            fillOpacity: 1
+	        }
+	    });
+	    
+		var lineOverlayWidget = this;
+		var lineLayer = new OpenLayers.Layer.Vector("Line Layer", {
+	        styleMap: styles,
+	        isBaseLayer:false
+	    });
+		mapWidget.openlayersMap.addLayer(lineLayer);
+		mapWidget.openlayersMap.setLayerIndex(lineLayer, 99);
+		this.attachedMapWidgets.push({mapWidget:mapWidget,lineLayer:lineLayer});
+		//register zoom event
+		mapWidget.openlayersMap.events.register("zoomend", lineOverlayWidget, function(){
+			this.drawLines(this.selected);
+		});
+	}
+};
+/*
 * DataObject.js
 *
 * Copyright (c) 2012, Stefan Jänicke. All rights reserved.
@@ -43588,500 +45817,500 @@
 	Publisher.Publish('dataobjectAfterCreation', this);
 };
 
-/*
-* Dataset.js
-*
-* Copyright (c) 2012, Stefan Jänicke. All rights reserved.
-*
-* This library is free software; you can redistribute it and/or
-* modify it under the terms of the GNU Lesser General Public
-* License as published by the Free Software Foundation; either
-* version 3 of the License, or (at your option) any later version.
-*
-* This library is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-* Lesser General Public License for more details.
-*
-* You should have received a copy of the GNU Lesser General Public
-* License along with this library; if not, write to the Free Software
-* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
-* MA 02110-1301  USA
-*/
-
-/**
- * @class Dataset
- * GeoTemCo's Dataset class
- * @author Stefan Jänicke (stjaenicke@informatik.uni-leipzig.de)
- * @release 1.0
- * @release date: 2012-07-27
- * @version date: 2012-07-27
- *
- * @param {Array} objects data item arrays from different datasets
- * @param {String} label label for the datasets
- */
-Dataset = function(objects, label, url, type) {
-
-	this.objects = objects;
-	this.label = label;
-	this.url = url;
-	this.type = type;
-	
-	this.color;
-	
-	//if the user can change shapes, every dataset needs a default shape
-	if (GeoTemConfig.allowUserShapeAndColorChange){
-		this.graphic={
-				shape: "circle",
-				rotation: 0
-		}
-	}
-	
-	Publisher.Publish('datasetAfterCreation', this);
-}
-/*
-* TimeDataSource.js
-*
-* Copyright (c) 2012, Stefan Jänicke. All rights reserved.
-*
-* This library is free software; you can redistribute it and/or
-* modify it under the terms of the GNU Lesser General Public
-* License as published by the Free Software Foundation; either
-* version 3 of the License, or (at your option) any later version.
-*
-* This library is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-* Lesser General Public License for more details.
-*
-* You should have received a copy of the GNU Lesser General Public
-* License along with this library; if not, write to the Free Software
-* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
-* MA 02110-1301  USA
-*/
-
-/**
- * @class TimeDataSource, TimeSlice, TimeStack
- * implementation for aggregation of time items
- * @author Stefan Jänicke (stjaenicke@informatik.uni-leipzig.de)
- * @release 1.0
- * @release date: 2012-07-27
- * @version date: 2012-07-27
- *
- * @param {JSON} options time configuration
- */
-function TimeDataSource(options) {
-
-	this.options = options;
-	this.timeSlices = [];
-	this.unit
-	this.minDate
-	this.maxDate
-	this.eventSources
-	this.events
-	this.leftSlice
-	this.rightSlice
-
-	this.hashMapping
-
-};
-
-TimeDataSource.prototype = {
-
-	findTimeUnits : function(granularity, timeUnit, pixels) {
-
-		var time = SimileAjax.DateTime;
-		this.availableUnits = [];
-		var givenUnits = SimileAjax.DateTime.gregorianUnitLengths;
-		for (var i = 0; i < givenUnits.length; i++) {
-			if (granularity > i) {
-				continue;
-			}
-			var slices = 0;
-			var t = new Date(this.minDate.getTime());
-			do {
-				time.roundDownToInterval(t, i, undefined, 1, 0);
-				slices++;
-				time.incrementByInterval(t, i, undefined);
-			} while( t.getTime() <= this.maxDate.getTime() && slices < pixels+2 );
-			if (slices > 0 && slices <= pixels) {
-				this.availableUnits.push({
-					unit : i,
-					slices : slices,
-					label : SimileAjax.DateTime.Strings[GeoTemConfig.language][i]
-				});
-			}
-		}
-		var unitDiff200 = pixels + 1;
-		for (var i = 0; i < this.availableUnits.length; i++) {
-			var diff = Math.abs(this.availableUnits[i].slices - 200);
-			if (diff < unitDiff200) {
-				unitDiff200 = diff;
-				this.unit = this.availableUnits[i].unit;
-			}
-		}
-
-	},
-
-	getUnitIndex : function() {
-		for (var i = 0; i < this.availableUnits.length; i++) {
-			if (this.unit == this.availableUnits[i].unit) {
-				return i;
-			}
-		}
-		return 0;
-	},
-
-	setTimeUnit : function(unit) {
-		this.unit = unit;
-		this.initializeSlices();
-	},
-
-	/**
-	 * initializes the TimeDataSource
-	 * @param {Timeplot.ColumnSource[]} dataSources the column sources corresponding to the data sets
-	 * @param {Timeplot.DefaultEventSource[]} eventSources the event sources corresponding to the column sources
-	 * @param {TimeObject[][]} timeObjects an array of time objects of different sets
-	 * @param {SimileAjax.DateTime} granularity the time granularity of the given data
-	 */
-	initialize : function(dataSources, eventSources, timeObjects, granularity, timeUnit, pixels) {
-
-		this.dataSources = dataSources;
-		this.eventSources = eventSources;
-		this.timeObjects = timeObjects;
-
-		this.minDate = undefined;
-		this.maxDate = undefined;
-		this.hashMapping = [];
-		this.projHashMapping = [];
-
-		for (var i = 0; i < timeObjects.length; i++) {
-			this.hashMapping.push([]);
-			this.projHashMapping.push([]);
-			for (var j = 0; j < timeObjects[i].length; j++) {
-				var o = timeObjects[i][j];
-				if (o.isTemporal) {
-					var g = o.dates[this.options.timeIndex].granularity;
-					//o.getTimeGranularity(this.options.timeIndex);
-					if (g == null) {
-						continue;
-					}
-					var time = o.dates[this.options.timeIndex].date;
-					//o.getDate(this.options.timeIndex);
-					if (this.minDate == undefined || time.getTime() < this.minDate.getTime()) {
-						this.minDate = time;
-					}
-					if (this.maxDate == undefined || time.getTime() > this.maxDate.getTime()) {
-						this.maxDate = time;
-					}
-				}
-			}
-		}
-
-		if (this.minDate == undefined) {
-			this.minDate = this.options.defaultMinDate;
-			this.maxDate = this.options.defaultMaxDate;
-		}
-
-		this.findTimeUnits(granularity, timeUnit, pixels);
-		this.initializeSlices();
-
-	},
-
-	initializeSlices : function() {
-		for (var i = 0; i < this.dataSources.length; i++) {
-			this.dataSources[i]._range = {
-				earliestDate : null,
-				latestDate : null,
-				min : 0,
-				max : 0
-			};
-		}
-		this.timeSlices = [];
-		var time = SimileAjax.DateTime;
-		var t = new Date(this.minDate.getTime() - 0.9 * time.gregorianUnitLengths[this.unit]);
-		do {
-			time.roundDownToInterval(t, this.unit, undefined, 1, 0);
-			var slice = new TimeSlice(SimileAjax.NativeDateUnit.cloneValue(t), this.timeObjects.length, this.dataSources.length);
-			this.timeSlices.push(slice);
-			time.incrementByInterval(t, this.unit, undefined);
-		} while (t.getTime() <= this.maxDate.getTime() + 1.1 * time.gregorianUnitLengths[this.unit]);
-
-		for (var i = 0; i < this.timeObjects.length; i++) {
-			var projId = i;
-			if( this.dataSources.length == 1 ){
-				projId = 0;
-			}
-			for (var j = 0; j < this.timeObjects[i].length; j++) {
-				var o = this.timeObjects[i][j];
-				if (o.isTemporal) {
-					var date = o.dates[this.options.timeIndex].date;
-					//o.getDate(this.options.timeIndex);
-					for (var k = 0; k < this.timeSlices.length - 1; k++) {
-						var t1 = this.timeSlices[k].date.getTime();
-						var t2 = this.timeSlices[k + 1].date.getTime();
-						var stack = null, projStack = null;
-						if (date >= t1 && date < t2) {
-							stack = this.timeSlices[k].getStack(i);
-							projStack = this.timeSlices[k].getProjStack(projId);
-						}
-						if (k == this.timeSlices.length - 2 && date >= t2) {
-							stack = this.timeSlices[k + 1].getStack(i);
-							projStack = this.timeSlices[k + 1].getProjStack(projId);
-						}
-						if (stack != null) {
-							stack.addObject(o);
-							projStack.addObject(o);
-							this.hashMapping[i][o.index] = stack;
-							this.projHashMapping[i][o.index] = projStack;
-							break;
-						}
-					}
-				}
-			}
-		}
-
-		this.events = [];
-		for (var i = 0; i < this.eventSources.length; i++) {
-			var eventSet = [];
-			for (var j = 0; j < this.timeSlices.length; j++) {
-				var value = new Array("" + this.timeSlices[j].projStacks[i].value);
-				eventSet.push({
-					date : this.timeSlices[j].date,
-					value : value
-				});
-			}
-			this.eventSources[i].loadData(eventSet);
-			this.events.push(eventSet);
-		}
-
-		this.leftSlice = 0;
-		this.rightSlice = this.timeSlices.length - 1;
-
-	},
-
-	getSliceNumber : function() {
-		return this.timeSlices.length;
-	},
-
-	/**
-	 * computes the slice index corresponding to a given time
-	 * @param {Date} time the given time
-	 * @return the corresponding slice index
-	 */
-	getSliceIndex : function(time) {
-		for (var i = 0; i < this.timeSlices.length; i++) {
-			if (time == this.timeSlices[i].date) {
-				return i;
-			}
-		}
-	},
-
-	/**
-	 * returns the time of a specific time slice
-	 * @param {int} time the given slice index
-	 * @return the corresponding slice date
-	 */
-	getSliceTime : function(index) {
-		return this.timeSlices[index].date;
-	},
-
-	/**
-	 * shifts the actual zoomed range
-	 * @param {int} delta the value to shift (negative for left shift, positive for right shift)
-	 * @return boolean value, if the range could be shifted
-	 */
-	setShift : function(delta) {
-		if (delta == 1 && this.leftSlice != 0) {
-			this.leftSlice--;
-			this.rightSlice--;
-			return true;
-		} else if (delta == -1 && this.rightSlice != this.timeSlices.length - 1) {
-			this.leftSlice++;
-			this.rightSlice++;
-			return true;
-		} else {
-			return false;
-		}
-	},
-
-	/**
-	 * zooms the actual range
-	 * @param {int} delta the value to zoom (negative for zoom out, positive for zoom in)
-	 * @param {Date} time the corresponding time of the actual mouse position on the plot
-	 * @param {Date} leftTime the time of the left border of a selected timerange or null
-	 * @param {Date} rightTime the time of the right border of a selected timerange or null
-	 * @return boolean value, if the range could be zoomed
-	 */
-	setZoom : function(delta, time, leftTime, rightTime) {
-		var n1 = 0;
-		var n2 = 0;
-		var m = -1;
-		if (delta > 0) {
-			m = 1;
-			if (leftTime != null) {
-				n1 = this.getSliceIndex(leftTime) - this.leftSlice;
-				n2 = this.rightSlice - this.getSliceIndex(rightTime);
-			} else {
-				slice = this.getSliceIndex(time);
-				if (slice == this.leftSlice || slice == this.rightSlice) {
-					return;
-				}
-				n1 = slice - 1 - this.leftSlice;
-				n2 = this.rightSlice - slice - 1;
-			}
-		} else if (delta < 0) {
-
-			n1 = this.leftSlice;
-			n2 = this.timeSlices.length - 1 - this.rightSlice;
-		}
-
-		var zoomSlices = 2 * delta;
-		if (Math.abs(n1 + n2) < Math.abs(zoomSlices)) {
-			zoomSlices = n1 + n2;
-		}
-
-		if (n1 + n2 == 0) {
-			return false;
-		}
-
-		var m1 = Math.round(n1 / (n1 + n2) * zoomSlices);
-		var m2 = zoomSlices - m1;
-
-		this.leftSlice += m1;
-		this.rightSlice -= m2;
-
-		return true;
-	},
-
-	/**
-	 * resets the plots by loading data of actual zoomed range
-	 */
-	reset : function(timeGeometry) {
-		for (var i = 0; i < this.eventSources.length; i++) {
-			this.eventSources[i].loadData(this.events[i].slice(this.leftSlice, this.rightSlice + 1));
-			if (i + 1 < this.eventSources.length) {
-				timeGeometry._earliestDate = null;
-				timeGeometry._latestDate = null;
-			}
-
-		}
-	},
-
-	/**
-	 * Getter for actual zoom
-	 * @return actual zoom value
-	 */
-	getZoom : function() {
-		if (this.timeSlices == undefined) {
-			return 0;
-		}
-		return Math.round((this.timeSlices.length - 3) / 2) - Math.round((this.rightSlice - this.leftSlice - 2) / 2);
-	},
-
-	/**
-	 * Getter for date of the first timeslice
-	 * @return date of the first timeslice
-	 */
-	earliest : function() {
-		return this.timeSlices[0].date;
-	},
-
-	/**
-	 * Getter for date of the last timeslice
-	 * @return date of the last timeslice
-	 */
-	latest : function() {
-		return this.timeSlices[this.timeSlices.length - 1].date;
-	},
-
-	setOverlay : function(timeObjects) {
-		for (var i = 0; i < this.timeSlices.length; i++) {
-			this.timeSlices[i].reset();
-		}
-		for (var j in timeObjects ) {
-			for (var k in timeObjects[j] ) {
-				var o = timeObjects[j][k];
-				if (o.isTemporal) {
-					if (o.getTimeGranularity(this.options.timeIndex) == null) {
-						continue;
-					}
-					this.hashMapping[j][o.index].overlay += o.weight;
-					this.projHashMapping[j][o.index].overlay += o.weight;
-				}
-			}
-		}
-	},
-
-	size : function() {
-		if (this.timeSlices.length == 0) {
-			return 0;
-		}
-		return this.timeSlices[0].stacks.length;
-	}
-};
-
-/**
- * small class that represents a time slice of the actual timeplot.
- * it has a specific date and contains its corrsponding data objects as well
- */
-function TimeSlice(date, rows, projRows) {
-
-	this.date = date;
-	this.selected = false;
-
-	this.stacks = [];
-	this.projStacks = [];
-	for (var i = 0; i < rows; i++) {
-		this.stacks.push(new TimeStack());
-	}
-	for (var i = 0; i < projRows; i++) {
-		this.projStacks.push(new TimeStack());
-	}
-
-	this.getStack = function(row) {
-		return this.stacks[row];
-	};
-
-	this.getProjStack = function(row) {
-		return this.projStacks[row];
-	};
-
-	this.reset = function() {
-		for (var i in this.projStacks ) {
-			this.stacks[i].overlay = 0;
-			this.projStacks[i].overlay = 0;
-		}
-	};
-
-	this.overlay = function() {
-		var value = 0;
-		for (var i in this.projStacks ) {
-			if (this.projStacks[i].overlay > value) {
-				value = this.projStacks[i].overlay;
-			}
-		}
-		return value;
-	};
-
-};
-
-/**
- * small class that represents a stack for a time slice which
- * holds items for different datasets for the specific time range
- */
-function TimeStack() {
-
-	this.overlay = 0;
-	this.value = 0;
-	this.elements = [];
-
-	this.addObject = function(object) {
-		this.elements.push(object);
-		this.value += object.weight;
-	};
-
-};
+/*
+* Dataset.js
+*
+* Copyright (c) 2012, Stefan Jänicke. All rights reserved.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 3 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+* MA 02110-1301  USA
+*/
+
+/**
+ * @class Dataset
+ * GeoTemCo's Dataset class
+ * @author Stefan Jänicke (stjaenicke@informatik.uni-leipzig.de)
+ * @release 1.0
+ * @release date: 2012-07-27
+ * @version date: 2012-07-27
+ *
+ * @param {Array} objects data item arrays from different datasets
+ * @param {String} label label for the datasets
+ */
+Dataset = function(objects, label, url, type) {
+
+	this.objects = objects;
+	this.label = label;
+	this.url = url;
+	this.type = type;
+	
+	this.color;
+	
+	//if the user can change shapes, every dataset needs a default shape
+	if (GeoTemConfig.allowUserShapeAndColorChange){
+		this.graphic={
+				shape: "circle",
+				rotation: 0
+		}
+	}
+	
+	Publisher.Publish('datasetAfterCreation', this);
+}
+/*
+* TimeDataSource.js
+*
+* Copyright (c) 2012, Stefan Jänicke. All rights reserved.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 3 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+* MA 02110-1301  USA
+*/
+
+/**
+ * @class TimeDataSource, TimeSlice, TimeStack
+ * implementation for aggregation of time items
+ * @author Stefan Jänicke (stjaenicke@informatik.uni-leipzig.de)
+ * @release 1.0
+ * @release date: 2012-07-27
+ * @version date: 2012-07-27
+ *
+ * @param {JSON} options time configuration
+ */
+function TimeDataSource(options) {
+
+	this.options = options;
+	this.timeSlices = [];
+	this.unit
+	this.minDate
+	this.maxDate
+	this.eventSources
+	this.events
+	this.leftSlice
+	this.rightSlice
+
+	this.hashMapping
+
+};
+
+TimeDataSource.prototype = {
+
+	findTimeUnits : function(granularity, timeUnit, pixels) {
+
+		var time = SimileAjax.DateTime;
+		this.availableUnits = [];
+		var givenUnits = SimileAjax.DateTime.gregorianUnitLengths;
+		for (var i = 0; i < givenUnits.length; i++) {
+			if (granularity > i) {
+				continue;
+			}
+			var slices = 0;
+			var t = new Date(this.minDate.getTime());
+			do {
+				time.roundDownToInterval(t, i, undefined, 1, 0);
+				slices++;
+				time.incrementByInterval(t, i, undefined);
+			} while( t.getTime() <= this.maxDate.getTime() && slices < pixels+2 );
+			if (slices > 0 && slices <= pixels) {
+				this.availableUnits.push({
+					unit : i,
+					slices : slices,
+					label : SimileAjax.DateTime.Strings[GeoTemConfig.language][i]
+				});
+			}
+		}
+		var unitDiff200 = pixels + 1;
+		for (var i = 0; i < this.availableUnits.length; i++) {
+			var diff = Math.abs(this.availableUnits[i].slices - 200);
+			if (diff < unitDiff200) {
+				unitDiff200 = diff;
+				this.unit = this.availableUnits[i].unit;
+			}
+		}
+
+	},
+
+	getUnitIndex : function() {
+		for (var i = 0; i < this.availableUnits.length; i++) {
+			if (this.unit == this.availableUnits[i].unit) {
+				return i;
+			}
+		}
+		return 0;
+	},
+
+	setTimeUnit : function(unit) {
+		this.unit = unit;
+		this.initializeSlices();
+	},
+
+	/**
+	 * initializes the TimeDataSource
+	 * @param {Timeplot.ColumnSource[]} dataSources the column sources corresponding to the data sets
+	 * @param {Timeplot.DefaultEventSource[]} eventSources the event sources corresponding to the column sources
+	 * @param {TimeObject[][]} timeObjects an array of time objects of different sets
+	 * @param {SimileAjax.DateTime} granularity the time granularity of the given data
+	 */
+	initialize : function(dataSources, eventSources, timeObjects, granularity, timeUnit, pixels) {
+
+		this.dataSources = dataSources;
+		this.eventSources = eventSources;
+		this.timeObjects = timeObjects;
+
+		this.minDate = undefined;
+		this.maxDate = undefined;
+		this.hashMapping = [];
+		this.projHashMapping = [];
+
+		for (var i = 0; i < timeObjects.length; i++) {
+			this.hashMapping.push([]);
+			this.projHashMapping.push([]);
+			for (var j = 0; j < timeObjects[i].length; j++) {
+				var o = timeObjects[i][j];
+				if (o.isTemporal) {
+					var g = o.dates[this.options.timeIndex].granularity;
+					//o.getTimeGranularity(this.options.timeIndex);
+					if (g == null) {
+						continue;
+					}
+					var time = o.dates[this.options.timeIndex].date;
+					//o.getDate(this.options.timeIndex);
+					if (this.minDate == undefined || time.getTime() < this.minDate.getTime()) {
+						this.minDate = time;
+					}
+					if (this.maxDate == undefined || time.getTime() > this.maxDate.getTime()) {
+						this.maxDate = time;
+					}
+				}
+			}
+		}
+
+		if (this.minDate == undefined) {
+			this.minDate = this.options.defaultMinDate;
+			this.maxDate = this.options.defaultMaxDate;
+		}
+
+		this.findTimeUnits(granularity, timeUnit, pixels);
+		this.initializeSlices();
+
+	},
+
+	initializeSlices : function() {
+		for (var i = 0; i < this.dataSources.length; i++) {
+			this.dataSources[i]._range = {
+				earliestDate : null,
+				latestDate : null,
+				min : 0,
+				max : 0
+			};
+		}
+		this.timeSlices = [];
+		var time = SimileAjax.DateTime;
+		var t = new Date(this.minDate.getTime() - 0.9 * time.gregorianUnitLengths[this.unit]);
+		do {
+			time.roundDownToInterval(t, this.unit, undefined, 1, 0);
+			var slice = new TimeSlice(SimileAjax.NativeDateUnit.cloneValue(t), this.timeObjects.length, this.dataSources.length);
+			this.timeSlices.push(slice);
+			time.incrementByInterval(t, this.unit, undefined);
+		} while (t.getTime() <= this.maxDate.getTime() + 1.1 * time.gregorianUnitLengths[this.unit]);
+
+		for (var i = 0; i < this.timeObjects.length; i++) {
+			var projId = i;
+			if( this.dataSources.length == 1 ){
+				projId = 0;
+			}
+			for (var j = 0; j < this.timeObjects[i].length; j++) {
+				var o = this.timeObjects[i][j];
+				if (o.isTemporal) {
+					var date = o.dates[this.options.timeIndex].date;
+					//o.getDate(this.options.timeIndex);
+					for (var k = 0; k < this.timeSlices.length - 1; k++) {
+						var t1 = this.timeSlices[k].date.getTime();
+						var t2 = this.timeSlices[k + 1].date.getTime();
+						var stack = null, projStack = null;
+						if (date >= t1 && date < t2) {
+							stack = this.timeSlices[k].getStack(i);
+							projStack = this.timeSlices[k].getProjStack(projId);
+						}
+						if (k == this.timeSlices.length - 2 && date >= t2) {
+							stack = this.timeSlices[k + 1].getStack(i);
+							projStack = this.timeSlices[k + 1].getProjStack(projId);
+						}
+						if (stack != null) {
+							stack.addObject(o);
+							projStack.addObject(o);
+							this.hashMapping[i][o.index] = stack;
+							this.projHashMapping[i][o.index] = projStack;
+							break;
+						}
+					}
+				}
+			}
+		}
+
+		this.events = [];
+		for (var i = 0; i < this.eventSources.length; i++) {
+			var eventSet = [];
+			for (var j = 0; j < this.timeSlices.length; j++) {
+				var value = new Array("" + this.timeSlices[j].projStacks[i].value);
+				eventSet.push({
+					date : this.timeSlices[j].date,
+					value : value
+				});
+			}
+			this.eventSources[i].loadData(eventSet);
+			this.events.push(eventSet);
+		}
+
+		this.leftSlice = 0;
+		this.rightSlice = this.timeSlices.length - 1;
+
+	},
+
+	getSliceNumber : function() {
+		return this.timeSlices.length;
+	},
+
+	/**
+	 * computes the slice index corresponding to a given time
+	 * @param {Date} time the given time
+	 * @return the corresponding slice index
+	 */
+	getSliceIndex : function(time) {
+		for (var i = 0; i < this.timeSlices.length; i++) {
+			if (time == this.timeSlices[i].date) {
+				return i;
+			}
+		}
+	},
+
+	/**
+	 * returns the time of a specific time slice
+	 * @param {int} time the given slice index
+	 * @return the corresponding slice date
+	 */
+	getSliceTime : function(index) {
+		return this.timeSlices[index].date;
+	},
+
+	/**
+	 * shifts the actual zoomed range
+	 * @param {int} delta the value to shift (negative for left shift, positive for right shift)
+	 * @return boolean value, if the range could be shifted
+	 */
+	setShift : function(delta) {
+		if (delta == 1 && this.leftSlice != 0) {
+			this.leftSlice--;
+			this.rightSlice--;
+			return true;
+		} else if (delta == -1 && this.rightSlice != this.timeSlices.length - 1) {
+			this.leftSlice++;
+			this.rightSlice++;
+			return true;
+		} else {
+			return false;
+		}
+	},
+
+	/**
+	 * zooms the actual range
+	 * @param {int} delta the value to zoom (negative for zoom out, positive for zoom in)
+	 * @param {Date} time the corresponding time of the actual mouse position on the plot
+	 * @param {Date} leftTime the time of the left border of a selected timerange or null
+	 * @param {Date} rightTime the time of the right border of a selected timerange or null
+	 * @return boolean value, if the range could be zoomed
+	 */
+	setZoom : function(delta, time, leftTime, rightTime) {
+		var n1 = 0;
+		var n2 = 0;
+		var m = -1;
+		if (delta > 0) {
+			m = 1;
+			if (leftTime != null) {
+				n1 = this.getSliceIndex(leftTime) - this.leftSlice;
+				n2 = this.rightSlice - this.getSliceIndex(rightTime);
+			} else {
+				slice = this.getSliceIndex(time);
+				if (slice == this.leftSlice || slice == this.rightSlice) {
+					return;
+				}
+				n1 = slice - 1 - this.leftSlice;
+				n2 = this.rightSlice - slice - 1;
+			}
+		} else if (delta < 0) {
+
+			n1 = this.leftSlice;
+			n2 = this.timeSlices.length - 1 - this.rightSlice;
+		}
+
+		var zoomSlices = 2 * delta;
+		if (Math.abs(n1 + n2) < Math.abs(zoomSlices)) {
+			zoomSlices = n1 + n2;
+		}
+
+		if (n1 + n2 == 0) {
+			return false;
+		}
+
+		var m1 = Math.round(n1 / (n1 + n2) * zoomSlices);
+		var m2 = zoomSlices - m1;
+
+		this.leftSlice += m1;
+		this.rightSlice -= m2;
+
+		return true;
+	},
+
+	/**
+	 * resets the plots by loading data of actual zoomed range
+	 */
+	reset : function(timeGeometry) {
+		for (var i = 0; i < this.eventSources.length; i++) {
+			this.eventSources[i].loadData(this.events[i].slice(this.leftSlice, this.rightSlice + 1));
+			if (i + 1 < this.eventSources.length) {
+				timeGeometry._earliestDate = null;
+				timeGeometry._latestDate = null;
+			}
+
+		}
+	},
+
+	/**
+	 * Getter for actual zoom
+	 * @return actual zoom value
+	 */
+	getZoom : function() {
+		if (this.timeSlices == undefined) {
+			return 0;
+		}
+		return Math.round((this.timeSlices.length - 3) / 2) - Math.round((this.rightSlice - this.leftSlice - 2) / 2);
+	},
+
+	/**
+	 * Getter for date of the first timeslice
+	 * @return date of the first timeslice
+	 */
+	earliest : function() {
+		return this.timeSlices[0].date;
+	},
+
+	/**
+	 * Getter for date of the last timeslice
+	 * @return date of the last timeslice
+	 */
+	latest : function() {
+		return this.timeSlices[this.timeSlices.length - 1].date;
+	},
+
+	setOverlay : function(timeObjects) {
+		for (var i = 0; i < this.timeSlices.length; i++) {
+			this.timeSlices[i].reset();
+		}
+		for (var j in timeObjects ) {
+			for (var k in timeObjects[j] ) {
+				var o = timeObjects[j][k];
+				if (o.isTemporal) {
+					if (o.getTimeGranularity(this.options.timeIndex) == null) {
+						continue;
+					}
+					this.hashMapping[j][o.index].overlay += o.weight;
+					this.projHashMapping[j][o.index].overlay += o.weight;
+				}
+			}
+		}
+	},
+
+	size : function() {
+		if (this.timeSlices.length == 0) {
+			return 0;
+		}
+		return this.timeSlices[0].stacks.length;
+	}
+};
+
+/**
+ * small class that represents a time slice of the actual timeplot.
+ * it has a specific date and contains its corrsponding data objects as well
+ */
+function TimeSlice(date, rows, projRows) {
+
+	this.date = date;
+	this.selected = false;
+
+	this.stacks = [];
+	this.projStacks = [];
+	for (var i = 0; i < rows; i++) {
+		this.stacks.push(new TimeStack());
+	}
+	for (var i = 0; i < projRows; i++) {
+		this.projStacks.push(new TimeStack());
+	}
+
+	this.getStack = function(row) {
+		return this.stacks[row];
+	};
+
+	this.getProjStack = function(row) {
+		return this.projStacks[row];
+	};
+
+	this.reset = function() {
+		for (var i in this.projStacks ) {
+			this.stacks[i].overlay = 0;
+			this.projStacks[i].overlay = 0;
+		}
+	};
+
+	this.overlay = function() {
+		var value = 0;
+		for (var i in this.projStacks ) {
+			if (this.projStacks[i].overlay > value) {
+				value = this.projStacks[i].overlay;
+			}
+		}
+		return value;
+	};
+
+};
+
+/**
+ * small class that represents a stack for a time slice which
+ * holds items for different datasets for the specific time range
+ */
+function TimeStack() {
+
+	this.overlay = 0;
+	this.value = 0;
+	this.elements = [];
+
+	this.addObject = function(object) {
+		this.elements.push(object);
+		this.value += object.weight;
+	};
+
+};
 /*
 * Binning.js
 *
@@ -44950,950 +47179,950 @@
 		return this.hashMapping[zoom][index][id];
 	}
 };
-/*
-* Clustering.js
-*
-* Copyright (c) 2012, Stefan Jänicke. All rights reserved.
-*
-* This library is free software; you can redistribute it and/or
-* modify it under the terms of the GNU Lesser General Public
-* License as published by the Free Software Foundation; either
-* version 3 of the License, or (at your option) any later version.
-*
-* This library is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-* Lesser General Public License for more details.
-*
-* You should have received a copy of the GNU Lesser General Public
-* License along with this library; if not, write to the Free Software
-* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
-* MA 02110-1301  USA
-*/
-
-/**
- * @class Vertex, Edge, Triangle, Clustering, BinaryHeap
- * Dynamic Delaunay clustering algorithm (see GeoTemCo paper)
- * @author Stefan Jänicke (stjaenicke@informatik.uni-leipzig.de)
- * @release 1.0
- * @release date: 2012-07-27
- * @version date: 2012-07-27
- */
-
-function Vertex(x, y, categories, binning) {
-	this.x = x;
-	this.y = y;
-	this.radius
-	this.size = 0;
-	this.elements = [];
-	this.radii = [];
-	this.weights = [];
-	this.legal = true;
-	this.binning = binning;
-	if (categories != undefined) {
-		for (var i = 0; i < categories; i++) {
-			this.elements.push([]);
-			this.weights.push(0);
-		}
-	}
-}
-
-Vertex.prototype.merge = function(v0, v1) {
-	for (var i = 0; i < v0.elements.length; i++) {
-		this.elements[i] = v0.elements[i].concat(v1.elements[i]);
-		this.weights[i] = v0.weights[i] + v1.weights[i];
-		this.size += this.weights[i];
-	}
-}
-
-Vertex.prototype.CalculateRadius = function(resolution) {
-	this.radii = [];
-	for (i in this.elements ) {
-		this.radii.push(this.binning.getRadius(this.weights[i]));
-	}
-	if (this.radii.length == 1) {
-		this.radius = this.radii[0] * resolution;
-	} else {
-		var count = 0;
-		var max1 = 0;
-		var max2 = 0;
-		for (i in this.radii ) {
-			if (this.radii[i] != 0) {
-				count++;
-			}
-			if (this.radii[i] > max1) {
-				if (max1 > max2) {
-					max2 = max1;
-				}
-				max1 = this.radii[i];
-			} else if (this.radii[i] > max2) {
-				max2 = this.radii[i];
-			}
-		}
-		if (count == 1) {
-			this.radius = max1 * resolution;
-		} else if (count == 2) {
-			this.radius = (max1 + max2) * resolution;
-		} else if (count == 3) {
-			var d = (2 / 3 * Math.sqrt(3) - 1) * max1;
-			this.radius = (d + max1 + max2) * resolution;
-		} else if (count == 4) {
-			var d = (Math.sqrt(2) - 1) * max2;
-			this.radius = (d + max1 + max2) * resolution;
-		}
-	}
-}
-
-Vertex.prototype.addElement = function(e, weight, index) {
-	this.elements[index].push(e);
-	this.size += weight;
-	this.weights[index] += weight;
-}
-function Edge(v0, v1) {
-	this.v0 = v0;
-	this.v1 = v1;
-	this.leftFace
-	this.rightFace
-	this.legal = true;
-	this.setLength();
-}
-
-Edge.prototype.setLength = function() {
-	var dx = this.v0.x - this.v1.x;
-	var dy = this.v0.y - this.v1.y;
-	this.length = Math.sqrt(dx * dx + dy * dy);
-}
-
-Edge.prototype.contains = function(v) {
-	if (this.v0 == v || this.v1 == v) {
-		return true;
-	}
-	return false;
-}
-
-Edge.prototype.replaceFace = function(f_old, f_new) {
-	if (this.leftFace == f_old) {
-		this.leftFace = f_new;
-	} else if (this.rightFace == f_old) {
-		this.rightFace = f_new;
-	}
-}
-
-Edge.prototype.setFace = function(f) {
-	if (f.leftOf(this)) {
-		this.leftFace = f;
-	} else {
-		this.rightFace = f;
-	}
-}
-
-Edge.prototype.setFaces = function(f1, f2) {
-	if (f1.leftOf(this)) {
-		this.leftFace = f1;
-		this.rightFace = f2;
-	} else {
-		this.leftFace = f2;
-		this.rightFace = f1;
-	}
-}
-
-Edge.prototype.removeFace = function(f) {
-	if (this.leftFace == f) {
-		this.leftFace = null;
-	} else {
-		this.rightFace = null;
-	}
-}
-
-Edge.prototype.equals = function(e) {
-	if (this.v0 == e.v0 && this.v1 == e.v1 || this.v0 == e.v1 && this.v1 == e.v0) {
-		return true;
-	}
-	return false;
-}
-function Triangle(edges) {
-	this.edges = edges;
-	this.setVertices();
-	this.descendants = [];
-}
-
-Triangle.prototype.getTriple = function(e) {
-	var i = arrayIndex(this.edges, e);
-	return {
-		e_s : this.edges[(i + 1) % 3],
-		e_p : this.edges[(i + 2) % 3],
-		u : this.vertices[(i + 2) % 3]
-	};
-}
-
-Triangle.prototype.leftOf = function(e) {
-	var i = arrayIndex(this.edges, e);
-	if (this.vertices[i].y != this.vertices[(i + 1) % 3].y) {
-		return this.vertices[i].y > this.vertices[(i + 1) % 3].y;
-	}
-	return this.vertices[i].y > this.vertices[(i + 2) % 3].y;
-}
-
-Triangle.prototype.getNext = function(v) {
-	var i = arrayIndex(this.vertices, v);
-	return this.vertices[(i + 1) % 3];
-}
-
-Triangle.prototype.oppositeEdge = function(v) {
-	var i = arrayIndex(this.vertices, v);
-	return this.edges[(i + 1) % 3];
-}
-
-Triangle.prototype.contains = function(v) {
-	return arrayIndex(this.vertices, v) != -1;
-}
-
-Triangle.prototype.replace = function(e_old, e_new) {
-	this.edges[arrayIndex(this.edges, e_old)] = e_new;
-}
-
-Triangle.prototype.setVertices = function() {
-	if (this.edges[1].v0 == this.edges[0].v0 || this.edges[1].v1 == this.edges[0].v0) {
-		this.vertices = [this.edges[0].v1, this.edges[0].v0];
-	} else {
-		this.vertices = [this.edges[0].v0, this.edges[0].v1];
-	}
-	if (this.edges[2].v0 == this.vertices[0]) {
-		this.vertices.push(this.edges[2].v1);
-	} else {
-		this.vertices.push(this.edges[2].v0);
-	}
-}
-
-Triangle.prototype.replaceBy = function(triangles) {
-	this.descendants = triangles;
-	this.edges[0].replaceFace(this, triangles[0]);
-	this.edges[1].replaceFace(this, triangles[1]);
-	this.edges[2].replaceFace(this, triangles[2]);
-}
-
-Triangle.prototype.CalcCircumcircle = function() {
-	var v0 = this.vertices[0];
-	var v1 = this.vertices[1];
-	var v2 = this.vertices[2];
-	var A = v1.x - v0.x;
-	var B = v1.y - v0.y;
-	var C = v2.x - v0.x;
-	var D = v2.y - v0.y;
-	var E = A * (v0.x + v1.x) + B * (v0.y + v1.y);
-	var F = C * (v0.x + v2.x) + D * (v0.y + v2.y);
-	var G = 2.0 * (A * (v2.y - v1.y) - B * (v2.x - v1.x));
-	var cx = (D * E - B * F) / G;
-	var cy = (A * F - C * E) / G;
-	this.center = new Vertex(cx, cy);
-	var dx = this.center.x - v0.x;
-	var dy = this.center.y - v0.y;
-	this.radius_squared = dx * dx + dy * dy;
-};
-
-Triangle.prototype.inCircumcircle = function(v) {
-	if (this.radius_squared == undefined) {
-		this.CalcCircumcircle();
-	}
-	var dx = this.center.x - v.x;
-	var dy = this.center.y - v.y;
-	var dist_squared = dx * dx + dy * dy;
-	return (dist_squared <= this.radius_squared );
-};
-
-Triangle.prototype.interior = function(v) {
-	var v0 = this.vertices[0];
-	var v1 = this.vertices[1];
-	var v2 = this.vertices[2];
-	var dotAB = (v.x - v0.x ) * (v0.y - v1.y ) + (v.y - v0.y ) * (v1.x - v0.x );
-	var dotBC = (v.x - v1.x ) * (v1.y - v2.y ) + (v.y - v1.y ) * (v2.x - v1.x );
-	var dotCA = (v.x - v2.x ) * (v2.y - v0.y ) + (v.y - v2.y ) * (v0.x - v2.x );
-	if (dotAB > 0 || dotBC > 0 || dotCA > 0) {
-		return null;
-	} else if (dotAB < 0 && dotBC < 0 && dotCA < 0) {
-		return this;
-	} else if (dotAB == 0) {
-		if (dotBC == 0) {
-			return this.vertices[1];
-		} else if (dotCA == 0) {
-			return this.vertices[0];
-		}
-		return this.edges[0];
-	} else if (dotBC == 0) {
-		if (dotCA == 0) {
-			return this.vertices[2];
-		}
-		return this.edges[1];
-	} else if (dotCA == 0) {
-		return this.edges[2];
-	}
-};
-
-function Clustering(xMin, yMin, xMax, yMax) {
-	this.triangles = [];
-	this.newTriangles = [];
-	this.bbox = {
-		x1 : xMin,
-		y1 : yMin,
-		x2 : xMax,
-		y2 : yMax
-	};
-	this.CreateBoundingTriangle();
-	this.edges = [];
-	this.vertices = [];
-	this.legalizes = 0;
-	this.collapses = 0;
-}
-
-Clustering.prototype.locate = function(v) {
-	if (this.boundingTriangle.descendants.length == 0) {
-		return this.boundingTriangle;
-	}
-	var triangles = this.boundingTriangle.descendants;
-	while (true) {
-		for (var i = 0; i < triangles.length; i++) {
-			var simplex = triangles[i].interior(v);
-			if (simplex == null) {
-				continue;
-			}
-			if ( simplex instanceof Vertex || this.isLeaf(triangles[i])) {
-				return simplex;
-			}
-			triangles = triangles[i].descendants;
-			break;
-		}
-	}
-}
-
-Clustering.prototype.legalize = function(v, e, t0_old) {
-	if (!e.v0.legal && !e.v1.legal) {
-		return;
-	}
-	this.legalizes++;
-	var flip = false;
-	var t1_old, tr1;
-	if (e.leftFace == t0_old && e.rightFace.inCircumcircle(v)) {
-		flip = true;
-		t1_old = e.rightFace;
-	} else if (e.rightFace == t0_old && e.leftFace.inCircumcircle(v)) {
-		flip = true;
-		t1_old = e.leftFace;
-	}
-	if (flip) {
-		var tr0 = t0_old.getTriple(e);
-		var tr1 = t1_old.getTriple(e);
-		var e_flip = new Edge(tr0.u, tr1.u);
-		var poly = [];
-		poly.push(e.v0);
-		poly.push(e_flip.v0);
-		poly.push(e.v1);
-		poly.push(e_flip.v1);
-		if (!this.JordanTest(poly, e_flip)) {
-			return;
-		}
-		e.legal = false;
-		this.edges.push(e_flip);
-		var t0_new = new Triangle([e_flip, tr0.e_p, tr1.e_s]);
-		var t1_new = new Triangle([e_flip, tr1.e_p, tr0.e_s]);
-		e_flip.setFaces(t0_new, t1_new);
-		tr0.e_p.replaceFace(t0_old, t0_new);
-		tr1.e_s.replaceFace(t1_old, t0_new);
-		tr1.e_p.replaceFace(t1_old, t1_new);
-		tr0.e_s.replaceFace(t0_old, t1_new);
-		t0_old.descendants = [t0_new, t1_new];
-		t1_old.descendants = [t0_new, t1_new];
-		this.legalize(v, t0_new.edges[2], t0_new);
-		this.legalize(v, t1_new.edges[1], t1_new);
-	}
-}
-
-Clustering.prototype.add = function(v) {
-	this.addVertex(v, this.locate(v));
-}
-
-Clustering.prototype.addVertex = function(v, simplex) {
-	if ( simplex instanceof Vertex) {
-		simplex.merge(simplex, v);
-	} else if ( simplex instanceof Edge) {
-		this.vertices.push(v);
-		simplex.legal = false;
-		var tr0 = simplex.leftFace.getTriple(simplex);
-		var tr1 = simplex.rightFace.getTriple(simplex);
-		var e0 = new Edge(v, tr0.u);
-		var e1 = new Edge(v, simplex.leftFace.getNext(tr0.u));
-		var e2 = new Edge(v, tr1.u);
-		var e3 = new Edge(v, simplex.rightFace.getNext(tr1.u));
-		var t0 = new Triangle([e0, tr0.e_p, e1]);
-		var t1 = new Triangle([e1, tr1.e_s, e2]);
-		var t2 = new Triangle([e2, tr1.e_p, e3]);
-		var t3 = new Triangle([e3, tr0.e_s, e0]);
-		simplex.leftFace.descendants = [t0, t3];
-		simplex.rightFace.descendants = [t1, t2];
-		this.edges.push(e0);
-		this.edges.push(e1);
-		this.edges.push(e2);
-		this.edges.push(e3);
-		e0.setFaces(t0, t3);
-		e1.setFaces(t0, t1);
-		e2.setFaces(t1, t2);
-		e3.setFaces(t2, t3);
-		tr0.e_p.replaceFace(simplex.leftFace, t0);
-		tr1.e_s.replaceFace(simplex.rightFace, t1);
-		tr1.e_p.replaceFace(simplex.rightFace, t2);
-		tr0.e_s.replaceFace(simplex.leftFace, t3);
-		this.legalize(v, tr0.e_p, t0);
-		this.legalize(v, tr1.e_s, t1);
-		this.legalize(v, tr1.e_p, t2);
-		this.legalize(v, tr0.e_s, t3);
-	} else {
-		this.vertices.push(v);
-		var e_i = new Edge(simplex.vertices[0], v);
-		var e_j = new Edge(simplex.vertices[1], v);
-		var e_k = new Edge(simplex.vertices[2], v);
-		this.edges.push(e_i);
-		this.edges.push(e_j);
-		this.edges.push(e_k);
-		var t0 = new Triangle([e_i, simplex.edges[0], e_j]);
-		var t1 = new Triangle([e_j, simplex.edges[1], e_k]);
-		var t2 = new Triangle([e_k, simplex.edges[2], e_i]);
-		e_i.setFaces(t0, t2);
-		e_j.setFaces(t0, t1);
-		e_k.setFaces(t1, t2);
-		simplex.replaceBy([t0, t1, t2]);
-		this.legalize(v, simplex.edges[0], t0);
-		this.legalize(v, simplex.edges[1], t1);
-		this.legalize(v, simplex.edges[2], t2);
-	}
-}
-
-Clustering.prototype.isLeaf = function(t) {
-	return t.descendants.length == 0;
-}
-
-Clustering.prototype.CreateBoundingTriangle = function() {
-	var dx = (this.bbox.x2 - this.bbox.x1 ) * 10;
-	var dy = (this.bbox.y2 - this.bbox.y1 ) * 10;
-	var v0 = new Vertex(this.bbox.x1 - dx, this.bbox.y1 - dy * 3);
-	var v1 = new Vertex(this.bbox.x2 + dx * 3, this.bbox.y2 + dy);
-	var v2 = new Vertex(this.bbox.x1 - dx, this.bbox.y2 + dy);
-	var e0 = new Edge(v1, v0);
-	var e1 = new Edge(v0, v2);
-	var e2 = new Edge(v2, v1);
-	v0.legal = false;
-	v1.legal = false;
-	v2.legal = false;
-	this.boundingTriangle = new Triangle([e0, e1, e2]);
-	var inf = new Triangle([e0, e1, e2]);
-	e0.setFaces(this.boundingTriangle, inf);
-	e1.setFaces(this.boundingTriangle, inf);
-	e2.setFaces(this.boundingTriangle, inf);
-}
-
-Clustering.prototype.mergeVertices = function(e) {
-	this.collapses++;
-	var s0 = e.v0.size;
-	var s1 = e.v1.size;
-	var x = (e.v0.x * s0 + e.v1.x * s1 ) / (s0 + s1 );
-	var y = (e.v0.y * s0 + e.v1.y * s1 ) / (s0 + s1 );
-	var v = new Vertex(x, y, e.v0.elements.length, e.v0.binning);
-	v.merge(e.v0, e.v1);
-
-	e.v0.legal = false;
-	e.v1.legal = false;
-
-	var hole = [];
-	var oldFacets = [];
-	e.legal = false;
-
-	var vertices = [];
-	var traverse = function(eLeft, eRight, triangle) {
-		eLeft.legal = false;
-		do {
-			var triple;
-			if (eLeft.leftFace == triangle) {
-				triple = eLeft.rightFace.getTriple(eLeft);
-				oldFacets.push(eLeft.rightFace);
-				triple.e_s.removeFace(eLeft.rightFace);
-				triangle = eLeft.rightFace;
-			} else {
-				triple = eLeft.leftFace.getTriple(eLeft);
-				oldFacets.push(eLeft.leftFace);
-				triple.e_s.removeFace(eLeft.leftFace);
-				triangle = eLeft.leftFace;
-			}
-			if (arrayIndex(hole, triple.e_s) == -1) {
-				hole.push(triple.e_s);
-			}
-			vertices.push(triple.u);
-			eLeft = triple.e_p;
-			eLeft.legal = false;
-		} while( eLeft != eRight );
-	}
-	var tr0 = e.leftFace.getTriple(e);
-	var tr1 = e.rightFace.getTriple(e);
-	oldFacets.push(e.leftFace);
-	oldFacets.push(e.rightFace);
-	traverse(tr0.e_p, tr1.e_s, e.leftFace);
-	traverse(tr1.e_p, tr0.e_s, e.rightFace);
-
-	var hd = new Clustering(this.bbox.x1 - 10, this.bbox.y1 - 10, this.bbox.x2 + 10, this.bbox.y2 + 10);
-	var hull = [];
-	for (var i in hole ) {
-		if (!(hole[i].leftFace == null && hole[i].rightFace == null)) {
-			hull.push(hole[i].v0);
-			hull.push(hole[i].v1);
-		}
-	}
-	var hullVertices = [];
-	var distinct = [];
-	for (var i in vertices ) {
-		if (arrayIndex(distinct, vertices[i]) == -1) {
-			hd.add(vertices[i]);
-			distinct.push(vertices[i]);
-		}
-		if (arrayIndex(hull, vertices[i]) != -1) {
-			hullVertices.push(vertices[i]);
-		}
-	}
-
-	var newFacets = [];
-	var isBoundary = function(e) {
-		for (var i = 0; i < hole.length; i++) {
-			if (hole[i].equals(e)) {
-				return i;
-			}
-		}
-		return -1;
-	}
-	var holeEdges = new Array(hole.length);
-	var nonHoleEdges = [];
-
-	for (var i = 0; i < hd.edges.length; i++) {
-		var e = hd.edges[i];
-		var b = isBoundary(e);
-		if (b != -1) {
-			if (!e.legal) {
-				var t1 = e.leftFace.getTriple(e);
-				var t2 = e.rightFace.getTriple(e);
-				var edge = new Edge(t1.u, t2.u);
-				for (var j = 0; j < hd.edges.length; j++) {
-					if (hd.edges[j].equals(edge) && hd.edges[j].legal) {
-						hd.edges[j].legal = false;
-						break;
-					}
-				}
-				t1.e_p.setFace(e.leftFace);
-				t1.e_s.setFace(e.leftFace);
-				t2.e_p.setFace(e.rightFace);
-				t2.e_s.setFace(e.rightFace);
-
-				e.legal = true;
-			}
-			holeEdges[b] = e;
-		} else {
-			nonHoleEdges.push(e);
-		}
-	}
-
-	for (var i = 0; i < holeEdges.length; i++) {
-		var e = holeEdges[i];
-		if (hole[i].leftFace == null) {
-			hole[i].leftFace = e.leftFace;
-			hole[i].leftFace.replace(e, hole[i]);
-			if (arrayIndex(newFacets, hole[i].leftFace) == -1) {
-				newFacets.push(hole[i].leftFace);
-			}
-		}
-		if (hole[i].rightFace == null) {
-			hole[i].rightFace = e.rightFace;
-			hole[i].rightFace.replace(e, hole[i]);
-			if (arrayIndex(newFacets, hole[i].rightFace) == -1) {
-				newFacets.push(hole[i].rightFace);
-			}
-		}
-	}
-
-	for (var i = 0; i < nonHoleEdges.length; i++) {
-		var e = nonHoleEdges[i];
-		if (!e.legal) {
-			continue;
-		}
-		if (this.JordanTest(hullVertices, e)) {
-			this.edges.push(e);
-			if (arrayIndex(newFacets, e.rightFace) == -1) {
-				newFacets.push(e.rightFace);
-			}
-			if (arrayIndex(newFacets, e.leftFace) == -1) {
-				newFacets.push(e.leftFace);
-			}
-		}
-	}
-
-	for (var i in oldFacets ) {
-		oldFacets[i].descendants = newFacets;
-	}
-
-	for (var i = 0; i < newFacets.length; i++) {
-		var simplex = newFacets[i].interior(v);
-		if (simplex == null) {
-			continue;
-		} else {
-			this.addVertex(v, simplex);
-			break;
-		}
-	}
-
-	return v;
-
-}
-
-Clustering.prototype.JordanTest = function(pol, e) {
-	var p = new Vertex((e.v0.x + e.v1.x) * 0.5, (e.v0.y + e.v1.y) * 0.5);
-	var inside = false;
-	var i, j = pol.length - 1;
-	for ( i = 0; i < pol.length; j = i++) {
-		var p1 = pol[i];
-		var p2 = pol[j];
-		if ((((p1.y <= p.y) && (p.y < p2.y)) || ((p2.y <= p.y) && (p.y < p1.y))) && (p.x < (p2.x - p1.x) * (p.y - p1.y) / (p2.y - p1.y) + p1.x))
-			inside = !inside;
-	}
-	return inside;
-}
-
-Clustering.prototype.mergeForResolution = function(resolution, circleGap, circleOverlap) {
-	this.deleteEdges = new BinaryHeap(function(e) {
-		return e.weight;
-	});
-	this.weightEdges(resolution, circleGap, circleOverlap);
-	var index = 0;
-	while (this.deleteEdges.size() > 0) {
-		var e = this.deleteEdges.pop();
-		if (e.legal) {
-			var l = this.edges.length;
-			var newVertex = this.mergeVertices(e);
-			newVertex.CalculateRadius(resolution);
-			for (var k = l; k < this.edges.length; k++) {
-				var eNew = this.edges[k];
-				if (eNew.legal) {
-					if( circleGap != 0 ){
-						eNew.weight = eNew.length / (eNew.v0.radius + eNew.v1.radius + circleGap * resolution );
-					}
-					else if( circleOverlap.overlap == 0 ){
-						eNew.weight = eNew.length / (eNew.v0.radius + eNew.v1.radius);
-					}
-					else {
-						var r1 = eNew.v0.radius;
-						var r2 = eNew.v1.radius;
-						var r = eNew.length;
-						if( r < r1 + r2 ){
-							if( circleOverlap.type == 'diameter' ){
-								var ol1 = (r2-(r-r1)) / r1 / 2;
-								var ol2 = (r1-(r-r2)) / r2 / 2;
-								var ol = Math.max(ol1,ol2);
-								eNew.weight = circleOverlap.overlap / ol;
-							}
-							if( circleOverlap.type == 'area' ){								
-								if( !(r+r1 < r2 || r+r2 < r1) ){
-									var A = r2*r2*Math.acos((r*r+r2*r2-r1*r1)/(2*r*r2))+r1*r1*Math.acos((r*r+r1*r1-r2*r2)/(2*r*r1))-1/2*Math.sqrt((-r+r1+r2)*(r-r1+r2)*(r+r1-r2)*(r+r1+r2));
-									var ol1 = A / (Math.PI*r1*r1);
-									var ol2 = A / (Math.PI*r2*r2);
-									var ol = Math.max(ol1,ol2);
-									eNew.weight = circleOverlap.overlap / ol;
-								}
-								else {
-									eNew.weight = 0;
-								}
-							}
-						}
-					}
-					if (eNew.weight < 1) {
-						this.deleteEdges.push(eNew);
-					}
-				}
-			}
-		}
-	}
-}
-
-Clustering.prototype.weightEdges = function(resolution, circleGap, circleOverlap) {
-	for (var i = 0; i < this.vertices.length; i++) {
-		if (this.vertices[i].legal) {
-			this.vertices[i].CalculateRadius(resolution);
-		}
-	}
-	var newEdges = [];
-	for (var i = 0; i < this.edges.length; i++) {
-		var e = this.edges[i];
-		if (e.legal) {
-			if (!e.v0.legal || !e.v1.legal) {
-				e.weight = 1;
-			} else {
-				if( circleGap != 0 ){
-					e.weight = e.length / (e.v0.radius + e.v1.radius + circleGap * resolution );
-				}
-				else if( circleOverlap.overlap == 0 ){
-					e.weight = e.length / (e.v0.radius + e.v1.radius);
-				}
-				else {
-					var r1 = e.v0.radius;
-					var r2 = e.v1.radius;
-					var r = e.length;
-					if( r < r1 + r2 ){
-						if( circleOverlap.type == 'diameter' ){
-							var ol1 = (r2-(r-r1)) / r1 / 2;
-							var ol2 = (r1-(r-r2)) / r2 / 2;
-							var ol = Math.max(ol1,ol2);
-							e.weight = circleOverlap.overlap / ol;
-						}
-						if( circleOverlap.type == 'area' ){
-							if( !(r+r1 < r2 || r+r2 < r1) ){
-								var A = r2*r2*Math.acos((r*r+r2*r2-r1*r1)/(2*r*r2))+r1*r1*Math.acos((r*r+r1*r1-r2*r2)/(2*r*r1))-1/2*Math.sqrt((-r+r1+r2)*(r-r1+r2)*(r+r1-r2)*(r+r1+r2));
-								var ol1 = A / (Math.PI*r1*r1);
-								var ol2 = A / (Math.PI*r2*r2);
-								var ol = Math.max(ol1,ol2);
-								e.weight = circleOverlap.overlap / ol;
-							}
-							else {
-								e.weight = 0;
-							}
-						}
-					}
-				}
-				if (e.weight < 1) {
-					this.deleteEdges.push(e);
-				}
-			}
-			newEdges.push(e);
-		}
-	}
-	this.edges = newEdges;
-}
-
-Clustering.prototype.ValidityTest = function() {
-	console.info("Test 1: Valid Delaunay ...");
-	/*
-	var leafs = [];
-	var triangles = this.boundingTriangle.descendants;
-	var j = 0;
-	while( triangles.length > j ){
-	var t = triangles[j];
-	if( t.taken == undefined ){
-	t.taken = true;
-	if( this.isLeaf(t) ){
-	leafs.push(t);
-	}
-	else {
-	triangles = triangles.concat(t.descendants);
-	}
-	}
-	j++;
-	}
-	console.info("  Number of Triangles: "+leafs.length);
-
-	var c = 0;
-	for( i in this.edges ){
-	if( this.edges[i].legal ){
-	c++;
-	}
-	}
-	console.info("  Number of Edges: "+c);*/
-	/*
-
-	for( var i=0; i<leafs.length; i++ ){
-	for( var j=0; j<vertices.length; j++ ){
-	if( !leafs[i].contains(vertices[j]) && leafs[i].inCircumcircle(vertices[j]) ){
-	console.info(leafs[i],vertices[j]);
-
-	}
-	}
-	}
-	*/
-
-	//console.info("Test 2: Edges Facets (null) ...");
-	for (i in this.edges ) {
-		var e = this.edges[i];
-		if (e.leftFace == null || e.rightFace == null) {
-			console.info(e);
-			alert();
-		}
-	}
-
-	//console.info("Test 3: Edges Facets ...");
-	var leftOf = function(v1, v2, v) {
-		var x2 = v1.x - v2.x;
-		var x3 = v1.x - v.x;
-		var y2 = v1.y - v2.y;
-		var y3 = v1.y - v.y;
-		if (x2 * y3 - y2 * x3 < 0) {
-			return true;
-		}
-		return false;
-	}
-	var c = 0;
-	for (i in this.edges ) {
-		var e = this.edges[i];
-		var t1 = e.leftFace.getTriple(e);
-		var t2 = e.rightFace.getTriple(e);
-		if (e.v0.y == e.v1.y) {
-			if (t1.u.y > t2.u.y) {
-				console.info("equal y conflict ...");
-				console.info(e);
-				alert();
-				c++;
-			}
-		} else {
-			var v1, v2;
-			if (e.v0.y > e.v1.y) {
-				v1 = e.v0;
-				v2 = e.v1;
-			} else {
-				v1 = e.v1;
-				v2 = e.v0;
-			}
-			if (!leftOf(v1, v2, t1.u)) {
-				console.info("left right conflict ... left is right");
-				console.info(e);
-				alert();
-				c++;
-			}
-			if (leftOf(v1, v2, t2.u)) {
-				console.info("left right conflict ... right is left");
-				console.info(e);
-				alert();
-				c++;
-			}
-		}
-	}
-	//console.info("Number of Edges: "+this.edges.length);
-	//console.info("Number of Conflicts: "+c);
-
-	for (i in this.edges ) {
-		if (this.edges[i].legal) {
-			var e = this.edges[i];
-			var tr0 = e.leftFace.getTriple(e);
-			var tr1 = e.rightFace.getTriple(e);
-			if (!tr0.e_p.legal || !tr0.e_s.legal || !tr1.e_p.legal || !tr1.e_s.legal) {
-				console.info(e);
-				console.info("conflict in edge continuity");
-				return;
-			}
-		}
-	}
-
-}
-function BinaryHeap(scoreFunction) {
-	this.content = [];
-	this.scoreFunction = scoreFunction;
-}
-
-BinaryHeap.prototype = {
-	push : function(element) {
-		// Add the new element to the end of the array.
-		this.content.push(element);
-		// Allow it to bubble up.
-		this.bubbleUp(this.content.length - 1);
-	},
-
-	pop : function() {
-		// Store the first element so we can return it later.
-		var result = this.content[0];
-		// Get the element at the end of the array.
-		var end = this.content.pop();
-		// If there are any elements left, put the end element at the
-		// start, and let it sink down.
-		if (this.content.length > 0) {
-			this.content[0] = end;
-			this.sinkDown(0);
-		}
-		return result;
-	},
-
-	remove : function(node) {
-		var len = this.content.length;
-		// To remove a value, we must search through the array to find
-		// it.
-		for (var i = 0; i < len; i++) {
-			if (this.content[i] == node) {
-				// When it is found, the process seen in 'pop' is repeated
-				// to fill up the hole.
-				var end = this.content.pop();
-				if (i != len - 1) {
-					this.content[i] = end;
-					if (this.scoreFunction(end) < this.scoreFunction(node))
-						this.bubbleUp(i);
-					else
-						this.sinkDown(i);
-				}
-				return;
-			}
-		}
-		throw new Error("Node not found.");
-	},
-
-	size : function() {
-		return this.content.length;
-	},
-
-	bubbleUp : function(n) {
-		// Fetch the element that has to be moved.
-		var element = this.content[n];
-		// When at 0, an element can not go up any further.
-		while (n > 0) {
-			// Compute the parent element's index, and fetch it.
-			var parentN = Math.floor((n + 1) / 2) - 1, parent = this.content[parentN];
-			// Swap the elements if the parent is greater.
-			if (this.scoreFunction(element) < this.scoreFunction(parent)) {
-				this.content[parentN] = element;
-				this.content[n] = parent;
-				// Update 'n' to continue at the new position.
-				n = parentN;
-
-			}
-			// Found a parent that is less, no need to move it further.
-			else {
-				break;
-			}
-		}
-	},
-
-	sinkDown : function(n) {
-		// Look up the target element and its score.
-		var length = this.content.length, element = this.content[n], elemScore = this.scoreFunction(element);
-
-		while (true) {
-			// Compute the indices of the child elements.
-			var child2N = (n + 1) * 2, child1N = child2N - 1;
-			// This is used to store the new position of the element,
-			// if any.
-			var swap = null;
-			// If the first child exists (is inside the array)...
-			if (child1N < length) {
-				// Look it up and compute its score.
-				var child1 = this.content[child1N], child1Score = this.scoreFunction(child1);
-				// If the score is less than our element's, we need to swap.
-				if (child1Score < elemScore)
-					swap = child1N;
-			}
-			// Do the same checks for the other child.
-			if (child2N < length) {
-				var child2 = this.content[child2N], child2Score = this.scoreFunction(child2);
-				if (child2Score < (swap == null ? elemScore : child1Score))
-					swap = child2N;
-			}
-
-			// If the element needs to be moved, swap it, and continue.
-			if (swap != null) {
-				this.content[n] = this.content[swap];
-				this.content[swap] = element;
-				n = swap;
-			}
-			// Otherwise, we are done.
-			else {
-				break;
-			}
-		}
-	}
-};
+/*
+* Clustering.js
+*
+* Copyright (c) 2012, Stefan Jänicke. All rights reserved.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 3 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+* MA 02110-1301  USA
+*/
+
+/**
+ * @class Vertex, Edge, Triangle, Clustering, BinaryHeap
+ * Dynamic Delaunay clustering algorithm (see GeoTemCo paper)
+ * @author Stefan Jänicke (stjaenicke@informatik.uni-leipzig.de)
+ * @release 1.0
+ * @release date: 2012-07-27
+ * @version date: 2012-07-27
+ */
+
+function Vertex(x, y, categories, binning) {
+	this.x = x;
+	this.y = y;
+	this.radius
+	this.size = 0;
+	this.elements = [];
+	this.radii = [];
+	this.weights = [];
+	this.legal = true;
+	this.binning = binning;
+	if (categories != undefined) {
+		for (var i = 0; i < categories; i++) {
+			this.elements.push([]);
+			this.weights.push(0);
+		}
+	}
+}
+
+Vertex.prototype.merge = function(v0, v1) {
+	for (var i = 0; i < v0.elements.length; i++) {
+		this.elements[i] = v0.elements[i].concat(v1.elements[i]);
+		this.weights[i] = v0.weights[i] + v1.weights[i];
+		this.size += this.weights[i];
+	}
+}
+
+Vertex.prototype.CalculateRadius = function(resolution) {
+	this.radii = [];
+	for (i in this.elements ) {
+		this.radii.push(this.binning.getRadius(this.weights[i]));
+	}
+	if (this.radii.length == 1) {
+		this.radius = this.radii[0] * resolution;
+	} else {
+		var count = 0;
+		var max1 = 0;
+		var max2 = 0;
+		for (i in this.radii ) {
+			if (this.radii[i] != 0) {
+				count++;
+			}
+			if (this.radii[i] > max1) {
+				if (max1 > max2) {
+					max2 = max1;
+				}
+				max1 = this.radii[i];
+			} else if (this.radii[i] > max2) {
+				max2 = this.radii[i];
+			}
+		}
+		if (count == 1) {
+			this.radius = max1 * resolution;
+		} else if (count == 2) {
+			this.radius = (max1 + max2) * resolution;
+		} else if (count == 3) {
+			var d = (2 / 3 * Math.sqrt(3) - 1) * max1;
+			this.radius = (d + max1 + max2) * resolution;
+		} else if (count == 4) {
+			var d = (Math.sqrt(2) - 1) * max2;
+			this.radius = (d + max1 + max2) * resolution;
+		}
+	}
+}
+
+Vertex.prototype.addElement = function(e, weight, index) {
+	this.elements[index].push(e);
+	this.size += weight;
+	this.weights[index] += weight;
+}
+function Edge(v0, v1) {
+	this.v0 = v0;
+	this.v1 = v1;
+	this.leftFace
+	this.rightFace
+	this.legal = true;
+	this.setLength();
+}
+
+Edge.prototype.setLength = function() {
+	var dx = this.v0.x - this.v1.x;
+	var dy = this.v0.y - this.v1.y;
+	this.length = Math.sqrt(dx * dx + dy * dy);
+}
+
+Edge.prototype.contains = function(v) {
+	if (this.v0 == v || this.v1 == v) {
+		return true;
+	}
+	return false;
+}
+
+Edge.prototype.replaceFace = function(f_old, f_new) {
+	if (this.leftFace == f_old) {
+		this.leftFace = f_new;
+	} else if (this.rightFace == f_old) {
+		this.rightFace = f_new;
+	}
+}
+
+Edge.prototype.setFace = function(f) {
+	if (f.leftOf(this)) {
+		this.leftFace = f;
+	} else {
+		this.rightFace = f;
+	}
+}
+
+Edge.prototype.setFaces = function(f1, f2) {
+	if (f1.leftOf(this)) {
+		this.leftFace = f1;
+		this.rightFace = f2;
+	} else {
+		this.leftFace = f2;
+		this.rightFace = f1;
+	}
+}
+
+Edge.prototype.removeFace = function(f) {
+	if (this.leftFace == f) {
+		this.leftFace = null;
+	} else {
+		this.rightFace = null;
+	}
+}
+
+Edge.prototype.equals = function(e) {
+	if (this.v0 == e.v0 && this.v1 == e.v1 || this.v0 == e.v1 && this.v1 == e.v0) {
+		return true;
+	}
+	return false;
+}
+function Triangle(edges) {
+	this.edges = edges;
+	this.setVertices();
+	this.descendants = [];
+}
+
+Triangle.prototype.getTriple = function(e) {
+	var i = arrayIndex(this.edges, e);
+	return {
+		e_s : this.edges[(i + 1) % 3],
+		e_p : this.edges[(i + 2) % 3],
+		u : this.vertices[(i + 2) % 3]
+	};
+}
+
+Triangle.prototype.leftOf = function(e) {
+	var i = arrayIndex(this.edges, e);
+	if (this.vertices[i].y != this.vertices[(i + 1) % 3].y) {
+		return this.vertices[i].y > this.vertices[(i + 1) % 3].y;
+	}
+	return this.vertices[i].y > this.vertices[(i + 2) % 3].y;
+}
+
+Triangle.prototype.getNext = function(v) {
+	var i = arrayIndex(this.vertices, v);
+	return this.vertices[(i + 1) % 3];
+}
+
+Triangle.prototype.oppositeEdge = function(v) {
+	var i = arrayIndex(this.vertices, v);
+	return this.edges[(i + 1) % 3];
+}
+
+Triangle.prototype.contains = function(v) {
+	return arrayIndex(this.vertices, v) != -1;
+}
+
+Triangle.prototype.replace = function(e_old, e_new) {
+	this.edges[arrayIndex(this.edges, e_old)] = e_new;
+}
+
+Triangle.prototype.setVertices = function() {
+	if (this.edges[1].v0 == this.edges[0].v0 || this.edges[1].v1 == this.edges[0].v0) {
+		this.vertices = [this.edges[0].v1, this.edges[0].v0];
+	} else {
+		this.vertices = [this.edges[0].v0, this.edges[0].v1];
+	}
+	if (this.edges[2].v0 == this.vertices[0]) {
+		this.vertices.push(this.edges[2].v1);
+	} else {
+		this.vertices.push(this.edges[2].v0);
+	}
+}
+
+Triangle.prototype.replaceBy = function(triangles) {
+	this.descendants = triangles;
+	this.edges[0].replaceFace(this, triangles[0]);
+	this.edges[1].replaceFace(this, triangles[1]);
+	this.edges[2].replaceFace(this, triangles[2]);
+}
+
+Triangle.prototype.CalcCircumcircle = function() {
+	var v0 = this.vertices[0];
+	var v1 = this.vertices[1];
+	var v2 = this.vertices[2];
+	var A = v1.x - v0.x;
+	var B = v1.y - v0.y;
+	var C = v2.x - v0.x;
+	var D = v2.y - v0.y;
+	var E = A * (v0.x + v1.x) + B * (v0.y + v1.y);
+	var F = C * (v0.x + v2.x) + D * (v0.y + v2.y);
+	var G = 2.0 * (A * (v2.y - v1.y) - B * (v2.x - v1.x));
+	var cx = (D * E - B * F) / G;
+	var cy = (A * F - C * E) / G;
+	this.center = new Vertex(cx, cy);
+	var dx = this.center.x - v0.x;
+	var dy = this.center.y - v0.y;
+	this.radius_squared = dx * dx + dy * dy;
+};
+
+Triangle.prototype.inCircumcircle = function(v) {
+	if (this.radius_squared == undefined) {
+		this.CalcCircumcircle();
+	}
+	var dx = this.center.x - v.x;
+	var dy = this.center.y - v.y;
+	var dist_squared = dx * dx + dy * dy;
+	return (dist_squared <= this.radius_squared );
+};
+
+Triangle.prototype.interior = function(v) {
+	var v0 = this.vertices[0];
+	var v1 = this.vertices[1];
+	var v2 = this.vertices[2];
+	var dotAB = (v.x - v0.x ) * (v0.y - v1.y ) + (v.y - v0.y ) * (v1.x - v0.x );
+	var dotBC = (v.x - v1.x ) * (v1.y - v2.y ) + (v.y - v1.y ) * (v2.x - v1.x );
+	var dotCA = (v.x - v2.x ) * (v2.y - v0.y ) + (v.y - v2.y ) * (v0.x - v2.x );
+	if (dotAB > 0 || dotBC > 0 || dotCA > 0) {
+		return null;
+	} else if (dotAB < 0 && dotBC < 0 && dotCA < 0) {
+		return this;
+	} else if (dotAB == 0) {
+		if (dotBC == 0) {
+			return this.vertices[1];
+		} else if (dotCA == 0) {
+			return this.vertices[0];
+		}
+		return this.edges[0];
+	} else if (dotBC == 0) {
+		if (dotCA == 0) {
+			return this.vertices[2];
+		}
+		return this.edges[1];
+	} else if (dotCA == 0) {
+		return this.edges[2];
+	}
+};
+
+function Clustering(xMin, yMin, xMax, yMax) {
+	this.triangles = [];
+	this.newTriangles = [];
+	this.bbox = {
+		x1 : xMin,
+		y1 : yMin,
+		x2 : xMax,
+		y2 : yMax
+	};
+	this.CreateBoundingTriangle();
+	this.edges = [];
+	this.vertices = [];
+	this.legalizes = 0;
+	this.collapses = 0;
+}
+
+Clustering.prototype.locate = function(v) {
+	if (this.boundingTriangle.descendants.length == 0) {
+		return this.boundingTriangle;
+	}
+	var triangles = this.boundingTriangle.descendants;
+	while (true) {
+		for (var i = 0; i < triangles.length; i++) {
+			var simplex = triangles[i].interior(v);
+			if (simplex == null) {
+				continue;
+			}
+			if ( simplex instanceof Vertex || this.isLeaf(triangles[i])) {
+				return simplex;
+			}
+			triangles = triangles[i].descendants;
+			break;
+		}
+	}
+}
+
+Clustering.prototype.legalize = function(v, e, t0_old) {
+	if (!e.v0.legal && !e.v1.legal) {
+		return;
+	}
+	this.legalizes++;
+	var flip = false;
+	var t1_old, tr1;
+	if (e.leftFace == t0_old && e.rightFace.inCircumcircle(v)) {
+		flip = true;
+		t1_old = e.rightFace;
+	} else if (e.rightFace == t0_old && e.leftFace.inCircumcircle(v)) {
+		flip = true;
+		t1_old = e.leftFace;
+	}
+	if (flip) {
+		var tr0 = t0_old.getTriple(e);
+		var tr1 = t1_old.getTriple(e);
+		var e_flip = new Edge(tr0.u, tr1.u);
+		var poly = [];
+		poly.push(e.v0);
+		poly.push(e_flip.v0);
+		poly.push(e.v1);
+		poly.push(e_flip.v1);
+		if (!this.JordanTest(poly, e_flip)) {
+			return;
+		}
+		e.legal = false;
+		this.edges.push(e_flip);
+		var t0_new = new Triangle([e_flip, tr0.e_p, tr1.e_s]);
+		var t1_new = new Triangle([e_flip, tr1.e_p, tr0.e_s]);
+		e_flip.setFaces(t0_new, t1_new);
+		tr0.e_p.replaceFace(t0_old, t0_new);
+		tr1.e_s.replaceFace(t1_old, t0_new);
+		tr1.e_p.replaceFace(t1_old, t1_new);
+		tr0.e_s.replaceFace(t0_old, t1_new);
+		t0_old.descendants = [t0_new, t1_new];
+		t1_old.descendants = [t0_new, t1_new];
+		this.legalize(v, t0_new.edges[2], t0_new);
+		this.legalize(v, t1_new.edges[1], t1_new);
+	}
+}
+
+Clustering.prototype.add = function(v) {
+	this.addVertex(v, this.locate(v));
+}
+
+Clustering.prototype.addVertex = function(v, simplex) {
+	if ( simplex instanceof Vertex) {
+		simplex.merge(simplex, v);
+	} else if ( simplex instanceof Edge) {
+		this.vertices.push(v);
+		simplex.legal = false;
+		var tr0 = simplex.leftFace.getTriple(simplex);
+		var tr1 = simplex.rightFace.getTriple(simplex);
+		var e0 = new Edge(v, tr0.u);
+		var e1 = new Edge(v, simplex.leftFace.getNext(tr0.u));
+		var e2 = new Edge(v, tr1.u);
+		var e3 = new Edge(v, simplex.rightFace.getNext(tr1.u));
+		var t0 = new Triangle([e0, tr0.e_p, e1]);
+		var t1 = new Triangle([e1, tr1.e_s, e2]);
+		var t2 = new Triangle([e2, tr1.e_p, e3]);
+		var t3 = new Triangle([e3, tr0.e_s, e0]);
+		simplex.leftFace.descendants = [t0, t3];
+		simplex.rightFace.descendants = [t1, t2];
+		this.edges.push(e0);
+		this.edges.push(e1);
+		this.edges.push(e2);
+		this.edges.push(e3);
+		e0.setFaces(t0, t3);
+		e1.setFaces(t0, t1);
+		e2.setFaces(t1, t2);
+		e3.setFaces(t2, t3);
+		tr0.e_p.replaceFace(simplex.leftFace, t0);
+		tr1.e_s.replaceFace(simplex.rightFace, t1);
+		tr1.e_p.replaceFace(simplex.rightFace, t2);
+		tr0.e_s.replaceFace(simplex.leftFace, t3);
+		this.legalize(v, tr0.e_p, t0);
+		this.legalize(v, tr1.e_s, t1);
+		this.legalize(v, tr1.e_p, t2);
+		this.legalize(v, tr0.e_s, t3);
+	} else {
+		this.vertices.push(v);
+		var e_i = new Edge(simplex.vertices[0], v);
+		var e_j = new Edge(simplex.vertices[1], v);
+		var e_k = new Edge(simplex.vertices[2], v);
+		this.edges.push(e_i);
+		this.edges.push(e_j);
+		this.edges.push(e_k);
+		var t0 = new Triangle([e_i, simplex.edges[0], e_j]);
+		var t1 = new Triangle([e_j, simplex.edges[1], e_k]);
+		var t2 = new Triangle([e_k, simplex.edges[2], e_i]);
+		e_i.setFaces(t0, t2);
+		e_j.setFaces(t0, t1);
+		e_k.setFaces(t1, t2);
+		simplex.replaceBy([t0, t1, t2]);
+		this.legalize(v, simplex.edges[0], t0);
+		this.legalize(v, simplex.edges[1], t1);
+		this.legalize(v, simplex.edges[2], t2);
+	}
+}
+
+Clustering.prototype.isLeaf = function(t) {
+	return t.descendants.length == 0;
+}
+
+Clustering.prototype.CreateBoundingTriangle = function() {
+	var dx = (this.bbox.x2 - this.bbox.x1 ) * 10;
+	var dy = (this.bbox.y2 - this.bbox.y1 ) * 10;
+	var v0 = new Vertex(this.bbox.x1 - dx, this.bbox.y1 - dy * 3);
+	var v1 = new Vertex(this.bbox.x2 + dx * 3, this.bbox.y2 + dy);
+	var v2 = new Vertex(this.bbox.x1 - dx, this.bbox.y2 + dy);
+	var e0 = new Edge(v1, v0);
+	var e1 = new Edge(v0, v2);
+	var e2 = new Edge(v2, v1);
+	v0.legal = false;
+	v1.legal = false;
+	v2.legal = false;
+	this.boundingTriangle = new Triangle([e0, e1, e2]);
+	var inf = new Triangle([e0, e1, e2]);
+	e0.setFaces(this.boundingTriangle, inf);
+	e1.setFaces(this.boundingTriangle, inf);
+	e2.setFaces(this.boundingTriangle, inf);
+}
+
+Clustering.prototype.mergeVertices = function(e) {
+	this.collapses++;
+	var s0 = e.v0.size;
+	var s1 = e.v1.size;
+	var x = (e.v0.x * s0 + e.v1.x * s1 ) / (s0 + s1 );
+	var y = (e.v0.y * s0 + e.v1.y * s1 ) / (s0 + s1 );
+	var v = new Vertex(x, y, e.v0.elements.length, e.v0.binning);
+	v.merge(e.v0, e.v1);
+
+	e.v0.legal = false;
+	e.v1.legal = false;
+
+	var hole = [];
+	var oldFacets = [];
+	e.legal = false;
+
+	var vertices = [];
+	var traverse = function(eLeft, eRight, triangle) {
+		eLeft.legal = false;
+		do {
+			var triple;
+			if (eLeft.leftFace == triangle) {
+				triple = eLeft.rightFace.getTriple(eLeft);
+				oldFacets.push(eLeft.rightFace);
+				triple.e_s.removeFace(eLeft.rightFace);
+				triangle = eLeft.rightFace;
+			} else {
+				triple = eLeft.leftFace.getTriple(eLeft);
+				oldFacets.push(eLeft.leftFace);
+				triple.e_s.removeFace(eLeft.leftFace);
+				triangle = eLeft.leftFace;
+			}
+			if (arrayIndex(hole, triple.e_s) == -1) {
+				hole.push(triple.e_s);
+			}
+			vertices.push(triple.u);
+			eLeft = triple.e_p;
+			eLeft.legal = false;
+		} while( eLeft != eRight );
+	}
+	var tr0 = e.leftFace.getTriple(e);
+	var tr1 = e.rightFace.getTriple(e);
+	oldFacets.push(e.leftFace);
+	oldFacets.push(e.rightFace);
+	traverse(tr0.e_p, tr1.e_s, e.leftFace);
+	traverse(tr1.e_p, tr0.e_s, e.rightFace);
+
+	var hd = new Clustering(this.bbox.x1 - 10, this.bbox.y1 - 10, this.bbox.x2 + 10, this.bbox.y2 + 10);
+	var hull = [];
+	for (var i in hole ) {
+		if (!(hole[i].leftFace == null && hole[i].rightFace == null)) {
+			hull.push(hole[i].v0);
+			hull.push(hole[i].v1);
+		}
+	}
+	var hullVertices = [];
+	var distinct = [];
+	for (var i in vertices ) {
+		if (arrayIndex(distinct, vertices[i]) == -1) {
+			hd.add(vertices[i]);
+			distinct.push(vertices[i]);
+		}
+		if (arrayIndex(hull, vertices[i]) != -1) {
+			hullVertices.push(vertices[i]);
+		}
+	}
+
+	var newFacets = [];
+	var isBoundary = function(e) {
+		for (var i = 0; i < hole.length; i++) {
+			if (hole[i].equals(e)) {
+				return i;
+			}
+		}
+		return -1;
+	}
+	var holeEdges = new Array(hole.length);
+	var nonHoleEdges = [];
+
+	for (var i = 0; i < hd.edges.length; i++) {
+		var e = hd.edges[i];
+		var b = isBoundary(e);
+		if (b != -1) {
+			if (!e.legal) {
+				var t1 = e.leftFace.getTriple(e);
+				var t2 = e.rightFace.getTriple(e);
+				var edge = new Edge(t1.u, t2.u);
+				for (var j = 0; j < hd.edges.length; j++) {
+					if (hd.edges[j].equals(edge) && hd.edges[j].legal) {
+						hd.edges[j].legal = false;
+						break;
+					}
+				}
+				t1.e_p.setFace(e.leftFace);
+				t1.e_s.setFace(e.leftFace);
+				t2.e_p.setFace(e.rightFace);
+				t2.e_s.setFace(e.rightFace);
+
+				e.legal = true;
+			}
+			holeEdges[b] = e;
+		} else {
+			nonHoleEdges.push(e);
+		}
+	}
+
+	for (var i = 0; i < holeEdges.length; i++) {
+		var e = holeEdges[i];
+		if (hole[i].leftFace == null) {
+			hole[i].leftFace = e.leftFace;
+			hole[i].leftFace.replace(e, hole[i]);
+			if (arrayIndex(newFacets, hole[i].leftFace) == -1) {
+				newFacets.push(hole[i].leftFace);
+			}
+		}
+		if (hole[i].rightFace == null) {
+			hole[i].rightFace = e.rightFace;
+			hole[i].rightFace.replace(e, hole[i]);
+			if (arrayIndex(newFacets, hole[i].rightFace) == -1) {
+				newFacets.push(hole[i].rightFace);
+			}
+		}
+	}
+
+	for (var i = 0; i < nonHoleEdges.length; i++) {
+		var e = nonHoleEdges[i];
+		if (!e.legal) {
+			continue;
+		}
+		if (this.JordanTest(hullVertices, e)) {
+			this.edges.push(e);
+			if (arrayIndex(newFacets, e.rightFace) == -1) {
+				newFacets.push(e.rightFace);
+			}
+			if (arrayIndex(newFacets, e.leftFace) == -1) {
+				newFacets.push(e.leftFace);
+			}
+		}
+	}
+
+	for (var i in oldFacets ) {
+		oldFacets[i].descendants = newFacets;
+	}
+
+	for (var i = 0; i < newFacets.length; i++) {
+		var simplex = newFacets[i].interior(v);
+		if (simplex == null) {
+			continue;
+		} else {
+			this.addVertex(v, simplex);
+			break;
+		}
+	}
+
+	return v;
+
+}
+
+Clustering.prototype.JordanTest = function(pol, e) {
+	var p = new Vertex((e.v0.x + e.v1.x) * 0.5, (e.v0.y + e.v1.y) * 0.5);
+	var inside = false;
+	var i, j = pol.length - 1;
+	for ( i = 0; i < pol.length; j = i++) {
+		var p1 = pol[i];
+		var p2 = pol[j];
+		if ((((p1.y <= p.y) && (p.y < p2.y)) || ((p2.y <= p.y) && (p.y < p1.y))) && (p.x < (p2.x - p1.x) * (p.y - p1.y) / (p2.y - p1.y) + p1.x))
+			inside = !inside;
+	}
+	return inside;
+}
+
+Clustering.prototype.mergeForResolution = function(resolution, circleGap, circleOverlap) {
+	this.deleteEdges = new BinaryHeap(function(e) {
+		return e.weight;
+	});
+	this.weightEdges(resolution, circleGap, circleOverlap);
+	var index = 0;
+	while (this.deleteEdges.size() > 0) {
+		var e = this.deleteEdges.pop();
+		if (e.legal) {
+			var l = this.edges.length;
+			var newVertex = this.mergeVertices(e);
+			newVertex.CalculateRadius(resolution);
+			for (var k = l; k < this.edges.length; k++) {
+				var eNew = this.edges[k];
+				if (eNew.legal) {
+					if( circleGap != 0 ){
+						eNew.weight = eNew.length / (eNew.v0.radius + eNew.v1.radius + circleGap * resolution );
+					}
+					else if( circleOverlap.overlap == 0 ){
+						eNew.weight = eNew.length / (eNew.v0.radius + eNew.v1.radius);
+					}
+					else {
+						var r1 = eNew.v0.radius;
+						var r2 = eNew.v1.radius;
+						var r = eNew.length;
+						if( r < r1 + r2 ){
+							if( circleOverlap.type == 'diameter' ){
+								var ol1 = (r2-(r-r1)) / r1 / 2;
+								var ol2 = (r1-(r-r2)) / r2 / 2;
+								var ol = Math.max(ol1,ol2);
+								eNew.weight = circleOverlap.overlap / ol;
+							}
+							if( circleOverlap.type == 'area' ){								
+								if( !(r+r1 < r2 || r+r2 < r1) ){
+									var A = r2*r2*Math.acos((r*r+r2*r2-r1*r1)/(2*r*r2))+r1*r1*Math.acos((r*r+r1*r1-r2*r2)/(2*r*r1))-1/2*Math.sqrt((-r+r1+r2)*(r-r1+r2)*(r+r1-r2)*(r+r1+r2));
+									var ol1 = A / (Math.PI*r1*r1);
+									var ol2 = A / (Math.PI*r2*r2);
+									var ol = Math.max(ol1,ol2);
+									eNew.weight = circleOverlap.overlap / ol;
+								}
+								else {
+									eNew.weight = 0;
+								}
+							}
+						}
+					}
+					if (eNew.weight < 1) {
+						this.deleteEdges.push(eNew);
+					}
+				}
+			}
+		}
+	}
+}
+
+Clustering.prototype.weightEdges = function(resolution, circleGap, circleOverlap) {
+	for (var i = 0; i < this.vertices.length; i++) {
+		if (this.vertices[i].legal) {
+			this.vertices[i].CalculateRadius(resolution);
+		}
+	}
+	var newEdges = [];
+	for (var i = 0; i < this.edges.length; i++) {
+		var e = this.edges[i];
+		if (e.legal) {
+			if (!e.v0.legal || !e.v1.legal) {
+				e.weight = 1;
+			} else {
+				if( circleGap != 0 ){
+					e.weight = e.length / (e.v0.radius + e.v1.radius + circleGap * resolution );
+				}
+				else if( circleOverlap.overlap == 0 ){
+					e.weight = e.length / (e.v0.radius + e.v1.radius);
+				}
+				else {
+					var r1 = e.v0.radius;
+					var r2 = e.v1.radius;
+					var r = e.length;
+					if( r < r1 + r2 ){
+						if( circleOverlap.type == 'diameter' ){
+							var ol1 = (r2-(r-r1)) / r1 / 2;
+							var ol2 = (r1-(r-r2)) / r2 / 2;
+							var ol = Math.max(ol1,ol2);
+							e.weight = circleOverlap.overlap / ol;
+						}
+						if( circleOverlap.type == 'area' ){
+							if( !(r+r1 < r2 || r+r2 < r1) ){
+								var A = r2*r2*Math.acos((r*r+r2*r2-r1*r1)/(2*r*r2))+r1*r1*Math.acos((r*r+r1*r1-r2*r2)/(2*r*r1))-1/2*Math.sqrt((-r+r1+r2)*(r-r1+r2)*(r+r1-r2)*(r+r1+r2));
+								var ol1 = A / (Math.PI*r1*r1);
+								var ol2 = A / (Math.PI*r2*r2);
+								var ol = Math.max(ol1,ol2);
+								e.weight = circleOverlap.overlap / ol;
+							}
+							else {
+								e.weight = 0;
+							}
+						}
+					}
+				}
+				if (e.weight < 1) {
+					this.deleteEdges.push(e);
+				}
+			}
+			newEdges.push(e);
+		}
+	}
+	this.edges = newEdges;
+}
+
+Clustering.prototype.ValidityTest = function() {
+	console.info("Test 1: Valid Delaunay ...");
+	/*
+	var leafs = [];
+	var triangles = this.boundingTriangle.descendants;
+	var j = 0;
+	while( triangles.length > j ){
+	var t = triangles[j];
+	if( t.taken == undefined ){
+	t.taken = true;
+	if( this.isLeaf(t) ){
+	leafs.push(t);
+	}
+	else {
+	triangles = triangles.concat(t.descendants);
+	}
+	}
+	j++;
+	}
+	console.info("  Number of Triangles: "+leafs.length);
+
+	var c = 0;
+	for( i in this.edges ){
+	if( this.edges[i].legal ){
+	c++;
+	}
+	}
+	console.info("  Number of Edges: "+c);*/
+	/*
+
+	for( var i=0; i<leafs.length; i++ ){
+	for( var j=0; j<vertices.length; j++ ){
+	if( !leafs[i].contains(vertices[j]) && leafs[i].inCircumcircle(vertices[j]) ){
+	console.info(leafs[i],vertices[j]);
+
+	}
+	}
+	}
+	*/
+
+	//console.info("Test 2: Edges Facets (null) ...");
+	for (i in this.edges ) {
+		var e = this.edges[i];
+		if (e.leftFace == null || e.rightFace == null) {
+			console.info(e);
+			alert();
+		}
+	}
+
+	//console.info("Test 3: Edges Facets ...");
+	var leftOf = function(v1, v2, v) {
+		var x2 = v1.x - v2.x;
+		var x3 = v1.x - v.x;
+		var y2 = v1.y - v2.y;
+		var y3 = v1.y - v.y;
+		if (x2 * y3 - y2 * x3 < 0) {
+			return true;
+		}
+		return false;
+	}
+	var c = 0;
+	for (i in this.edges ) {
+		var e = this.edges[i];
+		var t1 = e.leftFace.getTriple(e);
+		var t2 = e.rightFace.getTriple(e);
+		if (e.v0.y == e.v1.y) {
+			if (t1.u.y > t2.u.y) {
+				console.info("equal y conflict ...");
+				console.info(e);
+				alert();
+				c++;
+			}
+		} else {
+			var v1, v2;
+			if (e.v0.y > e.v1.y) {
+				v1 = e.v0;
+				v2 = e.v1;
+			} else {
+				v1 = e.v1;
+				v2 = e.v0;
+			}
+			if (!leftOf(v1, v2, t1.u)) {
+				console.info("left right conflict ... left is right");
+				console.info(e);
+				alert();
+				c++;
+			}
+			if (leftOf(v1, v2, t2.u)) {
+				console.info("left right conflict ... right is left");
+				console.info(e);
+				alert();
+				c++;
+			}
+		}
+	}
+	//console.info("Number of Edges: "+this.edges.length);
+	//console.info("Number of Conflicts: "+c);
+
+	for (i in this.edges ) {
+		if (this.edges[i].legal) {
+			var e = this.edges[i];
+			var tr0 = e.leftFace.getTriple(e);
+			var tr1 = e.rightFace.getTriple(e);
+			if (!tr0.e_p.legal || !tr0.e_s.legal || !tr1.e_p.legal || !tr1.e_s.legal) {
+				console.info(e);
+				console.info("conflict in edge continuity");
+				return;
+			}
+		}
+	}
+
+}
+function BinaryHeap(scoreFunction) {
+	this.content = [];
+	this.scoreFunction = scoreFunction;
+}
+
+BinaryHeap.prototype = {
+	push : function(element) {
+		// Add the new element to the end of the array.
+		this.content.push(element);
+		// Allow it to bubble up.
+		this.bubbleUp(this.content.length - 1);
+	},
+
+	pop : function() {
+		// Store the first element so we can return it later.
+		var result = this.content[0];
+		// Get the element at the end of the array.
+		var end = this.content.pop();
+		// If there are any elements left, put the end element at the
+		// start, and let it sink down.
+		if (this.content.length > 0) {
+			this.content[0] = end;
+			this.sinkDown(0);
+		}
+		return result;
+	},
+
+	remove : function(node) {
+		var len = this.content.length;
+		// To remove a value, we must search through the array to find
+		// it.
+		for (var i = 0; i < len; i++) {
+			if (this.content[i] == node) {
+				// When it is found, the process seen in 'pop' is repeated
+				// to fill up the hole.
+				var end = this.content.pop();
+				if (i != len - 1) {
+					this.content[i] = end;
+					if (this.scoreFunction(end) < this.scoreFunction(node))
+						this.bubbleUp(i);
+					else
+						this.sinkDown(i);
+				}
+				return;
+			}
+		}
+		throw new Error("Node not found.");
+	},
+
+	size : function() {
+		return this.content.length;
+	},
+
+	bubbleUp : function(n) {
+		// Fetch the element that has to be moved.
+		var element = this.content[n];
+		// When at 0, an element can not go up any further.
+		while (n > 0) {
+			// Compute the parent element's index, and fetch it.
+			var parentN = Math.floor((n + 1) / 2) - 1, parent = this.content[parentN];
+			// Swap the elements if the parent is greater.
+			if (this.scoreFunction(element) < this.scoreFunction(parent)) {
+				this.content[parentN] = element;
+				this.content[n] = parent;
+				// Update 'n' to continue at the new position.
+				n = parentN;
+
+			}
+			// Found a parent that is less, no need to move it further.
+			else {
+				break;
+			}
+		}
+	},
+
+	sinkDown : function(n) {
+		// Look up the target element and its score.
+		var length = this.content.length, element = this.content[n], elemScore = this.scoreFunction(element);
+
+		while (true) {
+			// Compute the indices of the child elements.
+			var child2N = (n + 1) * 2, child1N = child2N - 1;
+			// This is used to store the new position of the element,
+			// if any.
+			var swap = null;
+			// If the first child exists (is inside the array)...
+			if (child1N < length) {
+				// Look it up and compute its score.
+				var child1 = this.content[child1N], child1Score = this.scoreFunction(child1);
+				// If the score is less than our element's, we need to swap.
+				if (child1Score < elemScore)
+					swap = child1N;
+			}
+			// Do the same checks for the other child.
+			if (child2N < length) {
+				var child2 = this.content[child2N], child2Score = this.scoreFunction(child2);
+				if (child2Score < (swap == null ? elemScore : child1Score))
+					swap = child2N;
+			}
+
+			// If the element needs to be moved, swap it, and continue.
+			if (swap != null) {
+				this.content[n] = this.content[swap];
+				this.content[swap] = element;
+				n = swap;
+			}
+			// Otherwise, we are done.
+			else {
+				break;
+			}
+		}
+	}
+};
 /*
 * Dropdown.js
 *
@@ -46447,81 +48676,81 @@
 		}
 	}
 };
-/*
-* Dropdown.js
-*
-* Copyright (c) 2012, Stefan Jänicke. All rights reserved.
-*
-* This library is free software; you can redistribute it and/or
-* modify it under the terms of the GNU Lesser General Public
-* License as published by the Free Software Foundation; either
-* version 3 of the License, or (at your option) any later version.
-*
-* This library is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-* Lesser General Public License for more details.
-*
-* You should have received a copy of the GNU Lesser General Public
-* License along with this library; if not, write to the Free Software
-* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
-* MA 02110-1301  USA
-*/
-
-/**
- * @class Publisher
- * Publish/Subscribe mechanism
- * @author Stefan Jänicke (stjaenicke@informatik.uni-leipzig.de)
- * @release 1.0
- * @release date: 2012-07-27
- * @version date: 2012-07-27
- */
-if ( typeof Publisher == 'undefined') {
-
-	Publisher = function() {
-
-		var topics = [];
-
-		this.Get = function(topic) {
-			var value = topics[topic];
-			if (!value || !(value instanceof Array)) {
-				value = topics[topic] = [];
-			}
-			return value;
-		};
-
-		this.Publish = function(topic, data, publisher) {
-			var subscribers = this.Get(topic);
-			for (var i = 0; i < subscribers.length; i++) {
-				if (publisher == null || subscribers[i].client != publisher) {
-					subscribers[i].callback(data);
-				}
-			}
-		};
-
-		this.Subscribe = function(topic, subscriber, callback) {
-			var subscribers = this.Get(topic);
-			subscribers.push({
-				client : subscriber,
-				callback : callback
-			});
-		};
-
-		this.Unsubscribe = function(topic, unsubscriber) {
-			var subscribers = this.Get(topic);
-			for (var i = 0; i < subscribers.length; i++) {
-				if (subscribers[i].client == unsubscriber) {
-					subscribers.splice(i, 1);
-					return;
-				}
-			}
-		};
-
-		return this;
-
-	}();
-
-}
+/*
+* Dropdown.js
+*
+* Copyright (c) 2012, Stefan Jänicke. All rights reserved.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 3 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+* MA 02110-1301  USA
+*/
+
+/**
+ * @class Publisher
+ * Publish/Subscribe mechanism
+ * @author Stefan Jänicke (stjaenicke@informatik.uni-leipzig.de)
+ * @release 1.0
+ * @release date: 2012-07-27
+ * @version date: 2012-07-27
+ */
+if ( typeof Publisher == 'undefined') {
+
+	Publisher = function() {
+
+		var topics = [];
+
+		this.Get = function(topic) {
+			var value = topics[topic];
+			if (!value || !(value instanceof Array)) {
+				value = topics[topic] = [];
+			}
+			return value;
+		};
+
+		this.Publish = function(topic, data, publisher) {
+			var subscribers = this.Get(topic);
+			for (var i = 0; i < subscribers.length; i++) {
+				if (publisher == null || subscribers[i].client != publisher) {
+					subscribers[i].callback(data);
+				}
+			}
+		};
+
+		this.Subscribe = function(topic, subscriber, callback) {
+			var subscribers = this.Get(topic);
+			subscribers.push({
+				client : subscriber,
+				callback : callback
+			});
+		};
+
+		this.Unsubscribe = function(topic, unsubscriber) {
+			var subscribers = this.Get(topic);
+			for (var i = 0; i < subscribers.length; i++) {
+				if (subscribers[i].client == unsubscriber) {
+					subscribers.splice(i, 1);
+					return;
+				}
+			}
+		};
+
+		return this;
+
+	}();
+
+}
 /*
 * WidgetWrapper.js
 *
@@ -46618,42 +48847,42 @@
 	};
 
 };
-/*
-* final.js
-*
-* Copyright (c) 2012, Stefan Jänicke. All rights reserved.
-*
-* This library is free software; you can redistribute it and/or
-* modify it under the terms of the GNU Lesser General Public
-* License as published by the Free Software Foundation; either
-* version 3 of the License, or (at your option) any later version.
-*
-* This library is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-* Lesser General Public License for more details.
-*
-* You should have received a copy of the GNU Lesser General Public
-* License along with this library; if not, write to the Free Software
-* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
-* MA 02110-1301  USA
-*/
-
-/**
- * code which is included after all other sources have been included for the minified version
- * @author Stefan Jänicke (stjaenicke@informatik.uni-leipzig.de)
- * @release 1.0
- * @release date: 2012-07-27
- * @version date: 2012-07-27
- */
-
-OpenLayers.Util.getImagesLocation = function() {
-	return GeoTemCoMinifier_urlPrefix + "lib/openlayers/img/";
-};
-
-OpenLayers._getScriptLocation = function() {
-	return GeoTemCoMinifier_urlPrefix + "lib/openlayers/";
-};
-
-GeoTemConfig.configure(GeoTemCoMinifier_urlPrefix);
+/*
+* final.js
+*
+* Copyright (c) 2012, Stefan Jänicke. All rights reserved.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 3 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+* MA 02110-1301  USA
+*/
+
+/**
+ * code which is included after all other sources have been included for the minified version
+ * @author Stefan Jänicke (stjaenicke@informatik.uni-leipzig.de)
+ * @release 1.0
+ * @release date: 2012-07-27
+ * @version date: 2012-07-27
+ */
+
+OpenLayers.Util.getImagesLocation = function() {
+	return GeoTemCoMinifier_urlPrefix + "lib/openlayers/img/";
+};
+
+OpenLayers._getScriptLocation = function() {
+	return GeoTemCoMinifier_urlPrefix + "lib/openlayers/";
+};
+
+GeoTemConfig.configure(GeoTemCoMinifier_urlPrefix);
 })(jQuery);